Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
Design and Implementation

This document describes key design decisions and implementation choices.

Templating

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.

Performance Optimizations

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.

Functions with Variable Number of Arguments

Many member functions have two overloads: one accepting a function parameter pack and one accepting a std::tuple or std::array.

Arguments with Different Types

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:

template <typename... A> void Fill(const A &...args) {
Fill(std::forward_as_tuple(args...));
}

This will forward the arguments as references, so no copy-constructors are called (that could potentially be expensive).

Arguments with Same Type

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:

template <typename... A> const BinContentType &GetBinContent(const A &...args) {
std::array<RBinIndex, sizeof...(A)> indices{args...};
return GetBinContent(indices);
}

This will copy the arguments, which is fine in this case because RBinIndex is small (see below).

Special Arguments

Special arguments are passed last. For example

template <typename... A> void Fill(const std::tuple<A...> &args, RWeight w);
template <std::size_t N, typename V> void SetBinContent(const std::array<RBinIndex, N> &indices, const V &value);
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value

Note that we accept mandatory arguments with a template type as well to allow automatic conversion.

Variadic function templates receive all arguments in a single function parameter pack. For optional arguments, the function will check the type of the last argument to determine if it was passed.

Miscellaneous

The implementation uses standard C++17:

  • No backports from later C++ versions, such as std::span, and
  • No ROOT types, to make sure the histogram package can be compiled standalone.

Small 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.