Skip to content

Basic Concepts

The most important concepts in the mp-units library are Dimension, QuantitySpec, Unit, Reference, Representation, Quantity, and QuantityPoint:

flowchart TD
    Dimension --- QuantitySpec
    QuantitySpec --- Reference
    Unit --- Reference
    Reference --- Quantity
    Representation --- Quantity
    Quantity --- QuantityPoint
    PointOrigin --- QuantityPoint

    click Dimension "#Dimension"
    click QuantitySpec "#QuantitySpec"
    click Unit "#Unit"
    click Reference "#Reference"
    click Representation "#Representation"
    click Quantity "#Quantity"
    click PointOrigin "#PointOrigin"
    click QuantityPoint "#QuantityPoint"

Dimension<T>

Dimension concept matches a dimension of either a base or derived quantity:

Examples

isq::dim_length, isq::dim_mass, isq::dim_time, isq::dim_electric_current, isq::dim_thermodynamic_temperature, isq::dim_amount_of_substance, and isq::dim_luminous_intensity are the dimensions of base quantities in the ISQ.

IEC 80000 provides iec80000::dim_traffic_intensity base dimension to extend ISQ with information technology quantities.

A Dimension can be defined by the user in the following way:

inline constexpr struct dim_length : base_dimension<"L"> {} dim_length;

The division on quantity specifications also divides their dimensions:

static_assert((isq::length / isq::time).dimension == isq::dim_length / isq::dim_time);

The dimension equation of isq::dim_length / isq::dim_time results in the derived_dimension<isq::dim_length, per<isq::dim_time>> type.

DimensionOf<T, V>

DimensionOf concept is satisfied when both arguments satisfy a Dimension concept and when they compare equal.

QuantitySpec<T>

QuantitySpec concept matches all the quantity specifications including:

Examples

isq::length, isq::mass, isq::time, isq::electric_current, isq::thermodynamic_temperature, isq::amount_of_substance, and isq::luminous_intensity are the specifications of base quantities in the ISQ.

isq::width, isq::height, isq::radius, and isq::position_vector are only a few of many quantities of a kind length specified in the ISQ.

kind_of<isq::length> behaves as any of the quantities of a kind length.

isq::area, isq::speed, isq::moment_of_force are only a few of many derived quantities provided in the ISQ.

QuantitySpec can be defined by the user in one of the following ways:

inline constexpr struct length : quantity_spec<dim_length> {} length;
inline constexpr struct height : quantity_spec<length> {} height;
inline constexpr struct speed : quantity_spec<length / time> {} speed;
inline constexpr struct length : quantity_spec<length, dim_length> {} length;
inline constexpr struct height : quantity_spec<height, length> {} height;
inline constexpr struct speed : quantity_spec<speed, length / time> {} speed;
QUANTITY_SPEC(length, dim_length);
QUANTITY_SPEC(height, length);
QUANTITY_SPEC(speed, length / time);

The quantity equation of isq::length / isq::time results in the derived_quantity_spec<isq::length, per<isq::time>> type.

QuantitySpecOf<T, V>

QuantitySpecOf concept is satisfied when both arguments satisfy a QuantitySpec concept and when T is implicitly convertible to V.

More details

Additionally:

Those additional conditions are required to make the following work:

static_assert(ReferenceOf<si::radian, isq::angular_measure>);
static_assert(!ReferenceOf<si::radian, dimensionless>);
static_assert(!ReferenceOf<isq::angular_measure[si::radian], dimensionless>);
static_assert(ReferenceOf<one, isq::angular_measure>);
static_assert(!ReferenceOf<dimensionless[one], isq::angular_measure>);

Unit<T>

Unit concept matches all the units in the library including:

  • Base units defined by a user by inheriting from the named_unit class template instantiated with a unique symbol identifier describing this unit in a specific system of units.
  • Named scaled units defined by a user by inheriting from the named_unit class template instantiated with a unique symbol identifier and a product of multiplying another unit with some magnitude.
  • Prefixed units defined by a user by inheriting from the prefixed_unit class template instantiated with a prefix symbol, a magnitude, and a unit to be prefixed.
  • Derived named units defined by a user by inheriting from the named_unit class template instantiated with a unique symbol identifier and a result of unit equation passed as an argument.
  • Derived unnamed units being a result of a unit equations on other units.

Note

In the mp-units library, physical constants are also implemented as units.

Examples

si::second, si::metre, si::kilogram, si::ampere, si::kelvin, si::mole, and si::candela are the base units of SI.

si::kilo<si::metre> is a prefixed unit on length.

si::radian, si::newton, and si::watt are examples of named derived quantities within SI.

non_si::minute is an example of a scaled unit of time.

si::si2019::speed_of_light_in_vacuum is a physical constant standardized by the SI in 2019.

Unit can be defined by the user in one of the following ways:

template<PrefixableUnit auto U> struct kilo_ : prefixed_unit<"k", mag_power<10, 3>, U> {};
template<PrefixableUnit auto U> inline constexpr kilo_<U> kilo;

inline constexpr struct second : named_unit<"s", kind_of<isq::time>> {} second;
inline constexpr struct gram : named_unit<"g", kind_of<isq::mass>> {} gram;
inline constexpr struct minute : named_unit<"min", mag<60> * second> {} minute;
inline constexpr struct kilogram : decltype(kilo<gram>) {} kilogram;
inline constexpr struct newton : named_unit<"N", kilogram * metre / square(second)> {} newton;

inline constexpr struct speed_of_light_in_vacuum : named_unit<"c", mag<299'792'458> * metre / second> {} speed_of_light_in_vacuum;

The unit equation of si::metre / si::second results in the derived_unit<si::metre, per<si::second>> type.

AssociatedUnit<T>

AssociatedUnit concept describes a unit with an associated quantity and is satisfied by:

  • All units derived from a named_unit class template instantiated with a unique symbol identifier and a QuantitySpec.
  • All units being a result of a unit equations on other associated units.
Examples

All units in the SI have associated quantities. For example, si::second is specified to measure isq::time.

Natural units typically do not have an associated quantity. For example, if we assume c = 1, a natural::second unit can be used to measure both time and length. In such case speed would be a dimensionless quantity.

PrefixableUnit<T>

PrefixableUnit concept is satisfied by all units derived from a named_unit class template for which a customization point unit_can_be_prefixed<T{}> was not explicitly set to false. Such units can be passed as an argument to a prefixed_unit class template.

Examples

All units in the SI can be prefixed with SI-defined prefixes.

Some off-system units like non_si::day can't be prefixed. To enforce that the following has to be provided:

template<> inline constexpr bool unit_can_be_prefixed<non_si::day> = false;

UnitOf<T, V>

UnitOf concept is satisfied for all units T matching an AssociatedUnit concept with an associated quantity type implicitly convertible to V.

More details

Additionally, the kind of V and the kind of quantity type associated with T must be the same, or the quantity type associated with T may not be derived from the kind of V.

This condition is required to make dimensionless[si::radian] invalid as si::radian should be only used for isq::angular_measure which is a nested quantity kind within the dimensionless quantities tree.

Reference<T>

Reference concept is satisfied by all quantity reference types. Such types provide all the meta-information required to create a Quantity. A Reference can either be:

  • An AssociatedUnit.
  • The instantiation of a reference class template with a QuantitySpec passed as the first template argument and a Unit passed as the second one.
Examples

si::metre is defined in the SI as a unit of isq::length and thus can be used as a reference to instantiate a quantity of length.

The expression isq::height[m] results with reference<isq::height, si::metre> which can be used to instantiate a quantity of isq::height with a unit of si::metre.

ReferenceOf<T, V>

ReferenceOf concept is satisfied by references T that match the following value V:

V Condition
Dimension The dimension of a quantity specification satisfies DimensionOf<V> concept.
QuantitySpec The quantity specification satisfies QuantitySpecOf<V> concept.
quantity_character The quantity specification has a character of V.

Representation<T>

Representation concept constraints a type of a number that stores the value of a quantity.

RepresentationOf<T, Ch>

RepresentationOf concept is satisfied by all Representation types that are of a specified quantity character Ch.

A user can declare a custom representation type to be of a specific character by providing the specialization with true for one or more of the following variable templates:

  • is_scalar<T>
  • is_vector<T>
  • is_tensor<T>
Examples

If we want to use scalar types to express vector quantities (e.g. ignoring the "direction" of the vector) the following definition can be provided to enable such a behavior:

template<class T>
  requires mp_units::is_scalar<T>
inline constexpr bool mp_units::is_vector<T> = true;

Quantity<T>

Quantity concept matches every quantity in the library and is satisfied by all types being or deriving from and instantiation of a quantity class template.

Examples

All of 42 * m, 42 * si::metre, 42 * isq::height[m], and isq::height(42 * m) create a quantity and thus satisfy a Quantity concept.

A quantity type can also be specified explicitly (i.e. quantity<si::metre, int>, quantity<isq::height[m]>).

QuantityOf<T, V>

QuantityOf concept is satisfied by all the quantities for which a ReferenceOf<V> is true.

PointOrigin<T>

PointOrigin concept matches all quantity point origins in the library. It is satisfied by either:

  • All types derived from an absolute_point_origin class template.
  • All types derived from an relative_point_origin class template.
Examples

The types of both definitions below satisfy a PointOrigin concept:

inline constexpr struct absolute_zero : absolute_point_origin<isq::thermodynamic_temperature> {} absolute_zero;
inline constexpr struct ice_point : relative_point_origin<absolute_zero + 273.15 * kelvin> {} ice_point;

PointOriginFor<T, V>

PointOriginFor concept is satisfied by all PointOrigin types that have quantity type implicitly convertible from quantity specification V, which means that V must satisfy QuantitySpecOf<T::quantity_spec>.

Examples

ice_point can serve as a point origin for points of isq::Celsius_temperature because this quantity type implicitly converts to isq::thermodynamic_temperature.

However, if we define mean_sea_level in the following way:

inline constexpr struct mean_sea_level : absolute_point_origin<isq::altitude> {} mean_sea_level;

then it can't be used as a point origin for points of isq::length or isq::width as none of them is implicitly convertible to isq::altitude:

  • not every "length" is an "altitude",
  • "width" is not compatible with "altitude".

QuantityPoint<T>

QuantityPoint concept is satisfied by all types being either a specialization or derived from quantity_point class template.

Examples

The following specifies a quantity point defined in terms of an ice_point quantity point origin provided in the previous example:

constexpr auto room_reference_temperature = ice_point + isq::Celsius_temperature(21 * deg_C);

QuantityPointOf<T, V>

QuantityPointOf concept is satisfied by all the quantity points T that match the following value V:

V Condition
Reference The quantity point reference satisfies ReferenceOf<V> concept.
PointOrigin The point and V have the same absolute point origin.

QuantityLike<T>

QuantityLike concept provides interoperability with other libraries and is satisfied by a type T for which an instantiation of quantity_like_traits type trait yields a valid type that provides:

  • Static data member reference that matches the Reference concept,
  • rep type that matches RepresentationOf concept with the character provided in reference,
  • value(T) static member function returning a raw value of the quantity.
Examples

This is how support for std::chrono::seconds can be provided:

template<>
struct mp_units::quantity_like_traits<std::chrono::seconds> {
  static constexpr auto reference = si::second;
  using rep = std::chrono::seconds::rep;
  [[nodiscard]] static constexpr rep value(const std::chrono::seconds& q) { return q.count(); }
};

quantity q(42s);

QuantityPointLike<T>

QuantityPointLike concept provides interoperability with other libraries and is satisfied by a type T for which an instantiation of quantity_point_like_traits type trait yields a valid type that provides:

  • Static data member reference that matches the Reference concept
  • Static data member point_origin that matches the PointOrigin concept
  • rep type that matches RepresentationOf concept with the character provided in reference
  • quantity_from_origin(T) static member function returning the quantity being the offset of the point from the origin
Examples

This is how support for a std::chrono::time_point of std::chrono::seconds can be provided:

template<typename C>
struct mp_units::quantity_point_like_traits<std::chrono::time_point<C, std::chrono::seconds>> {
  static constexpr auto reference = si::second;
  static constexpr auto point_origin = chrono_point_origin;
  using rep = std::chrono::seconds::rep;
  [[nodiscard]] static constexpr auto quantity_from_origin(const std::chrono::time_point<C, std::chrono::seconds>& qp)
  {
    return quantity{std::chrono::duration_cast<std::chrono::seconds>(qp.time_since_epoch())};
  }
};

quantity_point qp(time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()));