15 KiB
		
	
	
	
	
	
	
	
			
		
		
	
	Concepts
This chapter enumerates all the user-facing concepts in the mp-units library.
Dimension<T>
Dimension concept matches a dimension of either a base
or derived quantity:
- Base dimensions are explicitly defined by the user
by inheriting from the instantiation of a base_dimensionclass template. It should be instantiated with a unique symbol identifier describing this dimension in a specific system of quantities.
- Derived dimensions are implicitly created by the library's framework based on the quantity equation provided in the quantity specification.
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:
- Base quantities defined by a user by inheriting from
the quantity_specclass template instantiated with a base dimension argument.
- Derived named quantities defined by a user by
inheriting from the quantity_specclass template instantiated with a result of a quantity equation passed as an argument.
- Other named quantities forming a hierarchy of quantities
of the same kind defined by a user by inheriting from the
quantity_specclass template instantiated with another "parent" quantity specification passed as an argument.
- Quantity kinds describing a family of mutually comparable quantities.
- Intermediate derived quantity specifications being a result of a quantity equations on other specifications.
QuantitySpecOf<T, V>
QuantitySpecOf concept is satisfied when both arguments satisfy a QuantitySpec concept
and when T is implicitly convertible to V.
??? info "More details"
Additionally:
- `T` should not be a [nested quantity specification of `V`](dimensionless_quantities.md/#nested-quantity-kinds)
- either `T` is quantity kind or `V` should not be a
  [nested quantity specification of `T`](dimensionless_quantities.md/#nested-quantity-kinds)
Those additional conditions are required to make the following work:
```cpp
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_unitclass 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_unitclass 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_unitclass 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_unitclass 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](faster_than_lightspeed_constants.md).
AssociatedUnit<T>
AssociatedUnit concept describes a unit with an associated quantity
and is satisfied by:
- All units derived from a named_unitclass template instantiated with a unique symbol identifier and aQuantitySpecof a quantity kind.
- All units being a result of unit equations on other associated units.
??? abstract "Examples"
All units in the [SI](../../appendix/glossary.md#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 have a unit of `one`.
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.
??? abstract "Examples"
All units in the [SI](../../appendix/glossary.md#si) can be prefixed with SI-defined prefixes.
Some [off-system units](../../appendix/glossary.md#off-system-unit) like `non_si::day`
can't be prefixed. To enforce that, the following has to be provided:
```cpp
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.
??? info "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](dimensionless_quantities.md/#nested-quantity-kinds).
UnitCompatibleWith<T, V1, V2>
UnitCompatibleWith concept is satisfied for all units T when:
- V1is a- Unit,
- V2is a- QuantitySpec,
- Tand- V1are defined in terms of the same reference unit,
- if Tis anAssociatedUnitit should satisfyUnitOf<V2>.
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 referenceclass template with aQuantitySpecpassed as the first template argument and aUnitpassed as the second one.
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. | 
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>
??? tip
If we want to use scalar types to also express [vector quantities](character_of_a_quantity.md#defining-vector-and-tensor-quantities)
(e.g., ignoring the "direction" of the vector) the following definition can be provided to enable such a behavior:
```cpp
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 an instantiation of a quantity class template.
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_originclass template.
- All types derived from a relative_point_originclass template.
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>.
??? abstract "Examples"
`si::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:
```cpp
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.
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 Vhave 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 referencethat matches theReferenceconcept,
- reptype that matches- RepresentationOfconcept with the character provided in- reference.
- to_numerical_value(T)static member function returning a raw value of the quantity packed in either- convert_explicitlyor- convert_implicitlywrapper that enables implicit conversion in the latter case.
- from_numerical_value(rep)static member function returning- Tpacked in either- convert_explicitlyor- convert_implicitlywrapper that enables implicit conversion in the latter case.
??? abstract "Examples"
This is how support for `std::chrono::seconds` can be provided:
```cpp
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 convert_implicitly<rep> to_numerical_value(const std::chrono::seconds& q)
  {
    return q.count();
  }
  [[nodiscard]] static constexpr convert_implicitly<std::chrono::seconds> from_numerical_value(const rep& v)
  {
    return std::chrono::seconds(v);
  }
};
quantity q = 42s;
std::chrono::seconds dur = 42 * s;
```
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 referencethat matches theReferenceconcept.
- Static data member point_originthat matches thePointOriginconcept.
- reptype that matches- RepresentationOfconcept with the character provided in- reference.
- to_quantity(T)static member function returning the- quantitybeing the offset of the point from the origin packed in either- convert_explicitlyor- convert_implicitlywrapper that enables implicit conversion in the latter case.
- from_quantity(quantity<reference, rep>)static member function returning- Tpacked in either- convert_explicitlyor- convert_implicitlywrapper that enables implicit conversion in the latter case.
??? abstract "Examples"
This is how support for a `std::chrono::time_point` of `std::chrono::seconds` can be provided:
```cpp
template<typename C>
struct mp_units::quantity_point_like_traits<std::chrono::time_point<C, std::chrono::seconds>> {
  using T = std::chrono::time_point<C, std::chrono::seconds>;
  static constexpr auto reference = si::second;
  static constexpr struct point_origin : absolute_point_origin<isq::time> {} point_origin{};
  using rep = std::chrono::seconds::rep;
  [[nodiscard]] static constexpr convert_implicitly<quantity<reference, rep>> to_quantity(const T& qp)
  {
    return quantity{qp.time_since_epoch()};
  }
  [[nodiscard]] static constexpr convert_implicitly<T> from_quantity(const quantity<reference, rep>& q)
  {
    return T(q);
  }
};
quantity_point qp = time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now());
std::chrono::sys_seconds q = qp + 42 * s;
```