File indexing completed on 2025-12-16 09:53:11
0001
0002
0003
0004
0005
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 ) {
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())
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())
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 ) {
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 ) {
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
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
0353 }
0354
0355
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
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
0368 template <class... Ts>
0369 storage_adaptor(Ts&&... ts) : impl_type(std::forward<Ts>(ts)...) {}
0370
0371
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 ) {
0387 ar& make_nvp("impl", static_cast<impl_type&>(*this));
0388 }
0389
0390 private:
0391 friend struct unsafe_access;
0392 };
0393
0394 }
0395 }
0396
0397 #endif