123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 |
- // Copyright 2018-2019 Hans Dembinski
- //
- // Distributed under the Boost Software License, Version 1.0.
- // (See accompanying file LICENSE_1_0.txt
- // or copy at http://www.boost.org/LICENSE_1_0.txt)
- #ifndef BOOST_HISTOGRAM_STORAGE_ADAPTOR_HPP
- #define BOOST_HISTOGRAM_STORAGE_ADAPTOR_HPP
- #include <algorithm>
- #include <boost/core/nvp.hpp>
- #include <boost/histogram/accumulators/is_thread_safe.hpp>
- #include <boost/histogram/detail/array_wrapper.hpp>
- #include <boost/histogram/detail/detect.hpp>
- #include <boost/histogram/detail/iterator_adaptor.hpp>
- #include <boost/histogram/detail/safe_comparison.hpp>
- #include <boost/histogram/fwd.hpp>
- #include <boost/mp11/utility.hpp>
- #include <boost/throw_exception.hpp>
- #include <stdexcept>
- #include <type_traits>
- namespace boost {
- namespace histogram {
- namespace detail {
- template <class T>
- struct vector_impl : T {
- using allocator_type = typename T::allocator_type;
- static constexpr bool has_threading_support =
- accumulators::is_thread_safe<typename T::value_type>::value;
- vector_impl(const allocator_type& a = {}) : T(a) {}
- vector_impl(const vector_impl&) = default;
- vector_impl& operator=(const vector_impl&) = default;
- vector_impl(vector_impl&&) = default;
- vector_impl& operator=(vector_impl&&) = default;
- explicit vector_impl(T&& t) : T(std::move(t)) {}
- explicit vector_impl(const T& t) : T(t) {}
- template <class U, class = requires_iterable<U>>
- explicit vector_impl(const U& u, const allocator_type& a = {})
- : T(std::begin(u), std::end(u), a) {}
- template <class U, class = requires_iterable<U>>
- vector_impl& operator=(const U& u) {
- T::resize(u.size());
- auto it = T::begin();
- for (auto&& x : u) *it++ = x;
- return *this;
- }
- void reset(std::size_t n) {
- using value_type = typename T::value_type;
- const auto old_size = T::size();
- T::resize(n, value_type());
- std::fill_n(T::begin(), (std::min)(n, old_size), value_type());
- }
- template <class Archive>
- void serialize(Archive& ar, unsigned /* version */) {
- ar& make_nvp("vector", static_cast<T&>(*this));
- }
- };
- template <class T>
- struct array_impl : T {
- static constexpr bool has_threading_support =
- accumulators::is_thread_safe<typename T::value_type>::value;
- array_impl() = default;
- array_impl(const array_impl& t) : T(t), size_(t.size_) {}
- array_impl& operator=(const array_impl& t) {
- T::operator=(t);
- size_ = t.size_;
- return *this;
- }
- explicit array_impl(T&& t) : T(std::move(t)) {}
- explicit array_impl(const T& t) : T(t) {}
- template <class U, class = requires_iterable<U>>
- explicit array_impl(const U& u) : size_(u.size()) {
- using std::begin;
- using std::end;
- std::copy(begin(u), end(u), this->begin());
- }
- template <class U, class = requires_iterable<U>>
- array_impl& operator=(const U& u) {
- if (u.size() > T::max_size()) // for std::arra
- BOOST_THROW_EXCEPTION(std::length_error("argument size exceeds maximum capacity"));
- size_ = u.size();
- using std::begin;
- using std::end;
- std::copy(begin(u), end(u), T::begin());
- return *this;
- }
- void reset(std::size_t n) {
- using value_type = typename T::value_type;
- if (n > T::max_size()) // for std::array
- BOOST_THROW_EXCEPTION(std::length_error("argument size exceeds maximum capacity"));
- std::fill_n(T::begin(), n, value_type());
- size_ = n;
- }
- typename T::iterator end() noexcept { return T::begin() + size_; }
- typename T::const_iterator end() const noexcept { return T::begin() + size_; }
- std::size_t size() const noexcept { return size_; }
- template <class Archive>
- void serialize(Archive& ar, unsigned /* version */) {
- ar& make_nvp("size", size_);
- auto w = detail::make_array_wrapper(T::data(), size_);
- ar& make_nvp("array", w);
- }
- std::size_t size_ = 0;
- };
- template <class T>
- struct map_impl : T {
- static_assert(std::is_same<typename T::key_type, std::size_t>::value,
- "requires std::size_t as key_type");
- using value_type = typename T::mapped_type;
- using const_reference = const value_type&;
- static constexpr bool has_threading_support = false;
- static_assert(
- !accumulators::is_thread_safe<value_type>::value,
- "std::map and std::unordered_map do not support thread-safe element access. "
- "If you have a map with thread-safe element access, please file an issue and"
- "support will be added.");
- struct reference {
- reference(map_impl* m, std::size_t i) noexcept : map(m), idx(i) {}
- reference(const reference&) noexcept = default;
- reference& operator=(const reference& o) {
- if (this != &o) operator=(static_cast<const_reference>(o));
- return *this;
- }
- operator const_reference() const noexcept {
- return static_cast<const map_impl*>(map)->operator[](idx);
- }
- reference& operator=(const_reference u) {
- auto it = map->find(idx);
- if (u == value_type{}) {
- if (it != static_cast<T*>(map)->end()) { map->erase(it); }
- } else {
- if (it != static_cast<T*>(map)->end()) {
- it->second = u;
- } else {
- map->emplace(idx, u);
- }
- }
- return *this;
- }
- template <class U, class V = value_type,
- class = std::enable_if_t<has_operator_radd<V, U>::value>>
- reference& operator+=(const U& u) {
- auto it = map->find(idx);
- if (it != static_cast<T*>(map)->end()) {
- it->second += u;
- } else {
- map->emplace(idx, u);
- }
- return *this;
- }
- template <class U, class V = value_type,
- class = std::enable_if_t<has_operator_rsub<V, U>::value>>
- reference& operator-=(const U& u) {
- auto it = map->find(idx);
- if (it != static_cast<T*>(map)->end()) {
- it->second -= u;
- } else {
- map->emplace(idx, -u);
- }
- return *this;
- }
- template <class U, class V = value_type,
- class = std::enable_if_t<has_operator_rmul<V, U>::value>>
- reference& operator*=(const U& u) {
- auto it = map->find(idx);
- if (it != static_cast<T*>(map)->end()) it->second *= u;
- return *this;
- }
- template <class U, class V = value_type,
- class = std::enable_if_t<has_operator_rdiv<V, U>::value>>
- reference& operator/=(const U& u) {
- auto it = map->find(idx);
- if (it != static_cast<T*>(map)->end()) {
- it->second /= u;
- } else if (!(value_type{} / u == value_type{})) {
- map->emplace(idx, value_type{} / u);
- }
- return *this;
- }
- template <class V = value_type,
- class = std::enable_if_t<has_operator_preincrement<V>::value>>
- reference operator++() {
- auto it = map->find(idx);
- if (it != static_cast<T*>(map)->end()) {
- ++it->second;
- } else {
- value_type tmp{};
- ++tmp;
- map->emplace(idx, tmp);
- }
- return *this;
- }
- template <class V = value_type,
- class = std::enable_if_t<has_operator_preincrement<V>::value>>
- value_type operator++(int) {
- const value_type tmp = *this;
- operator++();
- return tmp;
- }
- template <class U, class = std::enable_if_t<has_operator_equal<value_type, U>::value>>
- bool operator==(const U& rhs) const {
- return operator const_reference() == rhs;
- }
- template <class U, class = std::enable_if_t<has_operator_equal<value_type, U>::value>>
- bool operator!=(const U& rhs) const {
- return !operator==(rhs);
- }
- template <class CharT, class Traits>
- friend std::basic_ostream<CharT, Traits>& operator<<(
- std::basic_ostream<CharT, Traits>& os, reference x) {
- os << static_cast<const_reference>(x);
- return os;
- }
- template <class... Ts>
- auto operator()(const Ts&... args) -> decltype(std::declval<value_type>()(args...)) {
- return (*map)[idx](args...);
- }
- map_impl* map;
- std::size_t idx;
- };
- template <class Value, class Reference, class MapPtr>
- struct iterator_t
- : iterator_adaptor<iterator_t<Value, Reference, MapPtr>, std::size_t, Reference> {
- iterator_t() = default;
- template <class V, class R, class M,
- class = std::enable_if_t<std::is_convertible<M, MapPtr>::value>>
- iterator_t(const iterator_t<V, R, M>& it) noexcept : iterator_t(it.map_, it.base()) {}
- iterator_t(MapPtr m, std::size_t i) noexcept
- : iterator_t::iterator_adaptor_(i), map_(m) {}
- template <class V, class R, class M>
- bool equal(const iterator_t<V, R, M>& rhs) const noexcept {
- return map_ == rhs.map_ && iterator_t::base() == rhs.base();
- }
- Reference operator*() const { return (*map_)[iterator_t::base()]; }
- MapPtr map_ = nullptr;
- };
- using iterator = iterator_t<value_type, reference, map_impl*>;
- using const_iterator = iterator_t<const value_type, const_reference, const map_impl*>;
- using allocator_type = typename T::allocator_type;
- map_impl(const allocator_type& a = {}) : T(a) {}
- map_impl(const map_impl&) = default;
- map_impl& operator=(const map_impl&) = default;
- map_impl(map_impl&&) = default;
- map_impl& operator=(map_impl&&) = default;
- map_impl(const T& t) : T(t), size_(t.size()) {}
- map_impl(T&& t) : T(std::move(t)), size_(t.size()) {}
- template <class U, class = requires_iterable<U>>
- explicit map_impl(const U& u, const allocator_type& a = {}) : T(a), size_(u.size()) {
- using std::begin;
- using std::end;
- std::copy(begin(u), end(u), this->begin());
- }
- template <class U, class = requires_iterable<U>>
- map_impl& operator=(const U& u) {
- if (u.size() < size_)
- reset(u.size());
- else
- size_ = u.size();
- using std::begin;
- using std::end;
- std::copy(begin(u), end(u), this->begin());
- return *this;
- }
- void reset(std::size_t n) {
- T::clear();
- size_ = n;
- }
- reference operator[](std::size_t i) noexcept { return {this, i}; }
- const_reference operator[](std::size_t i) const noexcept {
- auto it = T::find(i);
- static const value_type null = value_type{};
- if (it == T::end()) return null;
- return it->second;
- }
- iterator begin() noexcept { return {this, 0}; }
- iterator end() noexcept { return {this, size_}; }
- const_iterator begin() const noexcept { return {this, 0}; }
- const_iterator end() const noexcept { return {this, size_}; }
- std::size_t size() const noexcept { return size_; }
- template <class Archive>
- void serialize(Archive& ar, unsigned /* version */) {
- ar& make_nvp("size", size_);
- ar& make_nvp("map", static_cast<T&>(*this));
- }
- std::size_t size_ = 0;
- };
- template <class T>
- struct ERROR_type_passed_to_storage_adaptor_not_recognized;
- // clang-format off
- template <class T>
- using storage_adaptor_impl =
- mp11::mp_cond<
- is_vector_like<T>, vector_impl<T>,
- is_array_like<T>, array_impl<T>,
- is_map_like<T>, map_impl<T>,
- std::true_type, ERROR_type_passed_to_storage_adaptor_not_recognized<T>
- >;
- // clang-format on
- } // namespace detail
- /// Turns any vector-like, array-like, and map-like container into a storage type.
- template <class T>
- class storage_adaptor : public detail::storage_adaptor_impl<T> {
- using impl_type = detail::storage_adaptor_impl<T>;
- public:
- // standard copy, move, assign
- storage_adaptor(storage_adaptor&&) = default;
- storage_adaptor(const storage_adaptor&) = default;
- storage_adaptor& operator=(storage_adaptor&&) = default;
- storage_adaptor& operator=(const storage_adaptor&) = default;
- // forwarding constructor
- template <class... Ts>
- storage_adaptor(Ts&&... ts) : impl_type(std::forward<Ts>(ts)...) {}
- // forwarding assign
- template <class U>
- storage_adaptor& operator=(U&& u) {
- impl_type::operator=(std::forward<U>(u));
- return *this;
- }
- template <class U, class = detail::requires_iterable<U>>
- bool operator==(const U& u) const {
- using std::begin;
- using std::end;
- return std::equal(this->begin(), this->end(), begin(u), end(u), detail::safe_equal{});
- }
- template <class Archive>
- void serialize(Archive& ar, unsigned /* version */) {
- ar& make_nvp("impl", static_cast<impl_type&>(*this));
- }
- private:
- friend struct unsafe_access;
- };
- } // namespace histogram
- } // namespace boost
- #endif
|