Skip to content

Commit

Permalink
implement value_cast<ToQ> and value_cast<ToQP>
Browse files Browse the repository at this point in the history
  • Loading branch information
burnpanck committed May 10, 2024
1 parent 15404cd commit 5b6df8b
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 4 deletions.
95 changes: 91 additions & 4 deletions src/core/include/mp-units/framework/value_cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ template<Representation ToRep, typename Q>
*
* auto q = value_cast<us, int>(1.23 * ms);
*
* @tparam ToRep a representation type to use for a target quantity
* @tparam ToU a unit to use for the target quantity
* @tparam ToRep a representation type to use for the 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)) &&
Expand All @@ -92,6 +93,31 @@ template<Unit auto ToU, Representation ToRep, typename Q>
return detail::sudo_cast<quantity<detail::make_reference(q_type::quantity_spec, ToU), ToRep>>(std::forward<Q>(q));
}


/**
* @brief Explicit cast of a quantity's representation
*
* 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.
*
* using ToQ = quantity<us, int>;
* auto q = value_cast<ToQ>(1.23 * ms);
*
* Note that value_cast only changes the "representation aspects" (unit and representation
* type), but not the "meaning" (quantity type).
*
* @tparam ToQ a target quantity type to which to cast the representation
*/
template<Quantity ToQ, typename Q>
requires Quantity<std::remove_cvref_t<Q>> && (convertible(std::remove_reference_t<Q>::reference, ToQ::unit)) &&
(ToQ::quantity_spec == std::remove_reference_t<Q>::quantity_spec) &&
std::constructible_from<ToQ::rep, 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<ToQ>(std::forward<Q>(q));
}

/**
* @brief Explicit cast of a quantity point's unit
*
Expand Down Expand Up @@ -131,14 +157,15 @@ template<Representation ToRep, typename QP>
}

/**
* @brief Explicit cast of a quantity's unit and representation type
* @brief Explicit cast of a quantity point'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);
* auto qp = value_cast<us, int>(quantity_point{1.23 * ms});
*
* @tparam ToRep a representation type to use for a target quantity
* @tparam ToU a unit to use for the target quantity
* @tparam ToRep a representation type to use for the 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)) &&
Expand All @@ -150,4 +177,64 @@ template<Unit auto ToU, Representation ToRep, typename QP>
qp.point_origin};
}

/**
* @brief Explicit cast of a quantity point's representation
*
* 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.
*
* inline constexpr struct A : absolute_point_origin<A, isq::distance> A;
* inline constexpr struct B : relative_point_origin<A + 1*m> B;
*
* using ToQP = quantity_point<mm, B, int>;
* auto qp = value_cast<ToQP>(quantity_point{1.23 * m});
*
* Note that value_cast only changes the "representation aspects" (unit and representation
* type), but not the "meaning" (quantity type or the actual point that is being described).
*
* @tparam ToQ a target quantity type to which to cast the representation of the point
*/
template<Quantity ToQ, typename QP>
requires QuantityPoint<std::remove_cvref_t<QP>> && (convertible(std::remove_reference_t<QP>::reference, ToQ::unit)) &&
(ToQ::quantity_spec == std::remove_reference_t<QP>::quantity_spec) &&
std::constructible_from<ToQ::rep, typename std::remove_reference_t<QP>::rep>
[[nodiscard]] constexpr QuantityPoint auto value_cast(QP&& qp)
{
return quantity_point{value_cast<ToQ>(std::forward<QP>(qp).quantity_from_origin_is_an_implementation_detail_),
qp.point_origin};
}

/**
* @brief Explicit cast of a quantity point's representation, including potentially the point origin
*
* 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.
*
* inline constexpr struct A : absolute_point_origin<A, isq::distance> A;
* inline constexpr struct B : relative_point_origin<A + 1*m> B;
*
* using ToQP = quantity_point<mm, B, int>;
* auto qp = value_cast<ToQP>(quantity_point{1.23 * m});
*
* Note that value_cast only changes the "representation aspects" (unit, representation
* type and point origin), but not the "meaning" (quantity type or the actual point that is
* being described).
*
* @tparam ToQP a target quantity point type to which to cast the representation of the point
*/
template<QuantityPoint ToQP, typename QP>
requires QuantityPoint<std::remove_cvref_t<QP>> &&
(convertible(std::remove_reference_t<QP>::reference, ToQP::unit)) &&
(ToQP::quantity_spec == std::remove_reference_t<QP>::quantity_spec) &&
(same_absolute_point_origins(ToQP::point_origin, std::remove_reference_t<QP>::point_origin)) &&
std::constructible_from<ToQP::rep, typename std::remove_reference_t<QP>::rep>
[[nodiscard]] constexpr QuantityPoint auto value_cast(QP&& qp)
{
return quantity_point{
value_cast<typename ToQP::quantity_type>(std::forward<QP>(qp).quantity_from_origin_is_an_implementation_detail_),
qp.point_origin}
.point_for(ToQP::point_origin);
}


} // namespace mp_units
29 changes: 29 additions & 0 deletions test/static/quantity_point_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1677,4 +1677,33 @@ static_assert(invalid_addition(5 * isq::activity[Bq], 10 / (2 * isq::time[s]), q
static_assert(invalid_subtraction(quantity_point{5 * isq::activity[Bq]}, 10 / (2 * isq::time[s]),
5 * isq::frequency[Hz]));


// value_cast

static_assert(value_cast<m>(quantity_point{2 * km}).quantity_from_zero().numerical_value_in(m) == 2000);
static_assert(value_cast<km>(quantity_point{2000 * m}).quantity_from_zero().numerical_value_in(km) == 2);
static_assert(value_cast<int>(quantity_point{1.23 * m}).quantity_from_zero().numerical_value_in(m) == 1);
static_assert(
value_cast<km / h>(quantity_point{2000.0 * m / (3600.0 * s)}).quantity_from_zero().numerical_value_in(km / h) == 2);
static_assert(value_cast<quantity<km, int>>(quantity_point{2000 * m}).quantity_from_zero().numerical_value_in(km) == 2);
static_assert(value_cast<quantity_point<km>>(quantity_point{2000 * m}).quantity_from_zero().numerical_value_in(km) ==
2);
static_assert(
!requires(quantity_point<isq::width[m]> qp) { value_cast<quantity<m>>(qp); },
"value_cast shall not cast between different quantity types");
static_assert(
!requires(quantity_point<m> qp) { value_cast<quantity<isq::width[m]>>(qp); },
"value_cast shall not cast between different quantity types");
static_assert(value_cast<quantity_point<m, mean_sea_level>>(quantity_point<km, ground_level>{2 * km})
.quantity_ref_from(mean_sea_level)
.numerical_value_in(m) == 2042);
static_assert(value_cast<quantity_point<cm, mean_sea_level, int>>(quantity_point<mm, ground_level, std::int8_t>{
std::int8_t{100} * mm})
.quantity_ref_from(mean_sea_level)
.numerical_value_in(cm) == 4210);
static_assert(value_cast<quantity_point<mm, ground_level, std::int8_t>>(quantity_point<cm, mean_sea_level>{4210 * cm})
.quantity_ref_from(ground_level)
.numerical_value_in(mm) == 100);


} // namespace
7 changes: 7 additions & 0 deletions test/static/quantity_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,13 @@ static_assert(value_cast<m>(2 * km).numerical_value_in(m) == 2000);
static_assert(value_cast<km>(2000 * m).numerical_value_in(km) == 2);
static_assert(value_cast<int>(1.23 * m).numerical_value_in(m) == 1);
static_assert(value_cast<km / h>(2000.0 * m / (3600.0 * s)).numerical_value_in(km / h) == 2);
static_assert(value_cast<quantity<km, int>>(2000 * m).numerical_value_in(km) == 2);
static_assert(
!requires(quantity<isq::width[m]> qp) { value_cast<quantity<m>>(qp); },
"value_cast shall not cast between different quantity types");
static_assert(
!requires(quantity<m> qp) { value_cast<quantity<isq::width[m]>>(qp); },
"value_cast shall not cast between different quantity types");

static_assert((2 * km).force_in(m).numerical_value_in(m) == 2000);
static_assert((2000 * m).force_in(km).numerical_value_in(km) == 2);
Expand Down

0 comments on commit 5b6df8b

Please sign in to comment.