storage_adaptor.hpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. // Copyright 2018 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/assert.hpp>
  10. #include <boost/histogram/detail/cat.hpp>
  11. #include <boost/histogram/detail/meta.hpp>
  12. #include <boost/histogram/fwd.hpp>
  13. #include <boost/iterator/iterator_adaptor.hpp>
  14. #include <boost/mp11/utility.hpp>
  15. #include <boost/throw_exception.hpp>
  16. #include <stdexcept>
  17. #include <type_traits>
  18. namespace boost {
  19. namespace histogram {
  20. namespace detail {
  21. template <class T>
  22. struct vector_impl : T {
  23. vector_impl() = default;
  24. explicit vector_impl(const T& t) : T(t) {}
  25. explicit vector_impl(typename T::allocator_type a) : T(a) {}
  26. template <class U, class = requires_iterable<U>>
  27. explicit vector_impl(const U& u) {
  28. T::reserve(u.size());
  29. for (auto&& x : u) T::emplace_back(x);
  30. }
  31. template <class U, class = requires_iterable<U>>
  32. vector_impl& operator=(const U& u) {
  33. T::resize(u.size());
  34. auto it = T::begin();
  35. for (auto&& x : u) *it++ = x;
  36. return *this;
  37. }
  38. void reset(std::size_t n) {
  39. using value_type = typename T::value_type;
  40. const auto old_size = T::size();
  41. T::resize(n, value_type());
  42. std::fill_n(T::begin(), std::min(n, old_size), value_type());
  43. }
  44. };
  45. template <class T>
  46. struct array_impl : T {
  47. array_impl() = default;
  48. explicit array_impl(const T& t) : T(t) {}
  49. template <class U, class = requires_iterable<U>>
  50. explicit array_impl(const U& u) : size_(u.size()) {
  51. std::size_t i = 0;
  52. for (auto&& x : u) T::operator[](i++) = x;
  53. }
  54. template <class U, class = requires_iterable<U>>
  55. array_impl& operator=(const U& u) {
  56. size_ = u.size();
  57. if (size_ > T::max_size()) // for std::array
  58. BOOST_THROW_EXCEPTION(std::length_error(
  59. detail::cat("size ", size_, " exceeds maximum capacity ", T::max_size())));
  60. auto it = T::begin();
  61. for (auto&& x : u) *it++ = x;
  62. return *this;
  63. }
  64. void reset(std::size_t n) {
  65. using value_type = typename T::value_type;
  66. if (n > T::max_size()) // for std::array
  67. BOOST_THROW_EXCEPTION(std::length_error(
  68. detail::cat("size ", n, " exceeds maximum capacity ", T::max_size())));
  69. std::fill_n(T::begin(), n, value_type());
  70. size_ = n;
  71. }
  72. typename T::iterator end() noexcept { return T::begin() + size_; }
  73. typename T::const_iterator end() const noexcept { return T::begin() + size_; }
  74. std::size_t size() const noexcept { return size_; }
  75. std::size_t size_ = 0;
  76. };
  77. template <class T>
  78. struct map_impl : T {
  79. static_assert(std::is_same<typename T::key_type, std::size_t>::value,
  80. "requires std::size_t as key_type");
  81. using value_type = typename T::mapped_type;
  82. using const_reference = const value_type&;
  83. struct reference {
  84. reference(map_impl* m, std::size_t i) noexcept : map(m), idx(i) {}
  85. reference(const reference&) noexcept = default;
  86. reference operator=(reference o) {
  87. if (this != &o) operator=(static_cast<const_reference>(o));
  88. return *this;
  89. }
  90. operator const_reference() const noexcept {
  91. return static_cast<const map_impl*>(map)->operator[](idx);
  92. }
  93. template <class U, class = requires_convertible<U, value_type>>
  94. reference& operator=(const U& u) {
  95. auto it = map->find(idx);
  96. if (u == value_type()) {
  97. if (it != static_cast<T*>(map)->end()) map->erase(it);
  98. } else {
  99. if (it != static_cast<T*>(map)->end())
  100. it->second = u;
  101. else {
  102. map->emplace(idx, u);
  103. }
  104. }
  105. return *this;
  106. }
  107. template <class U, class V = value_type,
  108. class = std::enable_if_t<has_operator_radd<V, U>::value>>
  109. reference operator+=(const U& u) {
  110. auto it = map->find(idx);
  111. if (it != static_cast<T*>(map)->end())
  112. it->second += u;
  113. else
  114. map->emplace(idx, u);
  115. return *this;
  116. }
  117. template <class U, class V = value_type,
  118. class = std::enable_if_t<has_operator_rsub<V, U>::value>>
  119. reference operator-=(const U& u) {
  120. auto it = map->find(idx);
  121. if (it != static_cast<T*>(map)->end())
  122. it->second -= u;
  123. else
  124. map->emplace(idx, -u);
  125. return *this;
  126. }
  127. template <class U, class V = value_type,
  128. class = std::enable_if_t<has_operator_rmul<V, U>::value>>
  129. reference operator*=(const U& u) {
  130. auto it = map->find(idx);
  131. if (it != static_cast<T*>(map)->end()) it->second *= u;
  132. return *this;
  133. }
  134. template <class U, class V = value_type,
  135. class = std::enable_if_t<has_operator_rdiv<V, U>::value>>
  136. reference operator/=(const U& u) {
  137. auto it = map->find(idx);
  138. if (it != static_cast<T*>(map)->end())
  139. it->second /= u;
  140. else if (!(value_type{} / u == value_type{}))
  141. map->emplace(idx, value_type{} / u);
  142. return *this;
  143. }
  144. template <class V = value_type,
  145. class = std::enable_if_t<has_operator_preincrement<V>::value>>
  146. reference operator++() {
  147. auto it = map->find(idx);
  148. if (it != static_cast<T*>(map)->end())
  149. ++it->second;
  150. else
  151. map->emplace(idx, 1);
  152. return *this;
  153. }
  154. template <class V = value_type,
  155. class = std::enable_if_t<has_operator_preincrement<V>::value>>
  156. value_type operator++(int) {
  157. const value_type tmp = operator const_reference();
  158. operator++();
  159. return tmp;
  160. }
  161. template <class... Ts>
  162. decltype(auto) operator()(Ts&&... args) {
  163. return map->operator[](idx)(std::forward<Ts>(args)...);
  164. }
  165. map_impl* map;
  166. std::size_t idx;
  167. };
  168. template <class Value, class Reference, class MapPtr>
  169. struct iterator_t
  170. : boost::iterator_adaptor<iterator_t<Value, Reference, MapPtr>, std::size_t, Value,
  171. std::random_access_iterator_tag, Reference,
  172. std::ptrdiff_t> {
  173. iterator_t() = default;
  174. template <class V, class R, class M, class = requires_convertible<M, MapPtr>>
  175. iterator_t(const iterator_t<V, R, M>& it) noexcept : iterator_t(it.map_, it.base()) {}
  176. iterator_t(MapPtr m, std::size_t i) noexcept
  177. : iterator_t::iterator_adaptor_(i), map_(m) {}
  178. template <class V, class R, class M>
  179. bool equal(const iterator_t<V, R, M>& rhs) const noexcept {
  180. return map_ == rhs.map_ && iterator_t::base() == rhs.base();
  181. }
  182. decltype(auto) dereference() const { return (*map_)[iterator_t::base()]; }
  183. MapPtr map_ = nullptr;
  184. };
  185. using iterator = iterator_t<value_type, reference, map_impl*>;
  186. using const_iterator = iterator_t<const value_type, const_reference, const map_impl*>;
  187. map_impl() = default;
  188. explicit map_impl(const T& t) : T(t) {}
  189. explicit map_impl(typename T::allocator_type a) : T(a) {}
  190. template <class U, class = requires_iterable<U>>
  191. explicit map_impl(const U& u) : size_(u.size()) {
  192. using std::begin;
  193. using std::end;
  194. std::copy(begin(u), end(u), this->begin());
  195. }
  196. template <class U, class = requires_iterable<U>>
  197. map_impl& operator=(const U& u) {
  198. if (u.size() < size_)
  199. reset(u.size());
  200. else
  201. size_ = u.size();
  202. using std::begin;
  203. using std::end;
  204. std::copy(begin(u), end(u), this->begin());
  205. return *this;
  206. }
  207. void reset(std::size_t n) {
  208. T::clear();
  209. size_ = n;
  210. }
  211. reference operator[](std::size_t i) noexcept { return {this, i}; }
  212. const_reference operator[](std::size_t i) const noexcept {
  213. auto it = T::find(i);
  214. static const value_type null = value_type{};
  215. if (it == T::end()) return null;
  216. return it->second;
  217. }
  218. iterator begin() noexcept { return {this, 0}; }
  219. iterator end() noexcept { return {this, size_}; }
  220. const_iterator begin() const noexcept { return {this, 0}; }
  221. const_iterator end() const noexcept { return {this, size_}; }
  222. std::size_t size() const noexcept { return size_; }
  223. std::size_t size_ = 0;
  224. };
  225. template <typename T>
  226. struct ERROR_type_passed_to_storage_adaptor_not_recognized;
  227. template <typename T>
  228. using storage_adaptor_impl = mp11::mp_if<
  229. is_vector_like<T>, vector_impl<T>,
  230. mp11::mp_if<is_array_like<T>, array_impl<T>,
  231. mp11::mp_if<is_map_like<T>, map_impl<T>,
  232. ERROR_type_passed_to_storage_adaptor_not_recognized<T>>>>;
  233. } // namespace detail
  234. /// Turns any vector-like array-like, and map-like container into a storage type.
  235. template <typename T>
  236. class storage_adaptor : public detail::storage_adaptor_impl<T> {
  237. using base_type = detail::storage_adaptor_impl<T>;
  238. public:
  239. using base_type::base_type;
  240. using base_type::operator=;
  241. template <class U, class = detail::requires_iterable<U>>
  242. bool operator==(const U& u) const {
  243. using std::begin;
  244. using std::end;
  245. return std::equal(this->begin(), this->end(), begin(u), end(u));
  246. }
  247. };
  248. } // namespace histogram
  249. } // namespace boost
  250. #endif