storage_adaptor.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  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. #ifndef BOOST_HISTOGRAM_STORAGE_ADAPTOR_HPP
  7. #define BOOST_HISTOGRAM_STORAGE_ADAPTOR_HPP
  8. #include <algorithm>
  9. #include <boost/core/nvp.hpp>
  10. #include <boost/histogram/accumulators/is_thread_safe.hpp>
  11. #include <boost/histogram/detail/array_wrapper.hpp>
  12. #include <boost/histogram/detail/detect.hpp>
  13. #include <boost/histogram/detail/iterator_adaptor.hpp>
  14. #include <boost/histogram/detail/safe_comparison.hpp>
  15. #include <boost/histogram/fwd.hpp>
  16. #include <boost/mp11/utility.hpp>
  17. #include <boost/throw_exception.hpp>
  18. #include <stdexcept>
  19. #include <type_traits>
  20. namespace boost {
  21. namespace histogram {
  22. namespace detail {
  23. template <class T>
  24. struct vector_impl : T {
  25. using allocator_type = typename T::allocator_type;
  26. static constexpr bool has_threading_support =
  27. accumulators::is_thread_safe<typename T::value_type>::value;
  28. vector_impl(const allocator_type& a = {}) : T(a) {}
  29. vector_impl(const vector_impl&) = default;
  30. vector_impl& operator=(const vector_impl&) = default;
  31. vector_impl(vector_impl&&) = default;
  32. vector_impl& operator=(vector_impl&&) = default;
  33. explicit vector_impl(T&& t) : T(std::move(t)) {}
  34. explicit vector_impl(const T& t) : T(t) {}
  35. template <class U, class = requires_iterable<U>>
  36. explicit vector_impl(const U& u, const allocator_type& a = {})
  37. : T(std::begin(u), std::end(u), a) {}
  38. template <class U, class = requires_iterable<U>>
  39. vector_impl& operator=(const U& u) {
  40. T::resize(u.size());
  41. auto it = T::begin();
  42. for (auto&& x : u) *it++ = x;
  43. return *this;
  44. }
  45. void reset(std::size_t n) {
  46. using value_type = typename T::value_type;
  47. const auto old_size = T::size();
  48. T::resize(n, value_type());
  49. std::fill_n(T::begin(), (std::min)(n, old_size), value_type());
  50. }
  51. template <class Archive>
  52. void serialize(Archive& ar, unsigned /* version */) {
  53. ar& make_nvp("vector", static_cast<T&>(*this));
  54. }
  55. };
  56. template <class T>
  57. struct array_impl : T {
  58. static constexpr bool has_threading_support =
  59. accumulators::is_thread_safe<typename T::value_type>::value;
  60. array_impl() = default;
  61. array_impl(const array_impl& t) : T(t), size_(t.size_) {}
  62. array_impl& operator=(const array_impl& t) {
  63. T::operator=(t);
  64. size_ = t.size_;
  65. return *this;
  66. }
  67. explicit array_impl(T&& t) : T(std::move(t)) {}
  68. explicit array_impl(const T& t) : T(t) {}
  69. template <class U, class = requires_iterable<U>>
  70. explicit array_impl(const U& u) : size_(u.size()) {
  71. using std::begin;
  72. using std::end;
  73. std::copy(begin(u), end(u), this->begin());
  74. }
  75. template <class U, class = requires_iterable<U>>
  76. array_impl& operator=(const U& u) {
  77. if (u.size() > T::max_size()) // for std::arra
  78. BOOST_THROW_EXCEPTION(std::length_error("argument size exceeds maximum capacity"));
  79. size_ = u.size();
  80. using std::begin;
  81. using std::end;
  82. std::copy(begin(u), end(u), T::begin());
  83. return *this;
  84. }
  85. void reset(std::size_t n) {
  86. using value_type = typename T::value_type;
  87. if (n > T::max_size()) // for std::array
  88. BOOST_THROW_EXCEPTION(std::length_error("argument size exceeds maximum capacity"));
  89. std::fill_n(T::begin(), n, value_type());
  90. size_ = n;
  91. }
  92. typename T::iterator end() noexcept { return T::begin() + size_; }
  93. typename T::const_iterator end() const noexcept { return T::begin() + size_; }
  94. std::size_t size() const noexcept { return size_; }
  95. template <class Archive>
  96. void serialize(Archive& ar, unsigned /* version */) {
  97. ar& make_nvp("size", size_);
  98. auto w = detail::make_array_wrapper(T::data(), size_);
  99. ar& make_nvp("array", w);
  100. }
  101. std::size_t size_ = 0;
  102. };
  103. template <class T>
  104. struct map_impl : T {
  105. static_assert(std::is_same<typename T::key_type, std::size_t>::value,
  106. "requires std::size_t as key_type");
  107. using value_type = typename T::mapped_type;
  108. using const_reference = const value_type&;
  109. static constexpr bool has_threading_support = false;
  110. static_assert(
  111. !accumulators::is_thread_safe<value_type>::value,
  112. "std::map and std::unordered_map do not support thread-safe element access. "
  113. "If you have a map with thread-safe element access, please file an issue and"
  114. "support will be added.");
  115. struct reference {
  116. reference(map_impl* m, std::size_t i) noexcept : map(m), idx(i) {}
  117. reference(const reference&) noexcept = default;
  118. reference& operator=(const reference& o) {
  119. if (this != &o) operator=(static_cast<const_reference>(o));
  120. return *this;
  121. }
  122. operator const_reference() const noexcept {
  123. return static_cast<const map_impl*>(map)->operator[](idx);
  124. }
  125. reference& operator=(const_reference u) {
  126. auto it = map->find(idx);
  127. if (u == value_type{}) {
  128. if (it != static_cast<T*>(map)->end()) { map->erase(it); }
  129. } else {
  130. if (it != static_cast<T*>(map)->end()) {
  131. it->second = u;
  132. } else {
  133. map->emplace(idx, u);
  134. }
  135. }
  136. return *this;
  137. }
  138. template <class U, class V = value_type,
  139. class = std::enable_if_t<has_operator_radd<V, U>::value>>
  140. reference& operator+=(const U& u) {
  141. auto it = map->find(idx);
  142. if (it != static_cast<T*>(map)->end()) {
  143. it->second += u;
  144. } else {
  145. map->emplace(idx, u);
  146. }
  147. return *this;
  148. }
  149. template <class U, class V = value_type,
  150. class = std::enable_if_t<has_operator_rsub<V, U>::value>>
  151. reference& operator-=(const U& u) {
  152. auto it = map->find(idx);
  153. if (it != static_cast<T*>(map)->end()) {
  154. it->second -= u;
  155. } else {
  156. map->emplace(idx, -u);
  157. }
  158. return *this;
  159. }
  160. template <class U, class V = value_type,
  161. class = std::enable_if_t<has_operator_rmul<V, U>::value>>
  162. reference& operator*=(const U& u) {
  163. auto it = map->find(idx);
  164. if (it != static_cast<T*>(map)->end()) it->second *= u;
  165. return *this;
  166. }
  167. template <class U, class V = value_type,
  168. class = std::enable_if_t<has_operator_rdiv<V, U>::value>>
  169. reference& operator/=(const U& u) {
  170. auto it = map->find(idx);
  171. if (it != static_cast<T*>(map)->end()) {
  172. it->second /= u;
  173. } else if (!(value_type{} / u == value_type{})) {
  174. map->emplace(idx, value_type{} / u);
  175. }
  176. return *this;
  177. }
  178. template <class V = value_type,
  179. class = std::enable_if_t<has_operator_preincrement<V>::value>>
  180. reference operator++() {
  181. auto it = map->find(idx);
  182. if (it != static_cast<T*>(map)->end()) {
  183. ++it->second;
  184. } else {
  185. value_type tmp{};
  186. ++tmp;
  187. map->emplace(idx, tmp);
  188. }
  189. return *this;
  190. }
  191. template <class V = value_type,
  192. class = std::enable_if_t<has_operator_preincrement<V>::value>>
  193. value_type operator++(int) {
  194. const value_type tmp = *this;
  195. operator++();
  196. return tmp;
  197. }
  198. template <class U, class = std::enable_if_t<has_operator_equal<value_type, U>::value>>
  199. bool operator==(const U& rhs) const {
  200. return operator const_reference() == rhs;
  201. }
  202. template <class U, class = std::enable_if_t<has_operator_equal<value_type, U>::value>>
  203. bool operator!=(const U& rhs) const {
  204. return !operator==(rhs);
  205. }
  206. template <class CharT, class Traits>
  207. friend std::basic_ostream<CharT, Traits>& operator<<(
  208. std::basic_ostream<CharT, Traits>& os, reference x) {
  209. os << static_cast<const_reference>(x);
  210. return os;
  211. }
  212. template <class... Ts>
  213. auto operator()(const Ts&... args) -> decltype(std::declval<value_type>()(args...)) {
  214. return (*map)[idx](args...);
  215. }
  216. map_impl* map;
  217. std::size_t idx;
  218. };
  219. template <class Value, class Reference, class MapPtr>
  220. struct iterator_t
  221. : iterator_adaptor<iterator_t<Value, Reference, MapPtr>, std::size_t, Reference> {
  222. iterator_t() = default;
  223. template <class V, class R, class M,
  224. class = std::enable_if_t<std::is_convertible<M, MapPtr>::value>>
  225. iterator_t(const iterator_t<V, R, M>& it) noexcept : iterator_t(it.map_, it.base()) {}
  226. iterator_t(MapPtr m, std::size_t i) noexcept
  227. : iterator_t::iterator_adaptor_(i), map_(m) {}
  228. template <class V, class R, class M>
  229. bool equal(const iterator_t<V, R, M>& rhs) const noexcept {
  230. return map_ == rhs.map_ && iterator_t::base() == rhs.base();
  231. }
  232. Reference operator*() const { return (*map_)[iterator_t::base()]; }
  233. MapPtr map_ = nullptr;
  234. };
  235. using iterator = iterator_t<value_type, reference, map_impl*>;
  236. using const_iterator = iterator_t<const value_type, const_reference, const map_impl*>;
  237. using allocator_type = typename T::allocator_type;
  238. map_impl(const allocator_type& a = {}) : T(a) {}
  239. map_impl(const map_impl&) = default;
  240. map_impl& operator=(const map_impl&) = default;
  241. map_impl(map_impl&&) = default;
  242. map_impl& operator=(map_impl&&) = default;
  243. map_impl(const T& t) : T(t), size_(t.size()) {}
  244. map_impl(T&& t) : T(std::move(t)), size_(t.size()) {}
  245. template <class U, class = requires_iterable<U>>
  246. explicit map_impl(const U& u, const allocator_type& a = {}) : T(a), size_(u.size()) {
  247. using std::begin;
  248. using std::end;
  249. std::copy(begin(u), end(u), this->begin());
  250. }
  251. template <class U, class = requires_iterable<U>>
  252. map_impl& operator=(const U& u) {
  253. if (u.size() < size_)
  254. reset(u.size());
  255. else
  256. size_ = u.size();
  257. using std::begin;
  258. using std::end;
  259. std::copy(begin(u), end(u), this->begin());
  260. return *this;
  261. }
  262. void reset(std::size_t n) {
  263. T::clear();
  264. size_ = n;
  265. }
  266. reference operator[](std::size_t i) noexcept { return {this, i}; }
  267. const_reference operator[](std::size_t i) const noexcept {
  268. auto it = T::find(i);
  269. static const value_type null = value_type{};
  270. if (it == T::end()) return null;
  271. return it->second;
  272. }
  273. iterator begin() noexcept { return {this, 0}; }
  274. iterator end() noexcept { return {this, size_}; }
  275. const_iterator begin() const noexcept { return {this, 0}; }
  276. const_iterator end() const noexcept { return {this, size_}; }
  277. std::size_t size() const noexcept { return size_; }
  278. template <class Archive>
  279. void serialize(Archive& ar, unsigned /* version */) {
  280. ar& make_nvp("size", size_);
  281. ar& make_nvp("map", static_cast<T&>(*this));
  282. }
  283. std::size_t size_ = 0;
  284. };
  285. template <class T>
  286. struct ERROR_type_passed_to_storage_adaptor_not_recognized;
  287. // clang-format off
  288. template <class T>
  289. using storage_adaptor_impl =
  290. mp11::mp_cond<
  291. is_vector_like<T>, vector_impl<T>,
  292. is_array_like<T>, array_impl<T>,
  293. is_map_like<T>, map_impl<T>,
  294. std::true_type, ERROR_type_passed_to_storage_adaptor_not_recognized<T>
  295. >;
  296. // clang-format on
  297. } // namespace detail
  298. /// Turns any vector-like, array-like, and map-like container into a storage type.
  299. template <class T>
  300. class storage_adaptor : public detail::storage_adaptor_impl<T> {
  301. using impl_type = detail::storage_adaptor_impl<T>;
  302. public:
  303. // standard copy, move, assign
  304. storage_adaptor(storage_adaptor&&) = default;
  305. storage_adaptor(const storage_adaptor&) = default;
  306. storage_adaptor& operator=(storage_adaptor&&) = default;
  307. storage_adaptor& operator=(const storage_adaptor&) = default;
  308. // forwarding constructor
  309. template <class... Ts>
  310. storage_adaptor(Ts&&... ts) : impl_type(std::forward<Ts>(ts)...) {}
  311. // forwarding assign
  312. template <class U>
  313. storage_adaptor& operator=(U&& u) {
  314. impl_type::operator=(std::forward<U>(u));
  315. return *this;
  316. }
  317. template <class U, class = detail::requires_iterable<U>>
  318. bool operator==(const U& u) const {
  319. using std::begin;
  320. using std::end;
  321. return std::equal(this->begin(), this->end(), begin(u), end(u), detail::safe_equal{});
  322. }
  323. template <class Archive>
  324. void serialize(Archive& ar, unsigned /* version */) {
  325. ar& make_nvp("impl", static_cast<impl_type&>(*this));
  326. }
  327. private:
  328. friend struct unsafe_access;
  329. };
  330. } // namespace histogram
  331. } // namespace boost
  332. #endif