5 Quantities and units library [quantities]

5.3 Utilities [qty.utils]

5.3.1 Non-types [qty.utils.non.types]

template<typename T, template<see below> typename U> consteval bool is-specialization-of(); // exposition only template<typename T, template<see below> typename U> consteval bool is-derived-from-specialization-of(); // exposition only
Returns:
  • For the first signature, true of T is a specialization of U, and false otherwise.
  • For the second signature, true if T has exactly one public base class that is a specialization of U and has no other base class that is a specialization of U, and false otherwise.
Remarks: An implementation provides enough overloads for all arguments to U.

5.3.2 Ratio [qty.ratio]

namespace mp_units { struct ratio { // exposition only std::intmax_t num; std::intmax_t den; consteval ratio(std::intmax_t n, std::intmax_t d = 1); friend consteval bool operator==(ratio, ratio) = default; friend consteval auto operator<=>(ratio lhs, ratio rhs) { return (lhs - rhs).num <=> 0; } friend consteval ratio operator-(ratio r) { return {-r.num, r.den}; } friend consteval ratio operator+(ratio lhs, ratio rhs) { return {lhs.num * rhs.den + lhs.den * rhs.num, lhs.den * rhs.den}; } friend consteval ratio operator-(ratio lhs, ratio rhs) { return lhs + (-rhs); } friend consteval ratio operator*(ratio lhs, ratio rhs); friend consteval ratio operator/(ratio lhs, ratio rhs) { return lhs * ratio{rhs.den, rhs.num}; } }; consteval bool is-integral(ratio r) { return r.num % r.den == 0; } consteval ratio common-ratio(ratio r1, ratio r2); }
ratio represents the rational number .
Unless otherwise specified, in the following descriptions, let R(r) be std​::​ratio<N, D>, where N and D are the values of r.num and r.den.
consteval ratio(std::intmax_t n, std::intmax_t d = 1);
Let N and D be the values of n and d.
Let R be std​::​ratio<N, D>.
Effects: Equivalent to R.
Postconditions: num == R​::​num && den == R​::​den is true.
friend consteval ratio operator*(ratio lhs, ratio rhs);
Let Res be std​::​ratio_multiply<R(lhs), R(rhs)>.
Effects: Equivalent to: return {Res​::​num, Res​::​den};
consteval ratio common-ratio(ratio r1, ratio r2);
Let Res be equal to std::common_type<std::chrono::duration<int, R(r1)>, std::chrono::duration<int, R(r2)>>::type::period
Effects: Equivalent to: return {Res​::​num, Res​::​den};

5.3.3 Symbol text [qty.sym.txt]

namespace mp_units { template<std::size_t N, std::size_t M> class symbol_text { public: std::fixed_u8string<N> utf8; // exposition only std::fixed_string<M> portable; // exposition only // constructors constexpr symbol_text(char portable); consteval symbol_text(const char (&portable)[N + 1]); constexpr symbol_text(const std::fixed_string<N>& portable); consteval symbol_text(const char8_t (&utf8)[N + 1], const char (&portable)[M + 1]); constexpr symbol_text(const std::fixed_u8string<N>& utf8, const std::fixed_string<M>& portable); // observers constexpr const auto& utf8() const { return utf8; } constexpr const auto& portable() const { return portable; } constexpr bool empty() const { return utf8().empty(); } // string operations template<std::size_t N2, std::size_t M2> friend constexpr symbol_text<N + N2, M + M2> operator+(const symbol_text& lhs, const symbol_text<N2, M2>& rhs); // comparison template<std::size_t N2, std::size_t M2> friend constexpr bool operator==(const symbol_text& lhs, const symbol_text<N2, M2>& rhs) noexcept; template<std::size_t N2, std::size_t M2> friend constexpr auto operator<=>(const symbol_text& lhs, const symbol_text<N2, M2>& rhs) noexcept; }; symbol_text(char) -> symbol_text<1, 1>; template<std::size_t N> symbol_text(const char (&)[N]) -> symbol_text<N - 1, N - 1>; template<std::size_t N> symbol_text(const std::fixed_string<N>&) -> symbol_text<N, N>; template<std::size_t N, std::size_t M> symbol_text(const char8_t (&)[N], const char (&)[M]) -> symbol_text<N - 1, M - 1>; template<std::size_t N, std::size_t M> symbol_text(const std::fixed_u8string<N>&, const std::fixed_string<M>&) -> symbol_text<N, M>; }
symbol_text represents a symbol text.
utf8 stores its UTF-8 representation, and portable stores its portable representation.
symbol_text<N, M> is a structural type (N4971, [temp.param]).
In the descriptions that follow, it is a Precondition that
  • values of char are in the basic literal character set (N4971, [lex.charset]), and
  • for a parameter of the form const CharT (&txt)[M], (txt[M - 1] == CharT()) is true.
constexpr symbol_text(char portable); consteval symbol_text(const char (&portable)[N + 1]); constexpr symbol_text(const std::fixed_string<N>& portable); consteval symbol_text(const char8_t (&utf8)[N + 1], const char (&portable)[M + 1]); constexpr symbol_text(const std::fixed_u8string<N>& utf8, const std::fixed_string<M>& portable);
For the constructors without a parameter named utf8, let utf8 be: std::bit_cast<std::fixed_u8string<N>>(std::basic_fixed_string(portable))
Effects: Equivalent to the mem-initializer-list: utf8{utf8}, portable{portable}
template<std::size_t N2, std::size_t M2> friend constexpr symbol_text<N + N2, M + M2> operator+(const symbol_text& lhs, const symbol_text<N2, M2>& rhs);
Effects: Equivalent to: return symbol_text<N + N2, M + M2>(lhs.utf8() + rhs.utf8(), lhs.portable() + rhs.portable());
template<std::size_t N2, std::size_t M2> friend constexpr bool operator==(const symbol_text& lhs, const symbol_text<N2, M2>& rhs) noexcept; template<std::size_t N2, std::size_t M2> friend constexpr auto operator<=>(const symbol_text& lhs, const symbol_text<N2, M2>& rhs) noexcept;
Let @ be the operator.
Effects: Equivalent to: return std::make_tuple(std::cref(lhs.utf8()), std::cref(lhs.portable())) @ std::make_tuple(std::cref(rhs.utf8()), std::cref(rhs.portable()));

5.3.4 Symbolic expressions [qty.sym.expr]

5.3.4.1 General [qty.sym.expr.general]

Subclause [qty.sym.expr] specifies the components used to maintain ordered, simplified, and readable argument lists in the names of specializations.
[Example 1: using namespace si::unit_symbols; int x = kg * km / square(h); // error: cannot construct from // derived_unit<si​::​kilo_<si​::​gram>, si​::​kilo_<si​::​metre>, per<power<non_si​::​hour, 2>>>
The library ensures decltype(kg * km / square(h)) is styled-like as commented in diagnostics, provided that, in the implementation-defined total order of types, decltype(kg) is less than decltype(km).
— end example]

5.3.4.2 Concept SymbolicConstant [qty.sym.expr.concepts]

template<typename T> concept SymbolicConstant = // exposition only std::is_empty_v<T> && std::is_final_v<T> && std::is_trivially_default_constructible_v<T> && std::is_trivially_copy_constructible_v<T> && std::is_trivially_move_constructible_v<T> && std::is_trivially_destructible_v<T>;
The concept SymbolicConstant is used to constrain the types that are used in symbolic expressions.

5.3.4.3 Types [qty.sym.expr.types]

namespace mp_units { template<typename T, typename... Ts> struct per final {}; }
per is used to store arguments with negative exponents.
A specialization of per represents the product of the inverse of its template arguments.
A program that instantiates a specialization of per that is not a possible result of the library specifications is ill-formed, no diagnostic required.
namespace mp_units { template<typename F, int Num, int... Den> requires see below struct power final { using factor = F; // exposition only static constexpr ratio exponent{Num, Den...}; // exposition only }; }
power represents a power (IEC 60050, 102-02-08) of the form .
[Note 1: 
Den is optional to shorten the type name when Den is 1.
— end note]
A program that instantiates a specialization of power that is not a possible result of the library specifications is ill-formed, no diagnostic required.
Let r be ratio{Num, Den...}.
Let is-valid-ratio be true if r is a valid constant expression, and false otherwise.
The expression in the requires-clause is equivalent to: is-valid-ratio && (r > ratio{0}) && (r != ratio{1})

5.3.4.4 Algorithms [qty.sym.expr.algos]

template<typename T> using expr-type = see below; // exposition only
expr-type<T> denotes U if T is of the form power<U, Ints...>, and T otherwise.
template<typename T, typename U> consteval bool type-less-impl(); // exposition only
Returns: true if T is less than U in an implementation-defined total order for types, and false otherwise.
template<typename Lhs, typename Rhs> struct type-less : // exposition only std::bool_constant<is-specialization-of<Rhs, power>() || type-less-impl<expr-type<Lhs>, expr-type<Rhs>>()> {};
type-less meets the requirements of the Pred parameter of the symbolic expression algorithms below.
template<typename... Ts> struct type-list {}; // exposition only template<typename OneType, typename... Ts> struct expr-fractions { // exposition only using num = see below; // exposition only using den = see below; // exposition only }
expr-fractions divides a symbolic expression to numerator and denominator parts.
Let EF be a specialization of expr-fractions.
  • If EF is of the form expr-fractions<OneType, Ts..., per<Us...>>, then
    • EF​::​num denotes type-list<Ts...>, and
    • EF​::​den denotes type-list<Us...>.
  • Otherwise, EF is of the form expr-fractions<OneType, Ts...>, and
    • EF​::​num denotes type-list<Ts...>, and
    • EF​::​den denotes type-list<>.
The symbolic expression algorithms perform operations on symbolic constants.
A symbolic constant is a type that is a model of SymbolicConstant.
[Example 1: 
The dimension dim_length, the quantity time, and the unit one are symbolic constants.
— end example]
The algorithms also support powers with a symbolic constant base and a rational exponent, products thereof, and fractions thereof.
template<template<typename...> typename To, typename OneType, template<typename, typename> typename Pred = type-less, typename Lhs, typename Rhs> consteval auto expr-multiply(Lhs, Rhs); // exposition only template<template<typename...> typename To, typename OneType, template<typename, typename> typename Pred = type-less, typename Lhs, typename Rhs> consteval auto expr-divide(Lhs lhs, Rhs rhs); // exposition only template<template<typename...> typename To, typename OneType, typename T> consteval auto expr-invert(T); // exposition only template<std::intmax_t Num, std::intmax_t Den, template<typename...> typename To, typename OneType, template<typename, typename> typename Pred = type-less, typename T> requires(Den != 0) consteval auto expr-pow(T); // exposition only
Mandates:
  • OneType is the neutral element (IEC 60050, 102-01-19) of the operation, and
  • Pred is a Cpp17BinaryTypeTrait (N4971, [meta.rqmts]) with a base characteristic of std​::​bool_constant<B>.
    Pred<T, U> implements a total order for types; B is true if T is ordered before U, and false otherwise.
Effects:
First, inputs to the operations are obtained from the types of the function parameters.
If the type of a function parameter is:
  • A specialization of To, then its input is the product of its template arguments, and the following also apply.
  • A specialization of per, then its input is the product of the inverse of its template arguments, and the following also apply.
  • A specialization of the form power<F, Num>, then its input is , or a specialization of the form power<F, Num, Den>, then its input is , and the following also applies.
  • Otherwise, the input is the symbolic constant itself.
[Example 2: 
Item by item, this algorithm step goes from the C++ parameter type decltype(km / square(h)), styled in diagnostics like derived_unit<si​::​kilo_<si​::​metre>, per<power<non_si​::​hour, 2>>,
  • to decltype(km) ×per<power<decltype(h), 2> (product of To's arguments),
  • to (product of inverse of per's arguments),
  • to (powers as powers),
  • to where and (symbolic substitution) in the mathematical domain.
— end example]
Then, the operation takes place:
  • expr-multiply multiplies its inputs,
  • expr-divide divides the input of its first parameter by the input of its second parameter,
  • expr-invert divides 1 by its input, and
  • expr-pow raises its input to the .
Finally, let r be the result of the operation simplified as follows:
  • All terms are part of the same fraction (if any).
  • There is at most a single term with a given symbolic constant.
  • There are no negative exponents.
  • 1 is only present as r and as a numerator with a denominator not equal to 1.
[Example 3: 
Item by item:

(single fraction)
(unique symbolic constants)
(positive exponents)
(non-redundant 1s)
— end example]
Returns: r is mapped to the return type:
  • If , returns OneType{}.
  • Otherwise, if r is a symbolic constant, returns r.
  • Otherwise, first applies the following mappings to the terms of r:
    • is mapped to power<x, n, d>, and is mapped to power<x, n>, and
    • 1 is mapped to OneType{}.
  • Then, a denominator x of r (if any) is mapped to per<x>.
  • Then, sorts r without per (if any) and the template arguments of per (if any) according to Pred.
  • Finally, returns To<r>{}, where per (if any) is the last argument.
Remarks: A valid template argument list for To and per is formed by interspersing commas between each mapped term.
If a mapping to std​::​intmax_t is not representable, the program is ill-formed.
expr-map maps the contents of one symbolic expression to another resulting in a different type list.
template<template<typename> typename Proj, template<typename...> typename To, typename OneType, template<typename, typename> typename Pred = type-less, typename T> consteval auto expr-map(T); // exposition only
Let
  • expr-type-map<U> be power<Proj<F>, Ints...> if U is of the form power<F, Ints...>, and Proj<U> otherwise,
  • map-power(u) be pow<Ints...>(F{}) if decltype(u) is of the form power<F, Ints...>, and u otherwise, and
  • Nums and Dens be packs denoting the template arguments of T​::​nums and T​::​dens, respectively.
Returns: (OneType{} * ... * map-power(expr-type-map<Nums>{})) / (OneType{} * ... * map-power(expr-type-map<Dens>{}))