Skip to content

formula-cpp 0.5.0

Latest

Choose a tag to compare

@christianparpart christianparpart released this 07 Oct 05:10
c18dbd3

formula-cpp 0.5.0

The fifth release of formula-cpp, a header-only C++23 library for traceable formulas. You write a formula once
in C++. Dimensions are checked at compile time, units are converted exactly, and evaluation runs in exact rational
arithmetic. The same formula renders as documentation and traces step by step into an audit trail.

This release makes the library easier to learn and lets it take units, limits and numbers that arrive at run time:

  • A tutorial. Nine chapters build one compressive-strength test step by step, and six more cover the rest of the
    library. Every snippet and every output on its pages comes from a program the test suite builds and runs.
  • Run-time units, limits and numbers. Unit symbols and ASCII keys from run-time text, one-sided bounds, limits
    checked at run time, a quantity's own decimal places, and decimal text parsed into its exact value.

Documentation, with the tutorial, 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 cl and clang-cl (Windows);
    • Clang and GCC 14 (Linux), including an UndefinedBehaviorSanitizer build;
    • Clang on macOS.

Installation

CPMAddPackage("gh:LASTRADA-Software/formula-cpp@0.5.0")
target_link_libraries(your_target PRIVATE formula-cpp::formula-cpp)

find_package(formula-cpp CONFIG REQUIRED) from an install tree, or copying include/, work as well.

What is new

  • Documentation.
    • A tutorial in two tracks: nine chapters that grow one compressive-strength test, from a first formula
      through units, exact numbers, missing and entered values, rounding, constraints, documentation, tracing and
      worksheets, and six independent chapters on lookup tables, methods and overlays, series, statistics, records,
      and opaque operations with bounded retry.
    • The README is a short landing page: a minimal example with its output, and installation with CPM,
      find_package or the headers alone.
    • No drift between pages and code. Tutorial and README code is included from compiled programs, and each
      program's whole output is compared with a checked-in file. The documentation describes the library as it is;
      the changelog alone records history, and a test enforces it.
    • examples/cycling_speed.cpp, a cyclist's steady-state speed from power, calculated on a worksheet.
  • Units from run-time text.
    • checked_symbol() builds a unit Symbol from run-time text byte for byte, returning
      std::expected<Symbol, SymbolError> (TooLong, EmbeddedNull). It never truncates and never aborts.
    • A stable ASCII key per unit. Unit gains asciiText, a key for serialising the unit that stays the same
      when its display symbol is restyled. view_ascii, has_ascii_key and checked_ascii_symbol() read and build
      it. The built-in units with non-ASCII symbols declare keys: permille, um, degC, degF.
  • Limits.
    • One-sided bounds. at_least() and at_most() declare a minimum or a maximum alone, beside bounds().
    • Limits held at run time. checked_within(value, lowEnd, highEnd) checks a Rational or a Measured
      against optional ends from a specification row or a catalogue entry, with no Unit built to carry them.
  • Numbers.
    • A quantity's own decimal places. Quantity takes an optional fifth parameter, DecimalPlaces, so two
      quantities in one unit can read to different precision. same_unit says whether two units are the same unit,
      whatever their declared decimals and bounds.
    • parse_decimal_text reads decimal text from a CSV import, a form field or a configuration value into its
      exact value and the places it was typed to: "2.400" is 12/5 at 3 places. _r literals take a 128-bit
      mantissa and scale from 10^-38 to 10^38.
    • checked_transform and checked_combine apply a callback that can fail to measured values, and stay
      noexcept when the callback is.
  • A bound formula is an operand. A formula bound with yields<Q>(...) can be used inside another formula, where
    it stands for the formula it holds: yields<Strength>(var<Load> / loadedArea). That covers the arithmetic and
    comparison operators and every function that builds a formula. .expression still compiles and means the same.
  • Coverage reporting. A workflow measures the test suite's coverage of include/ for Codecov.

Upgrading from 0.4.0

A minor release may break the public API before 1.0, and this one does. The CHANGELOG lists every change under
Changed; these four are marked Breaking:

  • symbol() is consteval. Its spelling must be a constant expression. Build a symbol from run-time text with
    checked_symbol().
  • SymbolCapacity is 32 bytes, up from 16. Symbol, Dimension, Unit and a trace's Step are larger, so a
    trace takes more memory.
  • A unit whose symbol is not ASCII must declare an ASCII key, .asciiText = formula::symbol("ug/L") for µg/L,
    or its use as a quantity's, constant's, rounding's or table's unit no longer compiles:
    formula: a unit whose symbol is not ASCII must declare an ASCII key.
    • Unit has a new member after symbolText, so a Unit built with positional initialisers must move to
      designated initialisers.
    • Unit equality compares asciiText too. Use same_unit to ask whether two units are the same unit.
  • Bounds::present is replaced by lowPresent and highPresent. A Bounds built with positional initialisers
    in the old order no longer compiles; { true } alone now declares a minimum of 0 and no maximum. Use bounds(),
    at_least(), at_most() or designated initialisers. Code that read present reads lowPresent || highPresent.

Full changelog: CHANGELOG.md