...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
boost::histogram::unlimited_storage — Memory-efficient storage for integral counters which cannot overflow.
// In header: <boost/histogram/unlimited_storage.hpp> template<typename Allocator> class unlimited_storage { public: // types typedef Allocator allocator_type; typedef double value_type; typedef unspecified mp_int; typedef reference_t< const buffer_type > const_reference; typedef iterator_t< const value_type, const_reference, const buffer_type > const_iterator; typedef iterator_t< value_type, reference, buffer_type > iterator; // member classes/structs/unions struct adder { // public member functions template<typename Buffer, typename U> void operator()(double *, Buffer &, std::size_t, const U &); template<typename T, typename Buffer, typename U> void operator()(T *, Buffer &, std::size_t, const U &); template<typename T, typename Buffer, typename U> void U_is_integral(std::false_type, T *, Buffer &, std::size_t, const U &); template<typename T, typename Buffer, typename U> void U_is_integral(std::true_type, T *, Buffer &, std::size_t, const U &); template<typename T, typename Buffer, typename U> void U_is_unsigned_integral(std::false_type, T *, Buffer &, std::size_t, const U &); template<typename Buffer, typename U> void U_is_unsigned_integral(std::true_type, mp_int *, Buffer &, std::size_t, const U &); template<typename T, typename Buffer, typename U> void U_is_unsigned_integral(std::true_type, T *, Buffer &, std::size_t, const U &); }; struct buffer_type { // construct/copy/destruct buffer_type(allocator_type = {}); buffer_type(buffer_type &&) noexcept; buffer_type(const buffer_type &); buffer_type & operator=(buffer_type &&) noexcept; buffer_type & operator=(const buffer_type &); ~buffer_type(); // public member functions template<typename F, class... Ts> decltype(auto) apply(F &&, Ts &&...) const; void destroy() noexcept; template<typename T> void make(std::size_t); template<typename T, typename U> void make(std::size_t, U); // public data members allocator_type alloc; std::size_t size; char type; void * ptr; }; struct incrementor { // public member functions template<typename T, typename Buffer> void operator()(T *, Buffer &, std::size_t); template<typename Buffer> void operator()(mp_int *, Buffer &, std::size_t); template<typename Buffer> void operator()(double *, Buffer &, std::size_t); }; template<typename Value, typename Reference, typename Buffer> class iterator_t : public boost::iterator_adaptor< iterator_t< Value, Reference, Buffer >, std::size_t, Value, std::random_access_iterator_tag, Reference, std::ptrdiff_t > { public: // construct/copy/destruct iterator_t() = default; template<typename V, typename R, typename B> iterator_t(const iterator_t< V, R, B > &); iterator_t(Buffer *, std::size_t) noexcept; // protected member functions template<typename V, typename R, typename B> bool equal(const iterator_t< V, R, B > &) const noexcept; Reference dereference() const; }; struct multiplier { // public member functions template<typename T, typename Buffer> void operator()(T *, Buffer &, const double); template<typename Buffer> void operator()(double *, Buffer &, const double); template<typename T, typename Buffer, typename U> void operator()(T *, Buffer &, std::size_t, const U &); template<typename Buffer, typename U> void operator()(double *, Buffer &, std::size_t, const U &); }; class reference : public boost::histogram::unlimited_storage< buffer_type >::reference_t { public: // construct/copy/destruct reference operator=(reference); reference operator=(const_reference); template<typename U> reference operator=(const U &); // public member functions template<typename U> reference operator+=(const U &); template<typename U> reference operator *=(const U &); template<typename U> reference operator-=(const U &); template<typename U> reference operator/=(const U &); reference operator++(); bool operator<(reference) const; bool operator>(reference) const; bool operator==(reference) const; }; template<typename Buffer> class reference_t { public: // construct/copy/destruct reference_t(Buffer *, std::size_t); reference_t(const reference_t &) = default; reference_t & operator=(const reference_t &) = delete; reference_t & operator=(reference_t &&) = delete; // public member functions bool operator<(reference_t) const; bool operator>(reference_t) const; bool operator==(reference_t) const; template<typename U> bool operator<(const U &) const; template<typename U> bool operator>(const U &) const; template<typename U> bool operator==(const U &) const; operator double() const; // protected member functions template<typename Binary, typename U> bool op(const reference_t< U > &) const; template<typename Binary, typename U> bool op(const U &) const; }; // construct/copy/destruct explicit unlimited_storage(allocator_type = {}); unlimited_storage(const unlimited_storage &) = default; unlimited_storage(unlimited_storage &&) = default; template<typename T> unlimited_storage(const storage_adaptor< T > &); template<typename T> unlimited_storage(std::size_t, const T *, allocator_type = {}); unlimited_storage & operator=(const unlimited_storage &) = default; unlimited_storage & operator=(unlimited_storage &&) = default; template<typename Iterable, typename = detail::requires_iterable<Iterable> > unlimited_storage & operator=(const Iterable &); // private static functions template<typename T> static constexpr char type_index() noexcept; // public member functions allocator_type get_allocator() const; void reset(std::size_t); std::size_t size() const noexcept; reference operator[](std::size_t) noexcept; const_reference operator[](std::size_t) const noexcept; bool operator==(const unlimited_storage &) const noexcept; template<typename T> bool operator==(const T &) const; unlimited_storage & operator *=(const double); iterator begin() noexcept; iterator end() noexcept; const_iterator begin() const noexcept; const_iterator end() const noexcept; template<typename Archive> void serialize(Archive &, unsigned); };
This storage provides a no-overflow-guarantee if it is filled with integral weights only. This storage implementation keeps a contiguous array of elemental counters, one for each cell. If an operation is requested, which would overflow a counter, the whole array is replaced with another of a wider integral type, then the operation is executed. The storage uses integers of 8, 16, 32, 64 bits, and then switches to a multiprecision integral type, similar to those in Boost.Multiprecision.
A scaling operation or adding a floating point number turns the elements into doubles, which voids the no-overflow-guarantee.
unlimited_storage
public
construct/copy/destructexplicit unlimited_storage(allocator_type a = {});
unlimited_storage(const unlimited_storage &) = default;
unlimited_storage(unlimited_storage &&) = default;
template<typename T> unlimited_storage(const storage_adaptor< T > & s);
template<typename T> unlimited_storage(std::size_t s, const T * p, allocator_type a = {});used by unit tests, not part of generic storage interface
unlimited_storage & operator=(const unlimited_storage &) = default;
unlimited_storage & operator=(unlimited_storage &&) = default;
template<typename Iterable, typename = detail::requires_iterable<Iterable> > unlimited_storage & operator=(const Iterable & s);
unlimited_storage
public member functionsallocator_type get_allocator() const;
void reset(std::size_t s);
std::size_t size() const noexcept;
reference operator[](std::size_t i) noexcept;
const_reference operator[](std::size_t i) const noexcept;
bool operator==(const unlimited_storage & o) const noexcept;
template<typename T> bool operator==(const T & o) const;
unlimited_storage & operator *=(const double x);
iterator begin() noexcept;
iterator end() noexcept;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
template<typename Archive> void serialize(Archive &, unsigned);