Boost 1.70 adds explicit failure results and configurable histograms
Outcome and Histogram put different policies into types, with consequences for error propagation and the treatment of observations outside configured ranges.
A function that reports failure through its return value needs a representation that keeps failure separate from a legitimate result. A histogram needs a policy for observations outside its normal bins. Boost 1.70, released April 12, adds libraries addressing both questions. Niall Douglas's Outcome and Hans Dembinski's Histogram make those decisions part of reusable C++ interfaces.
Their subjects are unrelated, but each library demonstrates a useful role for types. A return type can state which outcomes a caller must distinguish. An axis type can state how an observation maps to storage. The resulting implementation still has costs and constraints that a short call site can conceal.
I regard templates as useful when their abstraction disappears from the runtime work. The corresponding cost in C++ is often paid when compiling or interpreting diagnostics. These additions are interesting where their types communicate policy clearly enough to justify that complexity.
Outcome represents success or failure
The release describes Outcome as tools for contexts where directly using exception handling is unsuitable. Its versioned result tutorial explains result as holding either a success value or an error value. The success type is a template parameter, while the error representation and no-value policy are configurable.
In Boost.Outcome, the default error type is Boost.System's error_code. The documentation distinguishes that from standalone Outcome, whose default is std::error_code. A reader copying an example has to know which distribution supplies the type, even when the conceptual success-or-failure model is the same.
This arrangement makes failure part of a function's declared result. A caller cannot interpret the returned object as only the successful value without choosing how to observe it. That is useful for functions whose ordinary operation includes a failure result that needs to travel through several layers.
The no-value policy is also part of the design. The representation of an error and the behavior when code tries to obtain an absent success value are separate concerns. Selecting a result type does not by itself explain every accessor's behavior. The policy supplies that part of the contract.
The tutorial says triviality follows the corresponding properties of the success and error types. A trivially copyable pair of alternatives can yield a trivially copyable result. That is a more specific claim than assuming every instance has one fixed implementation cost. Choosing a heavier success or error type changes the operations the result must support.
Explicit tags resolve ambiguous construction
The tutorial permits convenient construction when the success and error types are sufficiently distinct. When conversions could make the intended alternative ambiguous, it disables those implicit conversions. The library then supplies tagged construction and helpers for success and failure.
That is an interface decision with practical value. A result object has to identify which interpretation its stored data represents. Convenience cannot safely depend on guessing whether a source expression meant a successful value or an error. An explicit helper records that choice where the object is constructed.
This also separates error representation from error handling. Returning an error object reports the failure. The calling layer still determines whether to propagate it or convert it into an application response. A result wrapper cannot choose the domain policy merely by making failure a type alternative.
I prefer to judge this design by how clearly it preserves that distinction. C++ already offers RAII for releasing resources when scopes end. A failure result adds an explicit route for communicating the operation's outcome, while the ownership of resources used during that operation remains a separate responsibility.
Histogram separates axes from storage
The Histogram getting-started guide describes axis objects that convert input values into indices. A factory combines those axes into a histogram. The library can specialize the resulting type when the axes are known at compile time, and also supports runtime-configured collections of axes.
A small example makes the bin-boundary policy visible. The regular axis below has two normal bins over the interval starting at zero and ending at one:
#include <boost/histogram.hpp>
void count_samples()
{
namespace bh = boost::histogram;
auto counts = bh::make_histogram(
bh::axis::regular<>(2, 0.0, 1.0));
counts(0.25);
counts(0.75);
counts(1.0);
}
The regular bins are half-open. The value at the upper endpoint goes into the default overflow bin, rather than the last normal bin. An underflow bin is also present by default. This preserves observations that fall outside the configured normal interval.
That is a semantic policy, not merely extra storage. Dropping out-of-range observations can change what a later projection or reduction represents. The Histogram overview explains why retaining those counts matters when summing over dimensions.
Runtime configuration has a distinct cost
Compile-time axis information lets the library generate a histogram type specialized for those axes. Runtime configuration instead needs a representation that can hold the chosen sequence. The documentation demonstrates a vector of axis variants for a selection among different axis types.
These approaches answer different requirements. A fixed analysis knows its axis types when the code is compiled. A general-purpose tool may learn them from user input. Requiring a compile-time configuration everywhere would push that variability into application code.
The overview also explains a storage choice. A separate variant in every cell would repeat a type index and reserve enough space for the largest alternative in each cell. Adaptive storage can instead keep one type selection for the whole array and allocate a homogeneous representation. That design makes the granularity of type erasure relevant to memory layout.
The release also updates Asio with custom I/O executors and async_compose for user-defined asynchronous operations. Those changes concern another kind of policy, how operations are initiated and completed. Boost 1.70 therefore offers several examples of library interfaces growing around work that applications previously had to express themselves.
For the new Histogram library, axis definitions and overflow treatment determine what the stored counts mean. For Outcome, the success and error types determine how a result carries its alternatives. In both cases, understanding those policies is necessary before a compact use site can be read accurately.