Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:53:11

0001 // Copyright 2018-2019 Hans Dembinski
0002 //
0003 // Distributed under the Boost Software License, Version 1.0.
0004 // (See accompanying file LICENSE_1_0.txt
0005 // or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 
0007 #ifndef BOOST_HISTOGRAM_STORAGE_ADAPTOR_HPP
0008 #define BOOST_HISTOGRAM_STORAGE_ADAPTOR_HPP
0009 
0010 #include <algorithm>
0011 #include <boost/core/nvp.hpp>
0012 #include <boost/histogram/accumulators/is_thread_safe.hpp>
0013 #include <boost/histogram/detail/array_wrapper.hpp>
0014 #include <boost/histogram/detail/detect.hpp>
0015 #include <boost/histogram/detail/iterator_adaptor.hpp>
0016 #include <boost/histogram/detail/safe_comparison.hpp>
0017 #include <boost/histogram/fwd.hpp>
0018 #include <boost/mp11/utility.hpp>
0019 #include <boost/throw_exception.hpp>
0020 #include <stdexcept>
0021 #include <type_traits>
0022 
0023 namespace boost {
0024 namespace histogram {
0025 namespace detail {
0026 
0027 template <class T>
0028 struct vector_impl : T {
0029   using allocator_type = typename T::allocator_type;
0030 
0031   static constexpr bool has_threading_support =
0032       accumulators::is_thread_safe<typename T::value_type>::value;
0033 
0034   vector_impl(const allocator_type& a = {}) : T(a) {}
0035   vector_impl(const vector_impl&) = default;
0036   vector_impl& operator=(const vector_impl&) = default;
0037   vector_impl(vector_impl&&) = default;
0038   vector_impl& operator=(vector_impl&&) = default;
0039 
0040   explicit vector_impl(T&& t) : T(std::move(t)) {}
0041   explicit vector_impl(const T& t) : T(t) {}
0042 
0043   template <class U, class = requires_iterable<U>>
0044   explicit vector_impl(const U& u, const allocator_type& a = {})
0045       : T(std::begin(u), std::end(u), a) {}
0046 
0047   template <class U, class = requires_iterable<U>>
0048   vector_impl& operator=(const U& u) {
0049     T::resize(u.size());
0050     auto it = T::begin();
0051     for (auto&& x : u) *it++ = x;
0052     return *this;
0053   }
0054 
0055   void reset(std::size_t n) {
0056     using value_type = typename T::value_type;
0057     const auto old_size = T::size();
0058     T::resize(n, value_type());
0059     std::fill_n(T::begin(), (std::min)(n, old_size), value_type());
0060   }
0061 
0062   template <class Archive>
0063   void serialize(Archive& ar, unsigned /* version */) {
0064     ar& make_nvp("vector", static_cast<T&>(*this));
0065   }
0066 };
0067 
0068 template <class T>
0069 struct array_impl : T {
0070   static constexpr bool has_threading_support =
0071       accumulators::is_thread_safe<typename T::value_type>::value;
0072 
0073   array_impl() = default;
0074   array_impl(const array_impl& t) : T(t), size_(t.size_) {}
0075   array_impl& operator=(const array_impl& t) {
0076     T::operator=(t);
0077     size_ = t.size_;
0078     return *this;
0079   }
0080 
0081   explicit array_impl(T&& t) : T(std::move(t)) {}
0082   explicit array_impl(const T& t) : T(t) {}
0083 
0084   template <class U, class = requires_iterable<U>>
0085   explicit array_impl(const U& u) : size_(u.size()) {
0086     using std::begin;
0087     using std::end;
0088     std::copy(begin(u), end(u), this->begin());
0089   }
0090 
0091   template <class U, class = requires_iterable<U>>
0092   array_impl& operator=(const U& u) {
0093     if (u.size() > T::max_size()) // for std::arra
0094       BOOST_THROW_EXCEPTION(std::length_error("argument size exceeds maximum capacity"));
0095     size_ = u.size();
0096     using std::begin;
0097     using std::end;
0098     std::copy(begin(u), end(u), T::begin());
0099     return *this;
0100   }
0101 
0102   void reset(std::size_t n) {
0103     using value_type = typename T::value_type;
0104     if (n > T::max_size()) // for std::array
0105       BOOST_THROW_EXCEPTION(std::length_error("argument size exceeds maximum capacity"));
0106     std::fill_n(T::begin(), n, value_type());
0107     size_ = n;
0108   }
0109 
0110   typename T::iterator end() noexcept { return T::begin() + size_; }
0111   typename T::const_iterator end() const noexcept { return T::begin() + size_; }
0112 
0113   std::size_t size() const noexcept { return size_; }
0114 
0115   template <class Archive>
0116   void serialize(Archive& ar, unsigned /* version */) {
0117     ar& make_nvp("size", size_);
0118     auto w = detail::make_array_wrapper(T::data(), size_);
0119     ar& make_nvp("array", w);
0120   }
0121 
0122   std::size_t size_ = 0;
0123 };
0124 
0125 template <class T>
0126 struct map_impl : T {
0127   static_assert(std::is_same<typename T::key_type, std::size_t>::value,
0128                 "requires std::size_t as key_type");
0129 
0130   using value_type = typename T::mapped_type;
0131   using const_reference = const value_type&;
0132 
0133   static constexpr bool has_threading_support = false;
0134   static_assert(
0135       !accumulators::is_thread_safe<value_type>::value,
0136       "std::map and std::unordered_map do not support thread-safe element access. "
0137       "If you have a map with thread-safe element access, please file an issue and"
0138       "support will be added.");
0139 
0140   struct reference {
0141     reference(map_impl* m, std::size_t i) noexcept : map(m), idx(i) {}
0142 
0143     reference(const reference&) noexcept = default;
0144     reference& operator=(const reference& o) {
0145       if (this != &o) operator=(static_cast<const_reference>(o));
0146       return *this;
0147     }
0148 
0149     operator const_reference() const noexcept {
0150       return static_cast<const map_impl*>(map)->operator[](idx);
0151     }
0152 
0153     reference& operator=(const_reference u) {
0154       auto it = map->find(idx);
0155       if (u == value_type{}) {
0156         if (it != static_cast<T*>(map)->end()) { map->erase(it); }
0157       } else {
0158         if (it != static_cast<T*>(map)->end()) {
0159           it->second = u;
0160         } else {
0161           map->emplace(idx, u);
0162         }
0163       }
0164       return *this;
0165     }
0166 
0167     template <class U, class V = value_type,
0168               class = std::enable_if_t<has_operator_radd<V, U>::value>>
0169     reference& operator+=(const U& u) {
0170       auto it = map->find(idx);
0171       if (it != static_cast<T*>(map)->end()) {
0172         it->second += u;
0173       } else {
0174         map->emplace(idx, u);
0175       }
0176       return *this;
0177     }
0178 
0179     template <class U, class V = value_type,
0180               class = std::enable_if_t<has_operator_rsub<V, U>::value>>
0181     reference& operator-=(const U& u) {
0182       auto it = map->find(idx);
0183       if (it != static_cast<T*>(map)->end()) {
0184         it->second -= u;
0185       } else {
0186         map->emplace(idx, -u);
0187       }
0188       return *this;
0189     }
0190 
0191     template <class U, class V = value_type,
0192               class = std::enable_if_t<has_operator_rmul<V, U>::value>>
0193     reference& operator*=(const U& u) {
0194       auto it = map->find(idx);
0195       if (it != static_cast<T*>(map)->end()) it->second *= u;
0196       return *this;
0197     }
0198 
0199     template <class U, class V = value_type,
0200               class = std::enable_if_t<has_operator_rdiv<V, U>::value>>
0201     reference& operator/=(const U& u) {
0202       auto it = map->find(idx);
0203       if (it != static_cast<T*>(map)->end()) {
0204         it->second /= u;
0205       } else if (!(value_type{} / u == value_type{})) {
0206         map->emplace(idx, value_type{} / u);
0207       }
0208       return *this;
0209     }
0210 
0211     template <class V = value_type,
0212               class = std::enable_if_t<has_operator_preincrement<V>::value>>
0213     reference operator++() {
0214       auto it = map->find(idx);
0215       if (it != static_cast<T*>(map)->end()) {
0216         ++it->second;
0217       } else {
0218         value_type tmp{};
0219         ++tmp;
0220         map->emplace(idx, tmp);
0221       }
0222       return *this;
0223     }
0224 
0225     template <class V = value_type,
0226               class = std::enable_if_t<has_operator_preincrement<V>::value>>
0227     value_type operator++(int) {
0228       const value_type tmp = *this;
0229       operator++();
0230       return tmp;
0231     }
0232 
0233     template <class U, class = std::enable_if_t<has_operator_equal<value_type, U>::value>>
0234     bool operator==(const U& rhs) const {
0235       return operator const_reference() == rhs;
0236     }
0237 
0238     template <class U, class = std::enable_if_t<has_operator_equal<value_type, U>::value>>
0239     bool operator!=(const U& rhs) const {
0240       return !operator==(rhs);
0241     }
0242 
0243     template <class CharT, class Traits>
0244     friend std::basic_ostream<CharT, Traits>& operator<<(
0245         std::basic_ostream<CharT, Traits>& os, reference x) {
0246       os << static_cast<const_reference>(x);
0247       return os;
0248     }
0249 
0250     template <class... Ts>
0251     auto operator()(const Ts&... args) -> decltype(std::declval<value_type>()(args...)) {
0252       return (*map)[idx](args...);
0253     }
0254 
0255     map_impl* map;
0256     std::size_t idx;
0257   };
0258 
0259   template <class Value, class Reference, class MapPtr>
0260   struct iterator_t
0261       : iterator_adaptor<iterator_t<Value, Reference, MapPtr>, std::size_t, Reference> {
0262     iterator_t() = default;
0263     template <class V, class R, class M,
0264               class = std::enable_if_t<std::is_convertible<M, MapPtr>::value>>
0265     iterator_t(const iterator_t<V, R, M>& it) noexcept : iterator_t(it.map_, it.base()) {}
0266     iterator_t(MapPtr m, std::size_t i) noexcept
0267         : iterator_t::iterator_adaptor_(i), map_(m) {}
0268     template <class V, class R, class M>
0269     bool equal(const iterator_t<V, R, M>& rhs) const noexcept {
0270       return map_ == rhs.map_ && iterator_t::base() == rhs.base();
0271     }
0272     Reference operator*() const { return (*map_)[iterator_t::base()]; }
0273     MapPtr map_ = nullptr;
0274   };
0275 
0276   using iterator = iterator_t<value_type, reference, map_impl*>;
0277   using const_iterator = iterator_t<const value_type, const_reference, const map_impl*>;
0278 
0279   using allocator_type = typename T::allocator_type;
0280 
0281   map_impl(const allocator_type& a = {}) : T(a) {}
0282 
0283   map_impl(const map_impl&) = default;
0284   map_impl& operator=(const map_impl&) = default;
0285   map_impl(map_impl&&) = default;
0286   map_impl& operator=(map_impl&&) = default;
0287 
0288   map_impl(const T& t) : T(t), size_(t.size()) {}
0289   map_impl(T&& t) : T(std::move(t)), size_(t.size()) {}
0290 
0291   template <class U, class = requires_iterable<U>>
0292   explicit map_impl(const U& u, const allocator_type& a = {}) : T(a), size_(u.size()) {
0293     using std::begin;
0294     using std::end;
0295     std::copy(begin(u), end(u), this->begin());
0296   }
0297 
0298   template <class U, class = requires_iterable<U>>
0299   map_impl& operator=(const U& u) {
0300     if (u.size() < size_)
0301       reset(u.size());
0302     else
0303       size_ = u.size();
0304     using std::begin;
0305     using std::end;
0306     std::copy(begin(u), end(u), this->begin());
0307     return *this;
0308   }
0309 
0310   void reset(std::size_t n) {
0311     T::clear();
0312     size_ = n;
0313   }
0314 
0315   reference operator[](std::size_t i) noexcept { return {this, i}; }
0316   const_reference operator[](std::size_t i) const noexcept {
0317     auto it = T::find(i);
0318     static const value_type null = value_type{};
0319     if (it == T::end()) return null;
0320     return it->second;
0321   }
0322 
0323   iterator begin() noexcept { return {this, 0}; }
0324   iterator end() noexcept { return {this, size_}; }
0325 
0326   const_iterator begin() const noexcept { return {this, 0}; }
0327   const_iterator end() const noexcept { return {this, size_}; }
0328 
0329   std::size_t size() const noexcept { return size_; }
0330 
0331   template <class Archive>
0332   void serialize(Archive& ar, unsigned /* version */) {
0333     ar& make_nvp("size", size_);
0334     ar& make_nvp("map", static_cast<T&>(*this));
0335   }
0336 
0337   std::size_t size_ = 0;
0338 };
0339 
0340 template <class T>
0341 struct ERROR_type_passed_to_storage_adaptor_not_recognized;
0342 
0343 // clang-format off
0344 template <class T>
0345 using storage_adaptor_impl =
0346   mp11::mp_cond<
0347     is_vector_like<T>, vector_impl<T>,
0348     is_array_like<T>, array_impl<T>,
0349     is_map_like<T>, map_impl<T>,
0350     std::true_type, ERROR_type_passed_to_storage_adaptor_not_recognized<T>
0351   >;
0352 // clang-format on
0353 } // namespace detail
0354 
0355 /// Turns any vector-like, array-like, and map-like container into a storage type.
0356 template <class T>
0357 class storage_adaptor : public detail::storage_adaptor_impl<T> {
0358   using impl_type = detail::storage_adaptor_impl<T>;
0359 
0360 public:
0361   // standard copy, move, assign
0362   storage_adaptor(storage_adaptor&&) = default;
0363   storage_adaptor(const storage_adaptor&) = default;
0364   storage_adaptor& operator=(storage_adaptor&&) = default;
0365   storage_adaptor& operator=(const storage_adaptor&) = default;
0366 
0367   // forwarding constructor
0368   template <class... Ts>
0369   storage_adaptor(Ts&&... ts) : impl_type(std::forward<Ts>(ts)...) {}
0370 
0371   // forwarding assign
0372   template <class U>
0373   storage_adaptor& operator=(U&& u) {
0374     impl_type::operator=(std::forward<U>(u));
0375     return *this;
0376   }
0377 
0378   template <class U, class = detail::requires_iterable<U>>
0379   bool operator==(const U& u) const {
0380     using std::begin;
0381     using std::end;
0382     return std::equal(this->begin(), this->end(), begin(u), end(u), detail::safe_equal{});
0383   }
0384 
0385   template <class Archive>
0386   void serialize(Archive& ar, unsigned /* version */) {
0387     ar& make_nvp("impl", static_cast<impl_type&>(*this));
0388   }
0389 
0390 private:
0391   friend struct unsafe_access;
0392 };
0393 
0394 } // namespace histogram
0395 } // namespace boost
0396 
0397 #endif