This document describes key design decisions and implementation choices.
Classes are only templated if required for data members, in particular the BinContentType
. We use member function templates to accept variable number of arguments (see also below). Classes are not templated to improve performance, in particular not on the axis type(s). This avoids an explosion of types and simplifies serialization. Instead axis objects are run-time choices and stored in a std::variant
. With a careful design, this still results in excellent performance.
If required, it would be possible to template performance-critical functions on the axis types. This was shown beneficial in microbenchmarks for one-dimensional histograms. However, it will not be implemented until shown useful in a real-world application. In virtually all cases, filling a (one-dimensional) histogram is negligible compared to reading, decompressing, and processing of data.
The same applies for other optimizations, such as caching the pointer to the axis object stored in the std::variant
. Such optimizations should only be implemented with a careful motivation for real-world applications.
Many member functions have two overloads: one accepting a function parameter pack and one accepting a std::tuple
or std::array
.
Functions that take arguments with different types expect a std::tuple
. An example is template <typename... A> void Fill(const std::tuple<A...> &args)
.
For user-convenience, a variadic function template forwards to the std::tuple
overload:
This will forward the arguments as references, so no copy-constructors are called (that could potentially be expensive).
In this case, the function has a std::size_t N
template argument and accepts a std::array
. An example is template <std::size_t N> const BinContentType &GetBinContent(const std::array<RBinIndex, N> &indices)
For user-convenience, a variadic function template forwards to the std::array
overload:
This will copy the arguments, which is fine in this case because RBinIndex
is small (see below).
Special arguments are passed last. Examples include
The same works for the variadic function templates that will check the type of the last argument.
For profiles, we accept the value with a template type as well to allow automatic conversion to double
, for example from int
.
The implementation uses standard C++17:
std::span
, andSmall objects are passed by value instead of by reference (RBinIndex
, RWeight
).
Complex objects, such as std::vector
, that have to be copied (for example in a constructor) are also accepted by value. This allows a single overload that can efficiently take expiring ("moved") objects.