• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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