// Copyright 2018 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 namespace boost { namespace histogram { namespace detail { template struct vector_impl : T { vector_impl() = default; explicit vector_impl(const T& t) : T(t) {} explicit vector_impl(typename T::allocator_type a) : T(a) {} template > explicit vector_impl(const U& u) { T::reserve(u.size()); for (auto&& x : u) T::emplace_back(x); } 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 struct array_impl : T { array_impl() = default; explicit array_impl(const T& t) : T(t) {} template > explicit array_impl(const U& u) : size_(u.size()) { std::size_t i = 0; for (auto&& x : u) T::operator[](i++) = x; } template > array_impl& operator=(const U& u) { size_ = u.size(); if (size_ > T::max_size()) // for std::array BOOST_THROW_EXCEPTION(std::length_error( detail::cat("size ", size_, " exceeds maximum capacity ", T::max_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; if (n > T::max_size()) // for std::array BOOST_THROW_EXCEPTION(std::length_error( detail::cat("size ", n, " exceeds maximum capacity ", T::max_size()))); 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_; } 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&; struct reference { reference(map_impl* m, std::size_t i) noexcept : map(m), idx(i) {} reference(const reference&) noexcept = default; reference operator=(reference o) { if (this != &o) operator=(static_cast(o)); return *this; } operator const_reference() const noexcept { return static_cast(map)->operator[](idx); } template > reference& operator=(const U& 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 map->emplace(idx, 1); return *this; } template ::value>> value_type operator++(int) { const value_type tmp = operator const_reference(); operator++(); return tmp; } template decltype(auto) operator()(Ts&&... args) { return map->operator[](idx)(std::forward(args)...); } map_impl* map; std::size_t idx; }; template struct iterator_t : boost::iterator_adaptor, std::size_t, Value, std::random_access_iterator_tag, Reference, std::ptrdiff_t> { iterator_t() = default; template > 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(); } decltype(auto) dereference() const { return (*map_)[iterator_t::base()]; } MapPtr map_ = nullptr; }; using iterator = iterator_t; using const_iterator = iterator_t; map_impl() = default; explicit map_impl(const T& t) : T(t) {} explicit map_impl(typename T::allocator_type a) : T(a) {} template > explicit map_impl(const U& u) : 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_; } std::size_t size_ = 0; }; template struct ERROR_type_passed_to_storage_adaptor_not_recognized; template using storage_adaptor_impl = mp11::mp_if< is_vector_like, vector_impl, mp11::mp_if, array_impl, mp11::mp_if, map_impl, ERROR_type_passed_to_storage_adaptor_not_recognized>>>; } // 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 base_type = detail::storage_adaptor_impl; public: using base_type::base_type; using base_type::operator=; template > bool operator==(const U& u) const { using std::begin; using std::end; return std::equal(this->begin(), this->end(), begin(u), end(u)); } }; } // namespace histogram } // namespace boost #endif