diff --git a/docs/discussion/idioms/unit-slots.md b/docs/discussion/idioms/unit-slots.md index 56ca447a..f2c1446a 100644 --- a/docs/discussion/idioms/unit-slots.md +++ b/docs/discussion/idioms/unit-slots.md @@ -57,6 +57,16 @@ they have two advantages that make them easier to read: 2. You can use grammatically correct names, such as `meters / squared(second)` (note: `second` is singular), rather than `Meters{} / squared(Seconds{})`. +#### Notes for `QuantityPoint` + +`QuantityPoint` doesn't use quantity makers: it uses quantity _point_ makers. For example, instead +of the quantity maker `meters`, it uses the quantity point maker `meters_pt`. + +The implications are straightforward. If you have a `QuantityPoint` instead of a `Quantity`, then +use the quantity _point_ maker instead of the _quantity_ maker. The library will enforce this +automatically: for example, you can't pass `meters` to a `QuantityPoint`'s unit slot, and you can't +pass `meters_pt` to a `Quantity`'s unit slot. + ## Examples: rounding to RPM Let's look at some examples, using this quantity variable: diff --git a/docs/reference/quantity.md b/docs/reference/quantity.md index abf8caf3..746d2994 100644 --- a/docs/reference/quantity.md +++ b/docs/reference/quantity.md @@ -41,33 +41,10 @@ For more practice with this, see [Tutorial 102: API Types](../tutorial/102-api-t ## Constructing `Quantity` {#constructing} -There are several ways to construct a `Quantity` object. For concreteness, we'll use this skeleton -to show the signatures. +There are several ways to construct a `Quantity` object. -```cpp -template -class Quantity { - - // A) Implicit constructor from another Quantity - template - Quantity(Quantity other); - - // B) Construct from `Zero` - Quantity(Zero); - - // C) Default constructor - Quantity(); - - // D) Construct from equivalent type - template - Quantity(T &&x); - // NOTE: only exists when `T` is an "exactly-equivalent" type. One example: - // `std::chrono::nanoseconds` can construct an `au::QuantityI64>`. -}; -``` - -However, the _preferred_ way to construct a `Quantity` is actually none of these. It's the -_quantity maker_, which we describe next. +- The **preferred** way, which we'll explain below, is to use a _quantity maker_. +- The other ways are all normal C++ constructors. ### Quantity Maker (preferred) @@ -84,17 +61,21 @@ Makers](../tutorial/101-quantity-makers.md). ### Implicit constructor from another `Quantity` {#implicit-from-quantity} -This is option `A)` from the [previous section](#constructing). - -```cpp -template -Quantity(Quantity other); -``` - This constructor performs a unit conversion. The result will represent the _same quantity_ as the input, but expressed in the _units_ of the newly constructed object's type. It will also convert the stored value to the _rep_ of the constructed object, if necessary. +Here is the signature. We've simplified it slightly for illustration purposes, and enclosed it +inside the class definition for context. + +```cpp +template +class Quantity { + template + Quantity(Quantity other); +}; +``` + This constructor only exists when this unit-and-rep conversion is both meaningful and safe. It can fail to exist in several ways. @@ -119,14 +100,17 @@ constructor, and it must be "forced" to do so. See [`.as(unit)`](#as) for mo ### Constructing from `Zero` -This is option `B)` from the [previous section](#constructing). +This constructs a `Quantity` with a value of 0. + +Here is the signature, enclosed in the class definition for context. ```cpp -Quantity(Zero); +template +class Quantity { + Quantity(Zero); +}; ``` -This constructs a `Quantity` with a value of 0. - !!! note `Zero` is the name of the type. At the callsite, you would pass an _instance_ of that type, such as the constant `ZERO`. For example: @@ -135,29 +119,39 @@ This constructs a `Quantity` with a value of 0. QuantityD height{ZERO}; ``` +For more information on the motivation and use of this type, read [our `Zero` +discussion](../discussion/concepts/zero.md). + ### Default constructor -This is option `C)` from the [previous section](#constructing). +Here is the signature of the constructor, enclosed in the class template definition for context. ```cpp -Quantity(); +template +class Quantity { + Quantity(); +}; ``` A default-constructed `Quantity` is initialized to some value, which helps avoid certain kinds of -memory safety bugs. However, the value is contractually unspecified. You can look up that value by -reading the source code, but we may change it in the future, and we would not consider this to be -a breaking change. The only valid operation on a default-constructed `Quantity` is to assign to it -later on. +memory safety bugs. However, **the value is contractually unspecified**. You can of course look up +that value by reading the source code, but we may change it in the future, and **we would not +consider this to be a breaking change**. The only valid operation on a default-constructed +`Quantity` is to assign to it later on. ### Constructing from corresponding quantity -This is option `D)` from the [previous section](#constructing). +Here is the signature. We've simplified it slightly for illustration purposes, and enclosed it +inside the class definition for context. ```cpp -template -Quantity(T &&x); -// NOTE: only exists when `T` is an "exactly-equivalent" type. One example: -// `std::chrono::nanoseconds` can construct an `au::QuantityI64>`. +template +class Quantity { + template + Quantity(T &&x); + // NOTE: only exists when `T` is an "exactly-equivalent" type. One example: + // `std::chrono::nanoseconds` can construct an `au::QuantityI64>`. +}; ``` This constructor will only exist when `T` has a ["corresponding @@ -170,7 +164,7 @@ In order to access the raw numeric value stored inside of `Quantity`, you must e unit at the callsite. There are two functions which can do this, depending on whether you want to access by value or by reference. -### `.in(unit)` {#extracting-with-in} +### By value: `.in(unit)` {#extracting-with-in} This function returns the underlying stored value, by value. See the [unit slots](../discussion/idioms/unit-slots.md) discussion for valid choices for `unit`. @@ -185,7 +179,7 @@ slots](../discussion/idioms/unit-slots.md) discussion for valid choices for `uni You can retrieve the underlying value by writing either `t.in(seconds)` (passing the `QuantityMaker`), or `t.in(Seconds{})` (passing an instance of the unit). -### `.data_in(unit)` +### By reference: `.data_in(unit)` This function returns a reference to the underlying stored value. See the [unit slots](../discussion/idioms/unit-slots.md) discussion for valid choices for `unit`. @@ -235,14 +229,14 @@ are forbidden. Additionally, the `Rep` of the output is identical to the `Rep` ??? example "Example: forcing a conversion from inches to feet" `inches(24).as(feet)` is not allowed. This conversion will divide the underlying value, `24`, - by `12`. While this particular value would produce an integer result, most other `int` values - would not. Because our result uses `int` for storage --- same as the input --- we forbid this. + by `12`. Now, it so happens that this _particular_ value _would_ produce an integer result. + However, the compiler must decide whether to permit this operation _at compile time_, which + means we don't yet know the value. Since most `int` values would _not_ produce integer results, + we forbid this. `inches(24).as(feet)` _is_ allowed. The "explicit rep" template parameter has "forcing" - semantics. This would produce `feet(2)`. - - However, note that this operation uses integer division, which truncates: so, for example, - `inches(23).as(feet)` would produce `feet(1)`. + semantics. This would produce `feet(2)`. However, note that this operation uses integer + division, which truncates: so, for example, `inches(23).as(feet)` would produce `feet(1)`. !!! tip Prefer to **omit** the template argument if possible, because you will get more safety checks. @@ -265,7 +259,7 @@ slots](../discussion/idioms/unit-slots.md) discussion for valid choices for `uni `int`. The above example used the quantity maker, `inches`. One could also use an instance of the unit - type `Inches`, writing `length.as(Inches{})`. The former is generally preferable; the latter is + type `Inches`, writing `length.in(Inches{})`. The former is generally preferable; the latter is mainly useful in generic code where the unit type may be all you have. **Without** a template argument, `.in(unit)` obeys the same safety checks as for the [implicit @@ -280,14 +274,14 @@ are forbidden. Additionally, the `Rep` of the output is identical to the `Rep` ??? example "Example: forcing a conversion from inches to feet" `inches(24).in(feet)` is not allowed. This conversion will divide the underlying value, `24`, - by `12`. While this particular value would produce an integer result, most other `int` values - would not. Because our result uses `int` --- same as the input's rep --- we forbid this. + by `12`. Now, it so happens that this _particular_ value _would_ produce an integer result. + However, the compiler must decide whether to permit this operation _at compile time_, which + means we don't yet know the value. Since most `int` values would _not_ produce integer results, + we forbid this. `inches(24).in(feet)` _is_ allowed. The "explicit rep" template parameter has "forcing" - semantics. This would produce `2`. - - However, note that this operation uses integer division, which truncates: so, for example, - `inches(23).in(feet)` would produce `1`. + semantics. This would produce `2`. However, note that this operation uses integer division, + which truncates: so, for example, `inches(23).in(feet)` would produce `1`. !!! tip Prefer to **omit** the template argument if possible, because you will get more safety checks. diff --git a/docs/reference/quantity_point.md b/docs/reference/quantity_point.md index 8cb64553..7e03b5ac 100644 --- a/docs/reference/quantity_point.md +++ b/docs/reference/quantity_point.md @@ -1,31 +1,418 @@ # QuantityPoint -`QuantityPoint` is our [affine space type](http://videocortex.io/2018/Affine-Space-Types/). The -core use cases include: +`QuantityPoint` is our [affine space type](http://videocortex.io/2018/Affine-Space-Types/). Common +example use cases include temperatures and mile markers. See our [`QuantityPoint` +discussion](../discussion/concepts/quantity_point.md) for a more detailed understanding. -- **Temperatures.** Specifically, when you want to represent "what temperature is it?" rather than - "how much did the temperature change?" +`QuantityPoint` is a template, `QuantityPoint`, with two parameters. Both are typenames. -- **Mile markers.** That is to say, points along a linear path which are indexed by distances. It - wouldn't make sense to _add_ two mile markers, but you can _subtract_ them and obtain a _distance_ - (which is a [`Quantity`](./quantity.md), not a `QuantityPoint`). +- `U` is the _unit_: a type representing the [unit of measure](./unit.md). +- `R` is the _"rep"_, a term we borrow from the [`std::chrono` + library](https://en.cppreference.com/w/cpp/chrono/duration). It's the underlying raw numeric type + which holds the wrapped value. -!!! warning "TODO: this page is a stub" +## Naming `QuantityPoint` in code -We will provide a full-fledged reference for quantity points later. For now, here are the basics. +You can use both template parameters directly (for example, `QuantityPoint`). However, +multiple template parameters can be awkward to read. It can also give the parser some trouble when +you're forced to use macros, as in googletest. For that reason, we also provide "Rep-named +aliases", so you can express the rep more precisely, and put the visual focus on the unit: -1. `QuantityPoint` stores a value of numeric type `R` (called the "Rep"), whose units are the - [unit type](./unit.md) `U`. +| Rep-named alias | Equivalent to: | +|-----------------|----------------| +| `QuantityPointD` | `QuantityPoint` | +| `QuantityPointF` | `QuantityPoint` | +| `QuantityPointI` | `QuantityPoint` | +| `QuantityPointU` | `QuantityPoint` | +| `QuantityPointI32` | `QuantityPoint` | +| `QuantityPointU32` | `QuantityPoint` | +| `QuantityPointI64` | `QuantityPoint` | +| `QuantityPointU64` | `QuantityPoint` | - - **Example:** `QuantityPoint` represents an along-path point, whose distance is - measured in `Meters`, in a `double` variable. +## Constructing `QuantityPoint` {#constructing} -2. We provide "Rep-named aliases" for better ergonomics. +There are three ways to construct a `QuantityPoint` object. - - **Example:** `QuantityPointD` is an alias for `QuantityPoint`. +- The **preferred** way, which we'll explain below, is to use a _quantity point maker_. +- The other two ways are both normal C++ constructors. -3. You cannot get values into or out of `QuantityPoint` without _explicitly naming the unit, at the - callsite_. We do not have tutorials for this yet, but it works the same as `Quantity`. In the - meantime, you can read [the `QuantityPoint` unit - tests](https://github.com/aurora-opensource/au/blob/main/au/quantity_point_test.cc) to see some - practical examples. +### Quantity point maker (preferred) {#quantity-point-maker} + +The preferred way to construct a `QuantityPoint` of a given unit is to use the _quantity point +maker_ for that unit. This is a callable whose name is the plural form of that unit, expressed in +"snake_case", and suffixed with `_pt` for "point": for example, `fahrenheit_pt`. When you pass +a raw numeric variable of type `T` to the quantity point maker, it returns a `QuantityPoint` of the +unit it represents, whose rep type is `T`. + +!!! example + `fahrenheit_pt(75)` returns a `QuantityPoint`. + +### Implicit constructor from another `QuantityPoint` {#implicit-from-quantity} + +This constructor performs a unit conversion. The result will represent the _same point_ as the +input, but expressed in the _units_ of the newly constructed object's type. It will also convert +the stored value to the _rep_ of the constructed object, if necessary. + +Here is the signature of the constructor, slightly simplified for illustration purposes. (We've +enclosed it in the class template definition to be clear about `Unit` and `Rep` in the discussion +that follows.) + +```cpp +template +class QuantityPoint { + + // Implicit constructor signature (for illustration purposes): + template + QuantityPoint(QuantityPoint other); +}; +``` + +This constructor only exists when this unit-and-rep conversion is both meaningful and safe. It can +fail to exist in several ways. + +1. If the input quantity has a **different dimension**, then the operation is intrinsically + meaningless and we forbid it. + +2. If the _constructed_ quantity's rep (that is, `Rep` in the code snippet above) is **not floating + point**, then we forbid any conversion that might produce a non-integer value. Examples include: + + a. When `OtherRep` _is floating point_, we forbid this conversion. + + b. When `UnitRatioT` is _not an integer_, we forbid this conversion. + + c. When the origin of `OtherUnit` is _additively offset_ from the origin of `Unit` by an amount + that can't be represented as an integer in the target units, `Unit`, we forbid this + conversion. + +Note that case `c` doesn't occur for `Quantity`; it is unique to `QuantityPoint`. + +We also inherit the "overflow safety surface" from the `Quantity` member inside of `QuantityPoint` +(discussed as point 3 of the [`Quantity` constructor docs](./quantity.md#implicit-from-quantity)). +This can prevent certain quantity point conversions which have excessive overflow risk. + +Here are several examples to illustrate the conditions under which implicit conversions are allowed. + + + +!!! example "Examples of `QuantityPoint` to `QuantityPoint` conversions" + | Source type | Target type | Implicit construction outcome | + |-------------|-------------|---------| + | `QuantityPoint` | `QuantityPoint` {: rowspan=3} | :cross_mark: **Forbidden:** `double` source not guaranteed to hold an integer value | + | `QuantityPoint, int>` | :cross_mark: **Forbidden:** point measured in $\text{mm}$ not generally an integer in $\text{m}$ | ⁠ {: style="padding:0"} | + | `QuantityPoint, int>` | :check_mark: **Permitted:** point measured in $\text{km}$ guaranteed to be integer in $\text{m}$ | ⁠ {: style="padding:0"} | + | `QuantityPoint` {: rowspan=3} | `QuantityPoint` | :cross_mark: **Forbidden:** Zero point offset from Kelvins to Celsius is $273.15\,\, \text{K}$, a non-integer number of Kelvins | + | `QuantityPoint` | :check_mark: **Permitted:** target Rep is floating point, and can represent offset of $273.15\,\, \text{K}$ | + | `QuantityPoint, int>` | :check_mark: **Permitted:** offset in target units is $273,\!150\,\, \text{mK}$, which is an integer | + +Note that every case in the above table is _physically_ meaningful (because the source and target +have the same dimension), but some conversions are forbidden due to the risk of larger-than-usual +errors. The library can still perform these conversions, but not via this constructor, and it must +be "forced" to do so. See [`.as(unit)`](#as) for more details. + +### Default constructor + +Here is the signature of the constructor, enclosed in the class template definition for context. + +```cpp +template +class QuantityPoint { + QuantityPoint(); +}; +``` + +A default-constructed `QuantityPoint` is initialized to some value, which helps avoid certain kinds +of memory safety bugs. However, **the value is contractually unspecified**. You can of course look +up that value by reading the source code, but we may change it in the future, and **we would not +consider this to be a breaking change**. The only valid operation on a default-constructed +`QuantityPoint` is to assign to it later on. + +## Extracting the stored value + +In order to access the raw numeric value stored inside of `QuantityPoint`, you must explicitly name +the unit at the callsite. There are two functions which can do this, depending on whether you want +to access by value or by reference. + +### By value: `.in(unit)` {#extracting-with-in} + +This function returns the underlying stored value, by value. See the [unit +slots](../discussion/idioms/unit-slots.md) discussion for valid choices for `unit`. + +??? example "Example: extracting `4.56` from `meters_pt(4.56)`" + Consider this `QuantityPoint`: + + ```cpp + auto p = meters_pt(4.56); + ``` + + You can retrieve the underlying value by writing either `p.in(meters_pt)` (passing the + `QuantityPointMaker`), or `p.in(Meters{})` (passing an instance of the unit). + +### By reference: `.data_in(unit)` + +This function returns a reference to the underlying stored value. See the [unit +slots](../discussion/idioms/unit-slots.md) discussion for valid choices for `unit`. + +??? example "Example: incrementing the underlying stored value" + ```cpp + auto temperature = celsius_pt(20); + temperature.data_in(celsius_pt)++; + ``` + + Since `temperature` is not `const`, the reference returned by `.data_in()` will be mutable, and + we can treat it like any other `int` lvalue. The above would result in `celsius_pt(21)`. + +## Performing unit conversions + +We have two methods for performing unit conversions. They have identical APIs, but their names are +different. `as` returns another `QuantityPoint`, but `in` exits the library and returns a raw +number. + +### `.as(unit)`, `.as(unit)` {#as} + +This function produces a new representation of the input `QuantityPoint`, converted to the new unit. +See the [unit slots](../discussion/idioms/unit-slots.md) discussion for valid choices for `unit`. + +??? example "Example: converting `meters_pt(3)` to `centi(meters_pt)`" + Consider this `QuantityPoint`: + + ```cpp + auto point = meters_pt(3); + ``` + + Then `point.as(centi(meters_pt))` re-expresses this quantity in units of centimeters. + Specifically, it returns a `QuantityPoint, int>`, which is equal to + `centi(meters_pt)(300)`. + + The above example used the quantity maker, `centi(meters_pt)`. One could also use an instance + of the unit type `Centi`, writing `point.as(Centi{})`. The former is generally + preferable; the latter is mainly useful in generic code where the unit type may be all you have. + +**Without** a template argument, `.as(unit)` obeys the same safety checks as for the [implicit +constructors](#implicit-from-quantity): conversions at high risk for integer overflow or truncation +are forbidden. Additionally, the `Rep` of the output is identical to the `Rep` of the input. + +**With** a template argument, `.as(unit)` has two differences. + +1. The output `Rep` will be `T`. +2. The conversion is considered "forcing", and will be permitted in spite of any overflow or + truncation risk. The semantics are similar to `static_cast`. + +??? example "Example: forcing a conversion from centimeters to meters" + `centi(meters_pt)(200).as(meters_pt)` is not allowed. This conversion will divide the + underlying value, `200`, by `100`. Now, it so happens that this _particular_ value _would_ + produce an integer result. However, the compiler must decide whether to permit this operation + _at compile time_, which means we don't yet know the value. Since most `int` values would _not_ + produce integer results, we forbid this. + + `centi(meters_pt)(200).as(meters_pt)` _is_ allowed. The "explicit rep" template parameter + has "forcing" semantics. This would produce `meters_pt(2)`. However, note that this operation + uses integer division, which truncates: so, for example, + `centi(meters_pt)(199).as(meters_pt)` would produce `meters_pt(1)`. + +!!! tip + Prefer to **omit** the template argument if possible, because you will get more safety checks. + The risks which the no-template-argument version warns about are real. + +### `.in(unit)`, `.in(unit)` + +This function produces the value of the `QuantityPoint`, re-expressed in the new unit. `unit` can +be either a `QuantityPointMaker` for the quantity's unit, or an instance of the unit itself. See the +[unit slots](../discussion/idioms/unit-slots.md) discussion for valid choices for `unit`. + +??? example "Example: getting the value of `meters_pt(3)` in `centi(meters_pt)`" + Consider this `QuantityPoint`: + + ```cpp + auto point = meters_pt(3); + ``` + + Then `point.in(centi(meters_pt))` converts this quantity to centimeters, and returns the value, + `300`, as an `int`. + + The above example used the quantity maker, `centi(meters_pt)`. One could also use an instance + of the unit type `Centi`, writing `point.in(Centi{})`. The former is generally + preferable; the latter is mainly useful in generic code where the unit type may be all you have. + +**Without** a template argument, `.in(unit)` obeys the same safety checks as for the [implicit +constructors](#implicit-from-quantity): conversions at high risk for integer overflow or truncation +are forbidden. Additionally, the `Rep` of the output is identical to the `Rep` of the input. + +**With** a template argument, `.in(unit)` has two differences. + +1. The output type will be `T`. +2. The conversion is considered "forcing", and will be permitted in spite of any overflow or + truncation risk. The semantics are similar to `static_cast`. + +??? example "Example: forcing a conversion from centimeters to meters" + `centi(meters_pt)(200).in(meters_pt)` is not allowed. This conversion will divide the + underlying value, `200`, by `100`. Now, it so happens that this _particular_ value _would_ + produce an integer result. However, the compiler must decide whether to permit this operation + _at compile time_, which means we don't yet know the value. Since most `int` values would _not_ + produce integer results, we forbid this. + + `centi(meters_pt)(200).in(meters_pt)` _is_ allowed. The "explicit rep" template parameter + has "forcing" semantics. This would produce `2`. However, note that this operation uses integer + division, which truncates: so, for example, `centi(meters_pt)(199).in(meters_pt)` would + produce `1`. + +!!! tip + Prefer to **omit** the template argument if possible, because you will get more safety checks. + The risks which the no-template-argument version warns about are real. + +## Operations + +Au includes as many common operations as possible. Our goal is to avoid incentivizing users to +leave the safety of the library. + +Recall that for `Quantity`, there are [two main categories](../discussion/concepts/arithmetic.md) of +operation: "arbitrary unit" operations, and "common unit" operations. However, `QuantityPoint` is +different. Since multiplication, division, and powers are generally meaningless, we don't have any +"arbitrary unit" operations: every operation is a ["common unit" +operation](../discussion/concepts/arithmetic.md#common-unit). + +### Comparison + +Comparison is a [common unit operation](../discussion/concepts/arithmetic.md#common-unit). If the +input `QuantityPoint` types are not identical --- same `Unit` _and_ `Rep` --- then we first convert +them to their [common type](#common-type). Then, we perform the comparison by delegating to the +comparison operator on the underlying values. + +We support the following binary comparison operators: + +- `==` +- `!=` +- `>` +- `>=` +- `<` +- `<=` + +### Addition + +Addition between any two `QuantityPoint` instances is not defined, because it is not meaningful --- +this is intrinsic to the [core design of quantity point +types](../discussion/concepts/quantity_point.md). Addition is only defined between +a `QuantityPoint` and a `Quantity` of the same dimension. + +Addition is a [common unit operation](../discussion/concepts/arithmetic.md#common-unit). If the +input `Quantity` and `QuantityPoint` types don't have the same `Unit` _and_ `Rep`, then we first +convert them to their [common types](#common-type) --- that is, we use the common unit and common +rep for each. The result is a `QuantityPoint` of this common unit and rep, whose value is the sum +of the input values (after conversions). + +### Subtraction {#subtraction} + +Subtraction is a [common unit operation](../discussion/concepts/arithmetic.md#common-unit). If the +input `QuantityPoint` types are not identical --- same `Unit` _and_ `Rep` --- then we first convert +them to their [common type](#common-type). The result is a `Quantity` --- note: **not** +a `QuantityPoint` --- of this common unit and Rep, whose value is the difference of the input values +(after any common type conversions). + +### Shorthand addition and subtraction (`+=`, `-=`) + +The input must be a `Quantity` which is [implicitly +convertible](./quantity.md#implicit-from-quantity) to the `Unit` and `Rep` of the target +`QuantityPoint` type. These operations first perform that conversion, and then replace the target +`QuantityPoint` with the result of that addition (for `+=`) or subtraction (for `-=`). + +## `rep_cast` + +`rep_cast` performs a `static_cast` on the underlying value of a `QuantityPoint`. It is used to +change the rep. + +Given any `QuantityPoint p` whose rep is `R`, then `rep_cast(p)` gives a `QuantityPoint`, whose underlying value is `static_cast(p.in(U{}))`. + +## Templates and traits + +### Matching: `typename U, typename R` + +To specialize a template to match any `QuantityPoint`, declare two template parameters: one for the +unit, one for the rep. + +??? example "Example: function template" + ```cpp + template + constexpr auto refine_scale(QuantityPoint p) { + return p.as(U{} / mag<10>()); + } + ``` + + This function template will match any `QuantityPoint` specialization, and nothing else. + +??? example "Example: type template" + ```cpp + // First, we need to declare the generic type template. It matches a single type, `T`. + template + struct Size; + + // Now we can declare a specialization that matches any `QuantityPoint`, by templating on its + // unit and rep. + template + struct Size> { + + // Note: this example uses inline variables, a C++17 feature. It's just for illustration. + static constexpr inline std::size_t value = sizeof(R); + }; + ``` + + In this way, `Size::value` will exist only when `T` is some `QuantityPoint` type (unless, of + course, other specializations get defined elsewhere). + +### `::Rep` + +Every `QuantityPoint` type has a public static alias, `::Rep`, which indicates its underlying +storage type (or, its "rep"). + +??? example + `decltype(meters_pt(5))::Rep` is `int`. + +### `::Unit` + +Every `QuantityPoint` type has a public static alias, `::Unit`, which indicates its unit type. + +??? example + `decltype(meters_pt(5))::Unit` is `Meters`. + +### `::unit` + +Every `QuantityPoint` type has a public static member variable, `::unit`, which is an instance of its +unit type. + +??? example + `decltype(meters_pt(5))::unit` is `Meters{}`. + +### `std::common_type` specialization {#common-type} + +For two `QuantityPoint` types, one with unit `U1` and rep `R1`, and the other with unit `U2` and rep +`R2`, then `std::common_type_t, QuantityPoint>` has the following properties. + +1. It exists if and only if `U1` and `U2` have the same dimension. + +2. When it exists, it is `QuantityPoint`, where `U` is the [common + point-unit](../discussion/concepts/common_unit.md#common-quantity-point) of `U1` and `U2`, and + `R` is `std::common_type_t`. + +As [required by the standard](https://en.cppreference.com/w/cpp/types/common_type), our +`std::common_type` specializations are +[SFINAE](https://en.cppreference.com/w/cpp/language/sfinae)-friendly: improper combinations will +simply not be present, rather than producing a hard error. + +### AreQuantityPointTypesEquivalent + +**Result:** Indicates whether two `QuantityPoint` types are equivalent. Equivalent types may be +freely converted to each other, and no arithmetic operations will be performed in doing so. + +More precisely, `QuantityPoint` and `QuantityPoint` are equivalent if and only if +**both** of the following conditions hold. + +1. The units `U1` and `U2` are [point-equivalent](./unit.md#point-equivalent). + +2. The reps `R1` and `R2` are the same type. + +**Syntax:** + +- For _types_ `U1` and `U2`: + - `AreQuantityPointTypesEquivalent::value` diff --git a/docs/reference/unit.md b/docs/reference/unit.md index 8198a13d..8788c4dc 100644 --- a/docs/reference/unit.md +++ b/docs/reference/unit.md @@ -292,7 +292,7 @@ For example, `Meters{} * Hertz{}` is not the _same unit_ as `Meters{} / Seconds{ - For _instances_ `u1` and `u2`: - `are_units_quantity_equivalent(u1, u2)` -### Are units point-equivalent? +### Are units point-equivalent? {#point-equivalent} **Result:** Indicates whether two units are point-equivalent. This means that they have the same dimension, same magnitude, _and_ same origin. `QuantityPoint` instances of point-equivalent units