1 // Copyright 2018-2019 Hans Dembinski 2 // 3 // Distributed under the Boost Software License, Version 1.0. 4 // (See accompanying file LICENSE_1_0.txt 5 // or copy at http://www.boost.org/LICENSE_1_0.txt) 6 7 #ifndef BOOST_HISTOGRAM_STORAGE_ADAPTOR_HPP 8 #define BOOST_HISTOGRAM_STORAGE_ADAPTOR_HPP 9 10 #include <algorithm> 11 #include <boost/core/nvp.hpp> 12 #include <boost/histogram/detail/array_wrapper.hpp> 13 #include <boost/histogram/detail/detect.hpp> 14 #include <boost/histogram/detail/iterator_adaptor.hpp> 15 #include <boost/histogram/detail/safe_comparison.hpp> 16 #include <boost/histogram/fwd.hpp> 17 #include <boost/mp11/utility.hpp> 18 #include <boost/throw_exception.hpp> 19 #include <stdexcept> 20 #include <type_traits> 21 22 namespace boost { 23 namespace histogram { 24 namespace detail { 25 26 template <class T> 27 struct vector_impl : T { 28 using allocator_type = typename T::allocator_type; 29 30 static constexpr bool has_threading_support = 31 accumulators::is_thread_safe<typename T::value_type>::value; 32 vector_implboost::histogram::detail::vector_impl33 vector_impl(const allocator_type& a = {}) : T(a) {} 34 vector_impl(const vector_impl&) = default; 35 vector_impl& operator=(const vector_impl&) = default; 36 vector_impl(vector_impl&&) = default; 37 vector_impl& operator=(vector_impl&&) = default; 38 vector_implboost::histogram::detail::vector_impl39 explicit vector_impl(T&& t) : T(std::move(t)) {} vector_implboost::histogram::detail::vector_impl40 explicit vector_impl(const T& t) : T(t) {} 41 42 template <class U, class = requires_iterable<U>> vector_implboost::histogram::detail::vector_impl43 explicit vector_impl(const U& u, const allocator_type& a = {}) 44 : T(std::begin(u), std::end(u), a) {} 45 46 template <class U, class = requires_iterable<U>> operator =boost::histogram::detail::vector_impl47 vector_impl& operator=(const U& u) { 48 T::resize(u.size()); 49 auto it = T::begin(); 50 for (auto&& x : u) *it++ = x; 51 return *this; 52 } 53 resetboost::histogram::detail::vector_impl54 void reset(std::size_t n) { 55 using value_type = typename T::value_type; 56 const auto old_size = T::size(); 57 T::resize(n, value_type()); 58 std::fill_n(T::begin(), (std::min)(n, old_size), value_type()); 59 } 60 61 template <class Archive> serializeboost::histogram::detail::vector_impl62 void serialize(Archive& ar, unsigned /* version */) { 63 ar& make_nvp("vector", static_cast<T&>(*this)); 64 } 65 }; 66 67 template <class T> 68 struct array_impl : T { 69 static constexpr bool has_threading_support = 70 accumulators::is_thread_safe<typename T::value_type>::value; 71 72 array_impl() = default; array_implboost::histogram::detail::array_impl73 array_impl(const array_impl& t) : T(t), size_(t.size_) {} operator =boost::histogram::detail::array_impl74 array_impl& operator=(const array_impl& t) { 75 T::operator=(t); 76 size_ = t.size_; 77 return *this; 78 } 79 array_implboost::histogram::detail::array_impl80 explicit array_impl(T&& t) : T(std::move(t)) {} array_implboost::histogram::detail::array_impl81 explicit array_impl(const T& t) : T(t) {} 82 83 template <class U, class = requires_iterable<U>> array_implboost::histogram::detail::array_impl84 explicit array_impl(const U& u) : size_(u.size()) { 85 using std::begin; 86 using std::end; 87 std::copy(begin(u), end(u), this->begin()); 88 } 89 90 template <class U, class = requires_iterable<U>> operator =boost::histogram::detail::array_impl91 array_impl& operator=(const U& u) { 92 if (u.size() > T::max_size()) // for std::arra 93 BOOST_THROW_EXCEPTION(std::length_error("argument size exceeds maximum capacity")); 94 size_ = u.size(); 95 using std::begin; 96 using std::end; 97 std::copy(begin(u), end(u), T::begin()); 98 return *this; 99 } 100 resetboost::histogram::detail::array_impl101 void reset(std::size_t n) { 102 using value_type = typename T::value_type; 103 if (n > T::max_size()) // for std::array 104 BOOST_THROW_EXCEPTION(std::length_error("argument size exceeds maximum capacity")); 105 std::fill_n(T::begin(), n, value_type()); 106 size_ = n; 107 } 108 endboost::histogram::detail::array_impl109 typename T::iterator end() noexcept { return T::begin() + size_; } endboost::histogram::detail::array_impl110 typename T::const_iterator end() const noexcept { return T::begin() + size_; } 111 sizeboost::histogram::detail::array_impl112 std::size_t size() const noexcept { return size_; } 113 114 template <class Archive> serializeboost::histogram::detail::array_impl115 void serialize(Archive& ar, unsigned /* version */) { 116 ar& make_nvp("size", size_); 117 auto w = detail::make_array_wrapper(T::data(), size_); 118 ar& make_nvp("array", w); 119 } 120 121 std::size_t size_ = 0; 122 }; 123 124 template <class T> 125 struct map_impl : T { 126 static_assert(std::is_same<typename T::key_type, std::size_t>::value, 127 "requires std::size_t as key_type"); 128 129 using value_type = typename T::mapped_type; 130 using const_reference = const value_type&; 131 132 static constexpr bool has_threading_support = false; 133 static_assert( 134 !accumulators::is_thread_safe<value_type>::value, 135 "std::map and std::unordered_map do not support thread-safe element access. " 136 "If you have a map with thread-safe element access, please file an issue and" 137 "support will be added."); 138 139 struct reference { referenceboost::histogram::detail::map_impl::reference140 reference(map_impl* m, std::size_t i) noexcept : map(m), idx(i) {} 141 142 reference(const reference&) noexcept = default; operator =boost::histogram::detail::map_impl::reference143 reference& operator=(const reference& o) { 144 if (this != &o) operator=(static_cast<const_reference>(o)); 145 return *this; 146 } 147 operator const_referenceboost::histogram::detail::map_impl::reference148 operator const_reference() const noexcept { 149 return static_cast<const map_impl*>(map)->operator[](idx); 150 } 151 operator =boost::histogram::detail::map_impl::reference152 reference& operator=(const_reference u) { 153 auto it = map->find(idx); 154 if (u == value_type{}) { 155 if (it != static_cast<T*>(map)->end()) { map->erase(it); } 156 } else { 157 if (it != static_cast<T*>(map)->end()) { 158 it->second = u; 159 } else { 160 map->emplace(idx, u); 161 } 162 } 163 return *this; 164 } 165 166 template <class U, class V = value_type, 167 class = std::enable_if_t<has_operator_radd<V, U>::value>> operator +=boost::histogram::detail::map_impl::reference168 reference& operator+=(const U& u) { 169 auto it = map->find(idx); 170 if (it != static_cast<T*>(map)->end()) { 171 it->second += u; 172 } else { 173 map->emplace(idx, u); 174 } 175 return *this; 176 } 177 178 template <class U, class V = value_type, 179 class = std::enable_if_t<has_operator_rsub<V, U>::value>> operator -=boost::histogram::detail::map_impl::reference180 reference& operator-=(const U& u) { 181 auto it = map->find(idx); 182 if (it != static_cast<T*>(map)->end()) { 183 it->second -= u; 184 } else { 185 map->emplace(idx, -u); 186 } 187 return *this; 188 } 189 190 template <class U, class V = value_type, 191 class = std::enable_if_t<has_operator_rmul<V, U>::value>> operator *=boost::histogram::detail::map_impl::reference192 reference& operator*=(const U& u) { 193 auto it = map->find(idx); 194 if (it != static_cast<T*>(map)->end()) it->second *= u; 195 return *this; 196 } 197 198 template <class U, class V = value_type, 199 class = std::enable_if_t<has_operator_rdiv<V, U>::value>> operator /=boost::histogram::detail::map_impl::reference200 reference& operator/=(const U& u) { 201 auto it = map->find(idx); 202 if (it != static_cast<T*>(map)->end()) { 203 it->second /= u; 204 } else if (!(value_type{} / u == value_type{})) { 205 map->emplace(idx, value_type{} / u); 206 } 207 return *this; 208 } 209 210 template <class V = value_type, 211 class = std::enable_if_t<has_operator_preincrement<V>::value>> operator ++boost::histogram::detail::map_impl::reference212 reference operator++() { 213 auto it = map->find(idx); 214 if (it != static_cast<T*>(map)->end()) { 215 ++it->second; 216 } else { 217 value_type tmp{}; 218 ++tmp; 219 map->emplace(idx, tmp); 220 } 221 return *this; 222 } 223 224 template <class V = value_type, 225 class = std::enable_if_t<has_operator_preincrement<V>::value>> operator ++boost::histogram::detail::map_impl::reference226 value_type operator++(int) { 227 const value_type tmp = *this; 228 operator++(); 229 return tmp; 230 } 231 232 template <class U, class = std::enable_if_t<has_operator_equal<value_type, U>::value>> operator ==boost::histogram::detail::map_impl::reference233 bool operator==(const U& rhs) const { 234 return operator const_reference() == rhs; 235 } 236 237 template <class U, class = std::enable_if_t<has_operator_equal<value_type, U>::value>> operator !=boost::histogram::detail::map_impl::reference238 bool operator!=(const U& rhs) const { 239 return !operator==(rhs); 240 } 241 242 template <class CharT, class Traits> operator <<boost::histogram::detail::map_impl243 friend std::basic_ostream<CharT, Traits>& operator<<( 244 std::basic_ostream<CharT, Traits>& os, reference x) { 245 os << static_cast<const_reference>(x); 246 return os; 247 } 248 249 template <class... Ts> operator ()boost::histogram::detail::map_impl::reference250 auto operator()(const Ts&... args) -> decltype(std::declval<value_type>()(args...)) { 251 return (*map)[idx](args...); 252 } 253 254 map_impl* map; 255 std::size_t idx; 256 }; 257 258 template <class Value, class Reference, class MapPtr> 259 struct iterator_t 260 : iterator_adaptor<iterator_t<Value, Reference, MapPtr>, std::size_t, Reference> { 261 iterator_t() = default; 262 template <class V, class R, class M, 263 class = std::enable_if_t<std::is_convertible<M, MapPtr>::value>> iterator_tboost::histogram::detail::map_impl::iterator_t264 iterator_t(const iterator_t<V, R, M>& it) noexcept : iterator_t(it.map_, it.base()) {} iterator_tboost::histogram::detail::map_impl::iterator_t265 iterator_t(MapPtr m, std::size_t i) noexcept 266 : iterator_t::iterator_adaptor_(i), map_(m) {} 267 template <class V, class R, class M> equalboost::histogram::detail::map_impl::iterator_t268 bool equal(const iterator_t<V, R, M>& rhs) const noexcept { 269 return map_ == rhs.map_ && iterator_t::base() == rhs.base(); 270 } operator *boost::histogram::detail::map_impl::iterator_t271 Reference operator*() const { return (*map_)[iterator_t::base()]; } 272 MapPtr map_ = nullptr; 273 }; 274 275 using iterator = iterator_t<value_type, reference, map_impl*>; 276 using const_iterator = iterator_t<const value_type, const_reference, const map_impl*>; 277 278 using allocator_type = typename T::allocator_type; 279 map_implboost::histogram::detail::map_impl280 map_impl(const allocator_type& a = {}) : T(a) {} 281 282 map_impl(const map_impl&) = default; 283 map_impl& operator=(const map_impl&) = default; 284 map_impl(map_impl&&) = default; 285 map_impl& operator=(map_impl&&) = default; 286 map_implboost::histogram::detail::map_impl287 map_impl(const T& t) : T(t), size_(t.size()) {} map_implboost::histogram::detail::map_impl288 map_impl(T&& t) : T(std::move(t)), size_(t.size()) {} 289 290 template <class U, class = requires_iterable<U>> map_implboost::histogram::detail::map_impl291 explicit map_impl(const U& u, const allocator_type& a = {}) : T(a), size_(u.size()) { 292 using std::begin; 293 using std::end; 294 std::copy(begin(u), end(u), this->begin()); 295 } 296 297 template <class U, class = requires_iterable<U>> operator =boost::histogram::detail::map_impl298 map_impl& operator=(const U& u) { 299 if (u.size() < size_) 300 reset(u.size()); 301 else 302 size_ = u.size(); 303 using std::begin; 304 using std::end; 305 std::copy(begin(u), end(u), this->begin()); 306 return *this; 307 } 308 resetboost::histogram::detail::map_impl309 void reset(std::size_t n) { 310 T::clear(); 311 size_ = n; 312 } 313 operator []boost::histogram::detail::map_impl314 reference operator[](std::size_t i) noexcept { return {this, i}; } operator []boost::histogram::detail::map_impl315 const_reference operator[](std::size_t i) const noexcept { 316 auto it = T::find(i); 317 static const value_type null = value_type{}; 318 if (it == T::end()) return null; 319 return it->second; 320 } 321 beginboost::histogram::detail::map_impl322 iterator begin() noexcept { return {this, 0}; } endboost::histogram::detail::map_impl323 iterator end() noexcept { return {this, size_}; } 324 beginboost::histogram::detail::map_impl325 const_iterator begin() const noexcept { return {this, 0}; } endboost::histogram::detail::map_impl326 const_iterator end() const noexcept { return {this, size_}; } 327 sizeboost::histogram::detail::map_impl328 std::size_t size() const noexcept { return size_; } 329 330 template <class Archive> serializeboost::histogram::detail::map_impl331 void serialize(Archive& ar, unsigned /* version */) { 332 ar& make_nvp("size", size_); 333 ar& make_nvp("map", static_cast<T&>(*this)); 334 } 335 336 std::size_t size_ = 0; 337 }; 338 339 template <class T> 340 struct ERROR_type_passed_to_storage_adaptor_not_recognized; 341 342 // clang-format off 343 template <class T> 344 using storage_adaptor_impl = 345 mp11::mp_cond< 346 is_vector_like<T>, vector_impl<T>, 347 is_array_like<T>, array_impl<T>, 348 is_map_like<T>, map_impl<T>, 349 std::true_type, ERROR_type_passed_to_storage_adaptor_not_recognized<T> 350 >; 351 // clang-format on 352 } // namespace detail 353 354 /// Turns any vector-like, array-like, and map-like container into a storage type. 355 template <class T> 356 class storage_adaptor : public detail::storage_adaptor_impl<T> { 357 using impl_type = detail::storage_adaptor_impl<T>; 358 359 public: 360 // standard copy, move, assign 361 storage_adaptor(storage_adaptor&&) = default; 362 storage_adaptor(const storage_adaptor&) = default; 363 storage_adaptor& operator=(storage_adaptor&&) = default; 364 storage_adaptor& operator=(const storage_adaptor&) = default; 365 366 // forwarding constructor 367 template <class... Ts> storage_adaptor(Ts &&...ts)368 storage_adaptor(Ts&&... ts) : impl_type(std::forward<Ts>(ts)...) {} 369 370 // forwarding assign 371 template <class U> operator =(U && u)372 storage_adaptor& operator=(U&& u) { 373 impl_type::operator=(std::forward<U>(u)); 374 return *this; 375 } 376 377 template <class U, class = detail::requires_iterable<U>> operator ==(const U & u) const378 bool operator==(const U& u) const { 379 using std::begin; 380 using std::end; 381 return std::equal(this->begin(), this->end(), begin(u), end(u), detail::safe_equal{}); 382 } 383 384 template <class Archive> serialize(Archive & ar,unsigned)385 void serialize(Archive& ar, unsigned /* version */) { 386 ar& make_nvp("impl", static_cast<impl_type&>(*this)); 387 } 388 389 private: 390 friend struct unsafe_access; 391 }; 392 393 } // namespace histogram 394 } // namespace boost 395 396 #endif 397