// Copyright 2018-2019 Hans Dembinski // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_HISTOGRAM_STORAGE_ADAPTOR_HPP #define BOOST_HISTOGRAM_STORAGE_ADAPTOR_HPP #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace histogram { namespace detail { template struct vector_impl : T { using allocator_type = typename T::allocator_type; static constexpr bool has_threading_support = accumulators::is_thread_safe::value; vector_impl(const allocator_type& a = {}) : T(a) {} vector_impl(const vector_impl&) = default; vector_impl& operator=(const vector_impl&) = default; vector_impl(vector_impl&&) = default; vector_impl& operator=(vector_impl&&) = default; explicit vector_impl(T&& t) : T(std::move(t)) {} explicit vector_impl(const T& t) : T(t) {} template > explicit vector_impl(const U& u, const allocator_type& a = {}) : T(std::begin(u), std::end(u), a) {} template > vector_impl& operator=(const U& u) { T::resize(u.size()); auto it = T::begin(); for (auto&& x : u) *it++ = x; return *this; } void reset(std::size_t n) { using value_type = typename T::value_type; const auto old_size = T::size(); T::resize(n, value_type()); std::fill_n(T::begin(), (std::min)(n, old_size), value_type()); } template void serialize(Archive& ar, unsigned /* version */) { ar& make_nvp("vector", static_cast(*this)); } }; template struct array_impl : T { static constexpr bool has_threading_support = accumulators::is_thread_safe::value; array_impl() = default; array_impl(const array_impl& t) : T(t), size_(t.size_) {} array_impl& operator=(const array_impl& t) { T::operator=(t); size_ = t.size_; return *this; } explicit array_impl(T&& t) : T(std::move(t)) {} explicit array_impl(const T& t) : T(t) {} template > explicit array_impl(const U& u) : size_(u.size()) { using std::begin; using std::end; std::copy(begin(u), end(u), this->begin()); } template > array_impl& operator=(const U& u) { if (u.size() > T::max_size()) // for std::arra BOOST_THROW_EXCEPTION(std::length_error("argument size exceeds maximum capacity")); size_ = u.size(); using std::begin; using std::end; std::copy(begin(u), end(u), T::begin()); return *this; } void reset(std::size_t n) { using value_type = typename T::value_type; if (n > T::max_size()) // for std::array BOOST_THROW_EXCEPTION(std::length_error("argument size exceeds maximum capacity")); std::fill_n(T::begin(), n, value_type()); size_ = n; } typename T::iterator end() noexcept { return T::begin() + size_; } typename T::const_iterator end() const noexcept { return T::begin() + size_; } std::size_t size() const noexcept { return size_; } template void serialize(Archive& ar, unsigned /* version */) { ar& make_nvp("size", size_); auto w = detail::make_array_wrapper(T::data(), size_); ar& make_nvp("array", w); } std::size_t size_ = 0; }; template struct map_impl : T { static_assert(std::is_same::value, "requires std::size_t as key_type"); using value_type = typename T::mapped_type; using const_reference = const value_type&; static constexpr bool has_threading_support = false; static_assert( !accumulators::is_thread_safe::value, "std::map and std::unordered_map do not support thread-safe element access. " "If you have a map with thread-safe element access, please file an issue and" "support will be added."); struct reference { reference(map_impl* m, std::size_t i) noexcept : map(m), idx(i) {} reference(const reference&) noexcept = default; reference& operator=(const reference& o) { if (this != &o) operator=(static_cast(o)); return *this; } operator const_reference() const noexcept { return static_cast(map)->operator[](idx); } reference& operator=(const_reference u) { auto it = map->find(idx); if (u == value_type{}) { if (it != static_cast(map)->end()) { map->erase(it); } } else { if (it != static_cast(map)->end()) { it->second = u; } else { map->emplace(idx, u); } } return *this; } template ::value>> reference& operator+=(const U& u) { auto it = map->find(idx); if (it != static_cast(map)->end()) { it->second += u; } else { map->emplace(idx, u); } return *this; } template ::value>> reference& operator-=(const U& u) { auto it = map->find(idx); if (it != static_cast(map)->end()) { it->second -= u; } else { map->emplace(idx, -u); } return *this; } template ::value>> reference& operator*=(const U& u) { auto it = map->find(idx); if (it != static_cast(map)->end()) it->second *= u; return *this; } template ::value>> reference& operator/=(const U& u) { auto it = map->find(idx); if (it != static_cast(map)->end()) { it->second /= u; } else if (!(value_type{} / u == value_type{})) { map->emplace(idx, value_type{} / u); } return *this; } template ::value>> reference operator++() { auto it = map->find(idx); if (it != static_cast(map)->end()) { ++it->second; } else { value_type tmp{}; ++tmp; map->emplace(idx, tmp); } return *this; } template ::value>> value_type operator++(int) { const value_type tmp = *this; operator++(); return tmp; } template ::value>> bool operator==(const U& rhs) const { return operator const_reference() == rhs; } template ::value>> bool operator!=(const U& rhs) const { return !operator==(rhs); } template friend std::basic_ostream& operator<<( std::basic_ostream& os, reference x) { os << static_cast(x); return os; } template auto operator()(const Ts&... args) -> decltype(std::declval()(args...)) { return (*map)[idx](args...); } map_impl* map; std::size_t idx; }; template struct iterator_t : iterator_adaptor, std::size_t, Reference> { iterator_t() = default; template ::value>> iterator_t(const iterator_t& it) noexcept : iterator_t(it.map_, it.base()) {} iterator_t(MapPtr m, std::size_t i) noexcept : iterator_t::iterator_adaptor_(i), map_(m) {} template bool equal(const iterator_t& rhs) const noexcept { return map_ == rhs.map_ && iterator_t::base() == rhs.base(); } Reference operator*() const { return (*map_)[iterator_t::base()]; } MapPtr map_ = nullptr; }; using iterator = iterator_t; using const_iterator = iterator_t; using allocator_type = typename T::allocator_type; map_impl(const allocator_type& a = {}) : T(a) {} map_impl(const map_impl&) = default; map_impl& operator=(const map_impl&) = default; map_impl(map_impl&&) = default; map_impl& operator=(map_impl&&) = default; map_impl(const T& t) : T(t), size_(t.size()) {} map_impl(T&& t) : T(std::move(t)), size_(t.size()) {} template > explicit map_impl(const U& u, const allocator_type& a = {}) : T(a), size_(u.size()) { using std::begin; using std::end; std::copy(begin(u), end(u), this->begin()); } template > map_impl& operator=(const U& u) { if (u.size() < size_) reset(u.size()); else size_ = u.size(); using std::begin; using std::end; std::copy(begin(u), end(u), this->begin()); return *this; } void reset(std::size_t n) { T::clear(); size_ = n; } reference operator[](std::size_t i) noexcept { return {this, i}; } const_reference operator[](std::size_t i) const noexcept { auto it = T::find(i); static const value_type null = value_type{}; if (it == T::end()) return null; return it->second; } iterator begin() noexcept { return {this, 0}; } iterator end() noexcept { return {this, size_}; } const_iterator begin() const noexcept { return {this, 0}; } const_iterator end() const noexcept { return {this, size_}; } std::size_t size() const noexcept { return size_; } template void serialize(Archive& ar, unsigned /* version */) { ar& make_nvp("size", size_); ar& make_nvp("map", static_cast(*this)); } std::size_t size_ = 0; }; template struct ERROR_type_passed_to_storage_adaptor_not_recognized; // clang-format off template using storage_adaptor_impl = mp11::mp_cond< is_vector_like, vector_impl, is_array_like, array_impl, is_map_like, map_impl, std::true_type, ERROR_type_passed_to_storage_adaptor_not_recognized >; // clang-format on } // namespace detail /// Turns any vector-like, array-like, and map-like container into a storage type. template class storage_adaptor : public detail::storage_adaptor_impl { using impl_type = detail::storage_adaptor_impl; public: // standard copy, move, assign storage_adaptor(storage_adaptor&&) = default; storage_adaptor(const storage_adaptor&) = default; storage_adaptor& operator=(storage_adaptor&&) = default; storage_adaptor& operator=(const storage_adaptor&) = default; // forwarding constructor template storage_adaptor(Ts&&... ts) : impl_type(std::forward(ts)...) {} // forwarding assign template storage_adaptor& operator=(U&& u) { impl_type::operator=(std::forward(u)); return *this; } template > bool operator==(const U& u) const { using std::begin; using std::end; return std::equal(this->begin(), this->end(), begin(u), end(u), detail::safe_equal{}); } template void serialize(Archive& ar, unsigned /* version */) { ar& make_nvp("impl", static_cast(*this)); } private: friend struct unsafe_access; }; } // namespace histogram } // namespace boost #endif