feat: value_cast<Unit, Representation> added

This commit is contained in:
Mateusz Pusz
2023-12-19 18:19:22 +01:00
parent 0db047803e
commit 436463b7a7
5 changed files with 121 additions and 9 deletions

View File

@ -6,6 +6,7 @@
- feat: `fma`, `isfinite`, `isinf`, and `isnan` math function added by @NAThompson - feat: `fma`, `isfinite`, `isinf`, and `isnan` math function added by @NAThompson
- feat: `quantity_point` support added for `quantity_cast` and `value_cast` - feat: `quantity_point` support added for `quantity_cast` and `value_cast`
- feat: `value_cast<Unit, Representation>` added
- (!) refactor: `zero_Fahrenheit` renamed to `zeroth_Fahrenheit` - (!) refactor: `zero_Fahrenheit` renamed to `zeroth_Fahrenheit`
- refactor: math functions constraints refactored - refactor: math functions constraints refactored
- fix: `QuantityLike` conversions required `Q::rep` instead of using one provided by `quantity_like_traits` - fix: `QuantityLike` conversions required `Q::rep` instead of using one provided by `quantity_like_traits`

View File

@ -83,7 +83,7 @@ Next, let's do the same for integral and floating-point representations, but thi
using US Customary units: using US Customary units:
```cpp title="avg_speed.cpp" linenums="54" ```cpp title="avg_speed.cpp" linenums="54"
--8<-- "example/avg_speed.cpp:93:125" --8<-- "example/avg_speed.cpp:93:124"
``` ```
One important difference here is the fact that as it is not possible to make a lossless conversion One important difference here is the fact that as it is not possible to make a lossless conversion
@ -108,8 +108,8 @@ Please note how the first and third results get truncated using integral represe
In the end, we repeat the scenario for CGS units: In the end, we repeat the scenario for CGS units:
```cpp title="avg_speed.cpp" linenums="87" ```cpp title="avg_speed.cpp" linenums="86"
--8<-- "example/avg_speed.cpp:127:157" --8<-- "example/avg_speed.cpp:126:155"
``` ```
Again, we observe `value_cast` being used in the same places and consistent truncation errors Again, we observe `value_cast` being used in the same places and consistent truncation errors
@ -129,6 +129,6 @@ Average speed of a car that makes 2.2e+07 cm in 7200 s is 110 km/h.
The example file ends with a simple `main()` function: The example file ends with a simple `main()` function:
```cpp title="avg_speed.cpp" linenums="118" ```cpp title="avg_speed.cpp" linenums="116"
--8<-- "example/avg_speed.cpp:159:" --8<-- "example/avg_speed.cpp:157:"
``` ```

View File

@ -77,3 +77,76 @@ quantity<si::metre, int> q3 = value_cast<int>(3.14 * m);
It is often fine to use an integral as a representation type, but in general, floating-point It is often fine to use an integral as a representation type, but in general, floating-point
types provide better precision and are privileged in the library as they are considered types provide better precision and are privileged in the library as they are considered
to be value-preserving. to be value-preserving.
In some cases, a unit and a representation type should be changed simultaneously. Moreover,
sometimes, the order of doing those operations matters. In such cases, the library provides
the `value_cast<U, Rep>(q)` which always returns the most precise result:
=== "C++23"
```cpp
inline constexpr struct dim_currency : base_dimension<"$"> {} dim_currency;
inline constexpr struct currency : quantity_spec<dim_currency> {} currency;
inline constexpr struct us_dollar : named_unit<"USD", kind_of<currency>> {} us_dollar;
inline constexpr struct scaled_us_dollar : named_unit<"USD_s", mag_power<10, -8> * us_dollar> {} scaled_us_dollar;
namespace unit_symbols {
inline constexpr auto USD = us_dollar;
inline constexpr auto USD_s = scaled_us_dollar;
} // namespace unit_symbols
using Price = quantity<currency[us_dollar], double>;
using Scaled = quantity<currency[scaled_us_dollar], std::int64_t>;
Price price = 12.95 * USD;
Scaled spx = value_cast<USD_s, std::int64_t>(price);
```
=== "C++20"
```cpp
inline constexpr struct dim_currency : base_dimension<"$"> {} dim_currency;
inline constexpr struct currency : quantity_spec<currency, dim_currency> {} currency;
inline constexpr struct us_dollar : named_unit<"USD", kind_of<currency>> {} us_dollar;
inline constexpr struct scaled_us_dollar : named_unit<"USD_s", mag_power<10, -8> * us_dollar> {} scaled_us_dollar;
namespace unit_symbols {
inline constexpr auto USD = us_dollar;
inline constexpr auto USD_s = scaled_us_dollar;
} // namespace unit_symbols
using Price = quantity<currency[us_dollar], double>;
using Scaled = quantity<currency[scaled_us_dollar], std::int64_t>;
Price price = 12.95 * USD;
Scaled spx = value_cast<USD_s, std::int64_t>(price);
```
=== "Portable"
```cpp
inline constexpr struct dim_currency : base_dimension<"$"> {} dim_currency;
QUANTITY_SPEC(currency, dim_currency);
inline constexpr struct us_dollar : named_unit<"USD", kind_of<currency>> {} us_dollar;
inline constexpr struct scaled_us_dollar : named_unit<"USD_s", mag_power<10, -8> * us_dollar> {} scaled_us_dollar;
namespace unit_symbols {
inline constexpr auto USD = us_dollar;
inline constexpr auto USD_s = scaled_us_dollar;
} // namespace unit_symbols
using Price = quantity<currency[us_dollar], double>;
using Scaled = quantity<currency[scaled_us_dollar], std::int64_t>;
Price price = 12.95 * USD;
Scaled spx = value_cast<USD_s, std::int64_t>(price);
```

View File

@ -118,8 +118,7 @@ void example()
// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed
// also it is not possible to make a lossless conversion of miles to meters on an integral type // also it is not possible to make a lossless conversion of miles to meters on an integral type
// (explicit cast needed) // (explicit cast needed)
print_result(distance, duration, print_result(distance, duration, fixed_int_si_avg_speed(value_cast<m, int>(distance), value_cast<int>(duration)));
fixed_int_si_avg_speed(value_cast<int>(distance.force_in(m)), value_cast<int>(duration)));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
print_result(distance, duration, avg_speed(distance, duration)); print_result(distance, duration, avg_speed(distance, duration));
} }
@ -148,8 +147,7 @@ void example()
// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed
// it is not possible to make a lossless conversion of centimeters to meters on an integral type // it is not possible to make a lossless conversion of centimeters to meters on an integral type
// (explicit cast needed) // (explicit cast needed)
print_result(distance, duration, print_result(distance, duration, fixed_int_si_avg_speed(value_cast<m, int>(distance), value_cast<int>(duration)));
fixed_int_si_avg_speed(value_cast<int>(distance.force_in(m)), value_cast<int>(duration)));
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
print_result(distance, duration, avg_speed(distance, duration)); print_result(distance, duration, avg_speed(distance, duration));

View File

@ -75,6 +75,26 @@ template<Representation ToRep, typename Q>
return detail::sudo_cast<quantity<std::remove_reference_t<Q>::reference, ToRep>>(std::forward<Q>(q)); return detail::sudo_cast<quantity<std::remove_reference_t<Q>::reference, ToRep>>(std::forward<Q>(q));
} }
/**
* @brief Explicit cast of a quantity's unit and representation type
*
* Implicit conversions between quantities of different types are allowed only for "safe"
* (e.g. non-truncating) conversion. In truncating cases an explicit cast have to be used.
*
* auto q = value_cast<us, int>(1.23 * ms);
*
* @tparam ToRep a representation type to use for a target quantity
*/
template<Unit auto ToU, Representation ToRep, typename Q>
requires Quantity<std::remove_cvref_t<Q>> && (convertible(std::remove_reference_t<Q>::reference, ToU)) &&
RepresentationOf<ToRep, std::remove_reference_t<Q>::quantity_spec.character> &&
std::constructible_from<ToRep, typename std::remove_reference_t<Q>::rep>
[[nodiscard]] constexpr Quantity auto value_cast(Q&& q)
{
using q_type = std::remove_reference_t<Q>;
return detail::sudo_cast<quantity<q_type::quantity_spec[ToU], ToRep>>(std::forward<Q>(q));
}
/** /**
* @brief Explicit cast of a quantity point's unit * @brief Explicit cast of a quantity point's unit
* *
@ -112,4 +132,24 @@ template<Representation ToRep, typename QP>
return {value_cast<ToRep>(std::forward<QP>(qp).quantity_from_origin_is_an_implementation_detail_), qp.point_origin}; return {value_cast<ToRep>(std::forward<QP>(qp).quantity_from_origin_is_an_implementation_detail_), qp.point_origin};
} }
/**
* @brief Explicit cast of a quantity's unit and representation type
*
* Implicit conversions between quantities of different types are allowed only for "safe"
* (e.g. non-truncating) conversion. In truncating cases an explicit cast have to be used.
*
* auto q = value_cast<us, int>(1.23 * ms);
*
* @tparam ToRep a representation type to use for a target quantity
*/
template<Unit auto ToU, Representation ToRep, typename QP>
requires QuantityPoint<std::remove_cvref_t<QP>> && (convertible(std::remove_reference_t<QP>::reference, ToU)) &&
RepresentationOf<ToRep, std::remove_reference_t<QP>::quantity_spec.character> &&
std::constructible_from<ToRep, typename std::remove_reference_t<QP>::rep>
[[nodiscard]] constexpr QuantityPoint auto value_cast(QP&& qp)
{
return quantity_point{value_cast<ToU, ToRep>(std::forward<QP>(qp).quantity_from_origin_is_an_implementation_detail_),
qp.point_origin};
}
} // namespace mp_units } // namespace mp_units