feat: representation type template parameter added to value convertion functions

Resolves #588
This commit is contained in:
Mateusz Pusz
2024-07-04 22:05:40 +01:00
parent 5903e5661f
commit 2cff579650
5 changed files with 112 additions and 20 deletions

View File

@@ -148,30 +148,45 @@ Price price{12.95 * USD};
Scaled spx = value_cast<USD_s, std::int64_t>(price);
```
As a shortcut, instead of providing a unit and a representation type to `value_cast`, you may also provide a
`Quantity` type directly, from which unit and representation type are taken. However, `value_cast<Quantity>`,
still only allows for changes in unit and representation type, but not changing the type of the quantity.
For that, you will have to use a `quantity_cast` instead.
As a shortcut, instead of providing a unit and a representation type to `value_cast`, you may also
provide a `Quantity` type directly, from which unit and representation type are taken. However,
`value_cast<Quantity>`, still only allows for changes in unit and representation type, but not
changing the type of the quantity. For that, you will have to use a `quantity_cast` instead.
Overloads are also provided for instances of `quantity_point`. All variants of `value_cast<...>(q)`
that apply to instances of `quantity` have a corresponding version applicable to `quantity_point`,
where the `point_origin` remains untouched, and the cast changes how the "offset" from the origin
is represented. Specifically, for any `quantity_point` instance `qp`, all of the following
equivalences hold:
Overloads are also provided for instances of `quantity_point`.
All variants of `value_cast<...>(q)` that apply to instances of `quantity`
have a corresponding version applicable to `quantity_point`, where the `point_origin` remains untouched,
and the cast changes how the "offset" from the origin is represented.
Specifically, for any `quantity_point` instance `qp`, all of the following equivalences hold:
```cpp
static_assert( value_cast<Rep>(qp) == quantity_point{value_cast<Rep>(qp.quantity_from(qp.point_origin)), qp.point_origin} );
static_assert( value_cast<U>(qp) == quantity_point{value_cast<U>(qp.quantity_from(qp.point_origin)), qp.point_origin} );
static_assert( value_cast<U, Rep>(qp) == quantity_point{value_cast<U, Rep>(qp.quantity_from(qp.point_origin)), qp.point_origin} );
static_assert( value_cast<Q>(qp) == quantity_point{value_cast<Q>(qp.quantity_from(qp.point_origin)), qp.point_origin} );
static_assert(value_cast<Rep>(qp) == quantity_point{value_cast<Rep>(qp.quantity_from(qp.point_origin)), qp.point_origin});
static_assert(value_cast<U>(qp) == quantity_point{value_cast<U>(qp.quantity_from(qp.point_origin)), qp.point_origin});
static_assert(value_cast<U, Rep>(qp) == quantity_point{value_cast<U, Rep>(qp.quantity_from(qp.point_origin)), qp.point_origin});
static_assert(value_cast<Q>(qp) == quantity_point{value_cast<Q>(qp.quantity_from(qp.point_origin)), qp.point_origin});
```
Furthermore, there is one additional overload `value_cast<ToQP>(qp)`.
This overload permits to additionally replace the `point_origin` with another compatible one,
while still representing the same point in the affine space.
Thus, it is roughly equivalent to
Furthermore, there is one additional overload `value_cast<ToQP>(qp)`. This overload permits to
additionally replace the `point_origin` with another compatible one, while still representing
the same point in the affine space. Thus, it is roughly equivalent to
`value_cast<ToQP::unit, ToQP::rep>(qp).point_for(ToQP::point_origin)`.
In contrast to a separate `value_cast` followed by `point_for` (or vice-versa), the combined
`value_cast` tries to choose the order of the individual conversion steps in a way
to avoid both overflow and unnecessary loss of precision. Overflow is a risk because the change of origin point
`value_cast` tries to choose the order of the individual conversion steps in a way to avoid both
overflow and unnecessary loss of precision. Overflow is a risk because the change of origin point
may require an addition of a potentially large offset (the difference between the origin points),
which may well be outside the range of one or both quantity types.
## Value conversions summary
The table below provides all the value conversions functions that may be run on `x` being the
instance of either `quantity` or `quantity_point`:
| Forcing | Representation | Unit | Member function | Conversion function |
|:-------:|:--------------:|:----:|--------------------|-----------------------|
| No | Same | `u` | `x.in(u)` | |
| No | `T` | Same | `x.in<T>()` | |
| No | `T` | `u` | `x.in<T>(u)` | |
| Yes | Same | `u` | `x.force_in(u)` | `value_cast<u>(x)` |
| Yes | `T` | Same | `x.force_in<T>()` | `value_cast<T>(x)` |
| Yes | `T` | `u` | `x.force_in<T>(u)` | `value_cast<u, T>(x)` |