diff --git a/src/core/include/mp-units/framework/value_cast.h b/src/core/include/mp-units/framework/value_cast.h index 1f92bcea..d3fb7445 100644 --- a/src/core/include/mp-units/framework/value_cast.h +++ b/src/core/include/mp-units/framework/value_cast.h @@ -80,7 +80,8 @@ template * * auto q = value_cast(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 requires Quantity> && (convertible(std::remove_reference_t::reference, ToU)) && @@ -92,6 +93,30 @@ template return detail::sudo_cast>(std::forward(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; + * auto q = value_cast(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 + requires Quantity> && (convertible(std::remove_reference_t::reference, ToQ::unit)) && + (ToQ::quantity_spec == std::remove_reference_t::quantity_spec) && + std::constructible_from::rep> +[[nodiscard]] constexpr Quantity auto value_cast(Q&& q) +{ + return detail::sudo_cast(std::forward(q)); +} + /** * @brief Explicit cast of a quantity point's unit * @@ -133,14 +158,15 @@ value_cast(QP&& 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(1.23 * ms); + * auto qp = value_cast(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 requires QuantityPoint> && (convertible(std::remove_reference_t::reference, ToU)) && @@ -152,4 +178,64 @@ template std::remove_reference_t::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; + * inline constexpr struct B : relative_point_origin B; + * + * using ToQP = quantity_point; + * auto qp = value_cast(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 + requires QuantityPoint> && (convertible(std::remove_reference_t::reference, ToQ::unit)) && + (ToQ::quantity_spec == std::remove_reference_t::quantity_spec) && + std::constructible_from::rep> +[[nodiscard]] constexpr QuantityPoint auto value_cast(QP&& qp) +{ + return quantity_point{value_cast(std::forward(qp).quantity_from_origin_is_an_implementation_detail_), + std::remove_reference_t::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; + * inline constexpr struct B : relative_point_origin B; + * + * using ToQP = quantity_point; + * auto qp = value_cast(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 + requires QuantityPoint> && + (convertible(std::remove_reference_t::reference, ToQP::unit)) && + (ToQP::quantity_spec == std::remove_reference_t::quantity_spec) && + (detail::same_absolute_point_origins(ToQP::point_origin, std::remove_reference_t::point_origin)) && + std::constructible_from::rep> +[[nodiscard]] constexpr QuantityPoint auto value_cast(QP&& qp) +{ + return quantity_point{ + value_cast(std::forward(qp).quantity_from_origin_is_an_implementation_detail_), + std::remove_reference_t::point_origin} + .point_for(ToQP::point_origin); +} + + } // namespace mp_units diff --git a/test/static/quantity_point_test.cpp b/test/static/quantity_point_test.cpp index acc53416..d03d3575 100644 --- a/test/static/quantity_point_test.cpp +++ b/test/static/quantity_point_test.cpp @@ -1692,4 +1692,25 @@ static_assert(value_cast(lvalue_qp).quantity_from_zero().numerical_value_ static_assert(value_cast(lvalue_qp).quantity_from_zero().numerical_value_in(m) == 2000.f); } // namespace lvalue_tests +static_assert(value_cast>(quantity_point{2000 * m}).quantity_from_zero().numerical_value_in(km) == 2); +static_assert(value_cast>(quantity_point{2000 * m}).quantity_from_zero().numerical_value_in(km) == + 2); +static_assert( + !requires(quantity_point qp) { value_cast>(qp); }, + "value_cast shall not cast between different quantity types"); +static_assert( + !requires(quantity_point qp) { value_cast>(qp); }, + "value_cast shall not cast between different quantity types"); +static_assert(value_cast>(quantity_point{2 * km}) + .quantity_ref_from(mean_sea_level) + .numerical_value_in(m) == 2042); +static_assert(value_cast>(quantity_point{ + std::int8_t{100} * mm}) + .quantity_ref_from(mean_sea_level) + .numerical_value_in(cm) == 4210); +static_assert(value_cast>(quantity_point{4210 * cm}) + .quantity_ref_from(ground_level) + .numerical_value_in(mm) == 100); + + } // namespace