Repository navigation
formula-cpp 0.3.0
formula-cpp 0.3.0
The third release of formula-cpp, a header-only C++23 library for traceable formulas: written
once in C++, with dimensions checked at compile time and units converted exactly, evaluated in exact
rational arithmetic, rendered as documentation, and traced step by step into an audit trail.
This release gives a shorter spelling to everything the examples repeated, and takes no spelling
away: exact decimal literals, plain numbers in series, one call that reads a result's number or
returns a trace, std::format for results, a rounding named once, and a formula bound to its result
quantity. Every example is rewritten with them.
Documentation, with the guides, a gallery and the API reference:
https://lastrada-software.github.io/formula-cpp/
Requirements
- C++23, CMake 3.23 or newer, and GCC 14 or newer.
- CI builds and tests every change on MSVC
clandclang-cl(Windows), Clang and GCC 14
(Linux, with an UndefinedBehaviorSanitizer build) and Clang on macOS.
What is new
- Literals and inputs:
27.3_ris the exactRational273/10, where27.3is the double
nearest it. A series takes plain numbers andnot_measuredbeside measured values:
measured_series<Determination>(41.3_r, 43.9_r, formula::not_measured, 45.7_r).
breakpoint(12.7_r)andband(83.7_r, 97.3_r)take exact numbers too. - Reading results:
number_of(x)is the number a result holds, or nothing, as a
std::optional<Rational>, for aMeasured, anOutcome, achecked_evaluateresult and the
other result types:number_of(checked_evaluate<Q>(...)) == 0.5_r. AMeasuredalso has throwing
twins,convert_to<R>,round_to_declaredandwithin_bounds, for callers who would only
rethrow. - Formatting: with
<formula-cpp/format.hpp>,std::format("{}", outcome)writes anOutcome,
aUnit, aDimensionand every enumeration that has adescribe().describenow covers
ConstraintOutcomeKind,RetryEnd,ValueSource,OutcomeKindandFailureSite.
symbol_of<Q>()needs no vocabulary, andrender(x, options)no placeholder one:
std::println("{} = {} ({})", formula::symbol_of<WaterCementRatio>(), *result, result->source()). - Traces:
traced(evaluation)runs any evaluation that takes a sink and returns the outcome with
its trace.explain_method<Tag>,explain_check_method,explain_check,explain_check_all,
explain_conformity,explain_curveandexplain_rejectionare the traced twins of their verbs, and
trace_of<Q>(expression, env)is the trace of an evaluation in one call, whether it succeeds or
fails:render_trace(trace_of(mixCost, inputs), { .maxSteps = 20 }). - A rounding named once:
DecimalRoundingandSignificantRoundinghold a unit, the places or
digits, and a mode, where the three were repeated at every use.
declared_rounding(unit, mode)takes the places the unit declares:
constexpr formula::DecimalRounding wholeCents = formula::declared_rounding(EuroCent, formula::RoundingMode::HalfAwayFromZero);
once, thenrounded<wholeCents>(var<Subtotal> + var<Vat>). - Bound formulas:
yields<Q>(expression)names a formula's result quantity once, where the
formula is written:constexpr auto circularArea = yields<Area>(pi * pow<2>(var<Diameter>) / 4);
thenevaluate(circularArea, environment).Qis still checked against the dimension the
expression computes, never deduced. Misusing a bound formula draws oneformula:message, not an
overload list. That covers a bound series or rejection handed to a verb that answers with one value,
and a bound formula used as an operand or compared. - The examples themselves: 18 example programs, 4705 → 4402 lines; uses of a private
rat()
helper 155 → 0;printf361 → 0 (nowstd::println);measurement().value()chains 60 → 0;
RecordingSinkin hand-built traces 23 → 0.
Upgrading from 0.2.0
A minor release may break the public API before 1.0, and this one does in a few places. Each change
is listed under Changed in the CHANGELOG. The ones most likely to need an edit:
- Argument-dependent lookup. An unqualified call with this library's types as arguments now also
finds the functions this release adds, among themdescribe,number_of,convert_to,
round_to_declared,within_bounds,traced,trace_of, theexplain_*twins,yieldsand
declared_rounding. A consumer's function template of the same name and shape, such as a
template <typename R, typename Q> Measured<R> convert_to(Measured<Q>)helper, is displaced
silently: the library's is more constrained and is chosen, so the helper no longer runs, and
where it returned an absent value on failure the library'sconvert_tothrows
ArithmeticException. A function of another shape, such as a non-template
describe(ConstraintOutcomeKind)or a template taking its argument by value, makes the call
ambiguous. A non-template taking exactly a library template's parameter types is preferred and
keeps working. Rename the helper, remove it, or call it by a qualified name such as::convert_to.
And since_ris in an inline namespace offormula,using namespace formula;brings it into
scope, where a consumer's own_ris ambiguous with it. checked_convert_toacross dimensions is now a compile error. Converting, say, a volume into
a mass, or euros into yen, used to compile and returnArithmeticError::DomainErrorat run time.
The message names the two quantities. A conversion within one dimension is unchanged.- A floating-point
breakpointorbandargument is refused.breakpoint(1.5)used to be
truncated silently tobreakpoint(1). It now fails to compile, with a message that names the exact
spelling for that call, such as12.7_r,Rational { 127, 10 }orband(12.7_r, 17.3_r). std::formatterspecialisations may collide with yours.<formula-cpp/format.hpp>now
specialises it forOutcome<Q>,Unit,Dimensionand every enumeration that has a
describe(). A program that defines its own for one of these now defines it twice, and a generic
formatter for every enumeration is ambiguous for them; remove it and use the library's.- GCC 14 is required. Older GCC is not supported.
- The
with_roundingrefusal text changed. With no citation it now opens
formula: with_rounding<...>() was given no citation, for either spelling.
Using it
Install it with CMake, then consume it with find_package:
cmake -S . -B build -DFORMULA_INSTALL=ON -DCMAKE_INSTALL_PREFIX=/your/prefix
cmake --install buildfind_package(formula-cpp 0.3 CONFIG REQUIRED)
target_link_libraries(your_target PRIVATE formula-cpp::formula-cpp)The package's compatibility is SameMinorVersion, so a request for 0.3 is not satisfied by 0.2 or
by 0.4. Because the library is header-only, copying include/ into your tree also works.
Known limits
- No stable API before 1.0. A minor release may change the public API. Every change is recorded
in the CHANGELOG. - 64-bit rational headroom (#1):
the exact route of some realistic laboratory statistics still overflows 64 bits. It reports an
overflow; it never gives a wrong number. The least-squares fits answer at a declared precision
where their exact route overflows, within the width of their wider integers. The guide
"Numeric headroom" measures where each computation stops. - Units of single-value arithmetic in a trace
(#2): a computed single value, such
as a product or a quotient, is still shown in its coherent SI value with no unit symbol.
The full list of changes is in CHANGELOG.md.