]>
Commit | Line | Data |
---|---|---|
92f5a8d4 TL |
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 | ||
33 | 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 | ||
39 | explicit vector_impl(T&& t) : T(std::move(t)) {} | |
40 | explicit vector_impl(const T& t) : T(t) {} | |
41 | ||
42 | template <class U, class = requires_iterable<U>> | |
43 | 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>> | |
47 | 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 | ||
54 | 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> | |
62 | 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; | |
73 | array_impl(const array_impl& t) : T(t), size_(t.size_) {} | |
74 | array_impl& operator=(const array_impl& t) { | |
75 | T::operator=(t); | |
76 | size_ = t.size_; | |
77 | return *this; | |
78 | } | |
79 | ||
80 | explicit array_impl(T&& t) : T(std::move(t)) {} | |
81 | explicit array_impl(const T& t) : T(t) {} | |
82 | ||
83 | template <class U, class = requires_iterable<U>> | |
84 | 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>> | |
91 | 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 | ||
101 | 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 | ||
109 | typename T::iterator end() noexcept { return T::begin() + size_; } | |
110 | typename T::const_iterator end() const noexcept { return T::begin() + size_; } | |
111 | ||
112 | std::size_t size() const noexcept { return size_; } | |
113 | ||
114 | template <class Archive> | |
115 | 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 { | |
140 | reference(map_impl* m, std::size_t i) noexcept : map(m), idx(i) {} | |
141 | ||
142 | reference(const reference&) noexcept = default; | |
143 | reference& operator=(const reference& o) { | |
144 | if (this != &o) operator=(static_cast<const_reference>(o)); | |
145 | return *this; | |
146 | } | |
147 | ||
148 | operator const_reference() const noexcept { | |
149 | return static_cast<const map_impl*>(map)->operator[](idx); | |
150 | } | |
151 | ||
152 | 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>> | |
168 | 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>> | |
180 | 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>> | |
192 | 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>> | |
200 | 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>> | |
212 | 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>> | |
226 | 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>> | |
233 | 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>> | |
238 | bool operator!=(const U& rhs) const { | |
239 | return !operator==(rhs); | |
240 | } | |
241 | ||
f67539c2 | 242 | template <class CharT, class Traits> |
92f5a8d4 TL |
243 | 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> | |
f67539c2 TL |
250 | auto operator()(const Ts&... args) -> decltype(std::declval<value_type>()(args...)) { |
251 | return (*map)[idx](args...); | |
92f5a8d4 TL |
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, class = requires_convertible<M, MapPtr>> | |
263 | iterator_t(const iterator_t<V, R, M>& it) noexcept : iterator_t(it.map_, it.base()) {} | |
264 | iterator_t(MapPtr m, std::size_t i) noexcept | |
265 | : iterator_t::iterator_adaptor_(i), map_(m) {} | |
266 | template <class V, class R, class M> | |
267 | bool equal(const iterator_t<V, R, M>& rhs) const noexcept { | |
268 | return map_ == rhs.map_ && iterator_t::base() == rhs.base(); | |
269 | } | |
270 | Reference operator*() const { return (*map_)[iterator_t::base()]; } | |
271 | MapPtr map_ = nullptr; | |
272 | }; | |
273 | ||
274 | using iterator = iterator_t<value_type, reference, map_impl*>; | |
275 | using const_iterator = iterator_t<const value_type, const_reference, const map_impl*>; | |
276 | ||
277 | using allocator_type = typename T::allocator_type; | |
278 | ||
279 | map_impl(const allocator_type& a = {}) : T(a) {} | |
280 | ||
281 | map_impl(const map_impl&) = default; | |
282 | map_impl& operator=(const map_impl&) = default; | |
283 | map_impl(map_impl&&) = default; | |
284 | map_impl& operator=(map_impl&&) = default; | |
285 | ||
286 | map_impl(const T& t) : T(t), size_(t.size()) {} | |
287 | map_impl(T&& t) : T(std::move(t)), size_(t.size()) {} | |
288 | ||
289 | template <class U, class = requires_iterable<U>> | |
290 | explicit map_impl(const U& u, const allocator_type& a = {}) : T(a), size_(u.size()) { | |
291 | using std::begin; | |
292 | using std::end; | |
293 | std::copy(begin(u), end(u), this->begin()); | |
294 | } | |
295 | ||
296 | template <class U, class = requires_iterable<U>> | |
297 | map_impl& operator=(const U& u) { | |
298 | if (u.size() < size_) | |
299 | reset(u.size()); | |
300 | else | |
301 | size_ = u.size(); | |
302 | using std::begin; | |
303 | using std::end; | |
304 | std::copy(begin(u), end(u), this->begin()); | |
305 | return *this; | |
306 | } | |
307 | ||
308 | void reset(std::size_t n) { | |
309 | T::clear(); | |
310 | size_ = n; | |
311 | } | |
312 | ||
313 | reference operator[](std::size_t i) noexcept { return {this, i}; } | |
314 | const_reference operator[](std::size_t i) const noexcept { | |
315 | auto it = T::find(i); | |
316 | static const value_type null = value_type{}; | |
317 | if (it == T::end()) return null; | |
318 | return it->second; | |
319 | } | |
320 | ||
321 | iterator begin() noexcept { return {this, 0}; } | |
322 | iterator end() noexcept { return {this, size_}; } | |
323 | ||
324 | const_iterator begin() const noexcept { return {this, 0}; } | |
325 | const_iterator end() const noexcept { return {this, size_}; } | |
326 | ||
327 | std::size_t size() const noexcept { return size_; } | |
328 | ||
329 | template <class Archive> | |
330 | void serialize(Archive& ar, unsigned /* version */) { | |
331 | ar& make_nvp("size", size_); | |
332 | ar& make_nvp("map", static_cast<T&>(*this)); | |
333 | } | |
334 | ||
335 | std::size_t size_ = 0; | |
336 | }; | |
337 | ||
338 | template <class T> | |
339 | struct ERROR_type_passed_to_storage_adaptor_not_recognized; | |
340 | ||
341 | // clang-format off | |
342 | template <class T> | |
343 | using storage_adaptor_impl = | |
344 | mp11::mp_cond< | |
345 | is_vector_like<T>, vector_impl<T>, | |
346 | is_array_like<T>, array_impl<T>, | |
347 | is_map_like<T>, map_impl<T>, | |
348 | std::true_type, ERROR_type_passed_to_storage_adaptor_not_recognized<T> | |
349 | >; | |
350 | // clang-format on | |
351 | } // namespace detail | |
352 | ||
353 | /// Turns any vector-like, array-like, and map-like container into a storage type. | |
354 | template <class T> | |
355 | class storage_adaptor : public detail::storage_adaptor_impl<T> { | |
356 | using impl_type = detail::storage_adaptor_impl<T>; | |
357 | ||
358 | public: | |
359 | // standard copy, move, assign | |
360 | storage_adaptor(storage_adaptor&&) = default; | |
361 | storage_adaptor(const storage_adaptor&) = default; | |
362 | storage_adaptor& operator=(storage_adaptor&&) = default; | |
363 | storage_adaptor& operator=(const storage_adaptor&) = default; | |
364 | ||
365 | // forwarding constructor | |
366 | template <class... Ts> | |
367 | storage_adaptor(Ts&&... ts) : impl_type(std::forward<Ts>(ts)...) {} | |
368 | ||
369 | // forwarding assign | |
370 | template <class U> | |
371 | storage_adaptor& operator=(U&& u) { | |
372 | impl_type::operator=(std::forward<U>(u)); | |
373 | return *this; | |
374 | } | |
375 | ||
376 | template <class U, class = detail::requires_iterable<U>> | |
377 | bool operator==(const U& u) const { | |
378 | using std::begin; | |
379 | using std::end; | |
380 | return std::equal(this->begin(), this->end(), begin(u), end(u), detail::safe_equal{}); | |
381 | } | |
382 | ||
383 | template <class Archive> | |
384 | void serialize(Archive& ar, unsigned /* version */) { | |
385 | ar& make_nvp("impl", static_cast<impl_type&>(*this)); | |
386 | } | |
387 | ||
388 | private: | |
389 | friend struct unsafe_access; | |
390 | }; | |
391 | ||
392 | } // namespace histogram | |
393 | } // namespace boost | |
394 | ||
395 | #endif |