|
| struct | morph::units::UnitMeta |
| | Static description of one unit, supplied by UnitTraits. More...
|
| struct | morph::units::UnitRelation< E > |
| | One exact within-dimension conversion ratio between two peer units. More...
|
| struct | morph::units::UnitAlternative< E > |
| | A derived per-unit view of one convertible display/entry unit. More...
|
| struct | morph::units::QuantityBounds |
| | Optional physical/wire bounds for one unit: the inclusive range a decoded value must fall within to be accepted. Returned by the optional UnitTraits<E>::bounds(E) customisation point (detected by HasUnitBounds) — e.g. a percentage unit bounding itself to [0, 100], or a mass unit rejecting a negative reading no scale can physically produce. Absent by default: a unit with no declared bounds accepts any value its precision allows, exactly as before this feature existed. More...
|
| struct | morph::units::Quantity< U, DeclaredDecimals > |
| | A unit-tagged, optionally-empty exact value with a declared precision in its type and (optionally) a recorded derivation. More...
|
| struct | morph::units::NamedQuantity< Name, U > |
| | A Quantity that names itself on construction and slices losslessly back to a plain Quantity. The name lives in the shared history. More...
|
| struct | morph::model::PayloadShapeTag< morph::units::Quantity< U, Dec > > |
| | Stable shape tag for Quantity<U, Dec>, carrying the unit's ascii id and the declared decimals. More...
|
|
template<auto From, auto To>
requires RatioConvertible<From, To> |
| void | morph::units::convert (const Quantity< From > &in, Quantity< To > &out) |
| | Auto-generated within-dimension conversion (ratio + chaining).
|
| template<auto U, std::uint32_t DecA, std::uint32_t DecB> |
| constexpr bool | morph::units::operator== (const Quantity< U, DecA > &lhs, const Quantity< U, DecB > &rhs) noexcept |
| | Equality across same-unit quantities of any declared precision. Ignores the runtime precision tag; empty==empty is true.
|
| template<auto U, std::uint32_t DecA, std::uint32_t DecB> |
| constexpr std::strong_ordering | morph::units::operator<=> (const Quantity< U, DecA > &lhs, const Quantity< U, DecB > &rhs) |
| | Ordering across same-unit quantities. Precondition: both operands are engaged; comparing an empty operand throws std::logic_error (a defined diagnostic — ordering an absent value is a programming error).
|
| template<auto U, std::uint32_t DecA, std::uint32_t DecB> |
| Quantity< U > | morph::units::operator+ (const Quantity< U, DecA > &lhs, const Quantity< U, DecB > &rhs) |
| | Same-unit sum across any declared precisions.
|
| template<auto U, std::uint32_t DecA, std::uint32_t DecB> |
| Quantity< U > | morph::units::operator- (const Quantity< U, DecA > &lhs, const Quantity< U, DecB > &rhs) |
| | Same-unit difference across any declared precisions.
|
template<auto A, std::uint32_t DecA, auto B, std::uint32_t DecB>
requires (detail::SameEnumDistinct<A, B> && requires(Quantity<B, DecB> value) { static_cast<Quantity<A>>(value); }) |
| Quantity< A > | morph::units::operator+ (const Quantity< A, DecA > &lhs, const Quantity< B, DecB > &rhs) |
| | Mixed-unit sum: converts rhs to lhs's unit first.
|
template<auto A, std::uint32_t DecA, auto B, std::uint32_t DecB>
requires (detail::SameEnumDistinct<A, B> && requires(Quantity<B, DecB> value) { static_cast<Quantity<A>>(value); }) |
| Quantity< A > | morph::units::operator- (const Quantity< A, DecA > &lhs, const Quantity< B, DecB > &rhs) |
| | Mixed-unit difference: converts rhs to lhs's unit first.
|
| template<auto U, std::uint32_t Dec> |
| Quantity< U, Dec > | morph::units::operator- (const Quantity< U, Dec > &operand) |
| | Negation. Empty stays empty; the declared precision is kept.
|
template<auto A, std::uint32_t DecA, auto B, std::uint32_t DecB>
requires requires { typename std::type_identity_t<Quantity<A * B>>; } |
| Quantity< A *B > | morph::units::operator* (const Quantity< A, DecA > &lhs, const Quantity< B, DecB > &rhs) |
| | Cross-unit product; the result unit is A * B per the application's consteval unit algebra. Removed from overload resolution when the algebra rejects the combination.
|
template<auto A, std::uint32_t DecA, auto B, std::uint32_t DecB>
requires requires { typename std::type_identity_t<Quantity<A / B>>; } |
| Quantity< A/B > | morph::units::operator/ (const Quantity< A, DecA > &lhs, const Quantity< B, DecB > &rhs) |
| | Cross-unit quotient; the result unit is A / B per the application's consteval unit algebra. Empty when either side is empty or the divisor is zero.
|
| template<auto U, std::uint32_t Dec> |
| Quantity< U, Dec > | morph::units::operator* (const Quantity< U, Dec > &lhs, const math::Rational &factor) |
| | Scales by a dimensionless rational (unit and declared precision kept).
|
| template<auto U, std::uint32_t Dec> |
| Quantity< U, Dec > | morph::units::operator* (const math::Rational &factor, const Quantity< U, Dec > &rhs) |
| | Scales by a dimensionless rational (unit and declared precision kept).
|
| template<auto U, std::uint32_t Dec> |
| Quantity< U, Dec > | morph::units::operator/ (const Quantity< U, Dec > &lhs, const math::Rational &divisor) |
| | Divides by a dimensionless rational (unit and declared precision kept). Empty when the quantity is empty or the divisor is zero.
|
| template<auto U, std::uint32_t Dec> |
| std::string | morph::units::toDecimalString (const Quantity< U, Dec > &quantity) |
| | Renders quantity's exact decimal alone, with no unit suffix ("5.2", "6", "N/A").
|
| template<auto U, std::uint32_t Dec> |
| std::string | morph::units::toString (const Quantity< U, Dec > &quantity) |
| | Renders quantity as value + unit (5.2kW, N/A%) — the same text std::format("{}", quantity) produces via the std::formatter specialization just below, exposed as a plain function so a caller can render a Quantity without going through std::format itself.
|
Unit-tagged, optionally-empty exact values with a derivation trail.
morph::units::Quantity<U> wraps an optional morph::math::Rational and knows three things about itself: its unit (a compile-time template tag), its exactness (an exact Rational, with a declared and a runtime notion of precision, and std::nullopt as a real "not entered" state), and how it was computed — its derivation, exposed through equation().
See docs/spec/util/quantity_type.md for the full design. The essentials:
- Units are types. Quantity<Unit::kg> and Quantity<Unit::m3> cannot be mixed; operator* / operator/ deduce the result unit from the application-defined consteval algebra on the unit enum. Within-dimension scaling is declared as UnitTraits<E>::relations (a flat list of exact UnitRelation ratios), which drive convert, conversion chaining, and the schema's display-unit selector.
- Precision is declared in the type; actual precision is runtime data. Quantity<Unit::m3, 4> overrides the unit default; the value's actual precision is the runtime DecimalPlaces tag inside the Rational and max-propagates through arithmetic. Magnitude is bounded by int64; overflow is a documented limitation, only division-by-zero yields empty.
- Empty propagates (SQL-NULL semantics). == is total; relational ordering has a precondition that both operands are engaged.
- Provenance is a build-wide toggle, MORPH_QUANTITY_PROVENANCE (default 1). With it 0, no derivation nodes are allocated and the provenance API returns empty results; Quantity<U> is the same type either way.
- Wire. On the morph JSON wire a quantity is its nullable Rational payload; the unit lives in generated schemas (ExtUnits) and C++ types.
Definition in file quantity.hpp.
template<auto U, std::uint32_t Dec>
| std::string morph::units::toDecimalString |
( |
const Quantity< U, Dec > & | quantity | ) |
|
|
inlinenodiscard |
Renders quantity's exact decimal alone, with no unit suffix ("5.2", "6", "N/A").
The numeric half of toString, and the only public way to obtain it. A view that places the number and the unit separately — a table with the unit in its column header, a right-aligned suffix, an editable field whose adjacent label carries the unit — asks for the two halves independently instead of concatenating them and then chopping the suffix back off. The unit half is Quantity::unitMeta().display.
This is the same exact-decimal path std::format("{}", quantity) takes: an integer long division of the canonical Rational at its runtime DecimalPlaces, trailing zeros trimmed. It is deliberately not std::format("{}", *quantity.value()) (which prints a num/den fraction) nor std::format("{:.Nf}", *quantity.value()) (which delegates to std::formatter<double> via toDouble() and so leaves the exact domain — 9007199254740993 renders as …992 there and as itself here).
An empty quantity renders as the fixed literal "N/A", not as an empty string. That keeps the identity toString(q) == toDecimalString(q) +
unitMeta().display true for every value, engaged or not, so toString can be — and is — implemented in terms of this function rather than growing a second rendering path that can drift from it. Rendering an absent value as blank is a presentation policy and belongs to the layer that has a layout to decide it for; the value type only reports the fact that nothing is there. A caller that wants a blank writes q.hasValue() ? toDecimalString(q) : "".
- Template Parameters
-
| U | Unit enumerator. |
| Dec | Declared decimals. |
- Parameters
-
| quantity | The value to render. |
- Returns
- The exact decimal text, or "N/A" when the quantity is empty.
Definition at line 1215 of file quantity.hpp.
template<auto U, std::uint32_t Dec>
| std::string morph::units::toString |
( |
const Quantity< U, Dec > & | quantity | ) |
|
|
inlinenodiscard |
Renders quantity as value + unit (5.2kW, N/A%) — the same text std::format("{}", quantity) produces via the std::formatter specialization just below, exposed as a plain function so a caller can render a Quantity without going through std::format itself.
Exists because Emscripten's bundled libc++ (older than the Linux/Windows standard library this project otherwise builds against — see .github/workflows/wasm-demo.yml's EMSDK_VERSION) has a known limitation recognising std::formatter partial specializations parameterized over an auto non-type template parameter (U here) for std::format's compile-time formattability check — std::format("{}",
someQuantity) fails to compile there with "the supplied type is not
formattable" even though the specialization is valid and the identical call compiles and runs correctly on every other toolchain this project targets. toString() bypasses that check entirely: it calls the same underlying logic directly instead of through std::format's trait machinery, so it works identically everywhere, WASM included.
Defined as toDecimalString(quantity) + unitMeta().display — the number and the unit are one concatenation with a single implementation of each half, so a caller that needs them apart calls toDecimalString rather than undoing the join.
- Template Parameters
-
| U | Unit enumerator. |
| Dec | Declared decimals. |
- Parameters
-
| quantity | The value to render. |
- Returns
- The formatted text.
Definition at line 1246 of file quantity.hpp.