diff --git a/src/core/include/mp-units/bits/magnitude.h b/src/core/include/mp-units/bits/magnitude.h index 6f9e238a..c4d23b31 100644 --- a/src/core/include/mp-units/bits/magnitude.h +++ b/src/core/include/mp-units/bits/magnitude.h @@ -719,6 +719,16 @@ template } } +template +[[nodiscard]] consteval auto common_magnitude_type_impl(magnitude) +{ + return (... * decltype(get_base(Ms)){}) * std::intmax_t{}; +} + +// Returns the most precise type to express the magnitude factor +template +using common_magnitude_type = decltype(common_magnitude_type_impl(M)); + } // namespace detail //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -764,6 +774,7 @@ struct prime_factorization<1> { template inline constexpr auto prime_factorization_v = prime_factorization::value; + } // namespace detail /** diff --git a/src/core/include/mp-units/bits/quantity_cast.h b/src/core/include/mp-units/bits/quantity_cast.h index 61047592..712ea041 100644 --- a/src/core/include/mp-units/bits/quantity_cast.h +++ b/src/core/include/mp-units/bits/quantity_cast.h @@ -22,22 +22,12 @@ #pragma once -#include -#include #include -#include -#include -#include -#include -#include #include -#include +#include namespace mp_units { -template Rep> -class quantity; - /** * @brief Explicit cast of a quantity type * @@ -56,11 +46,10 @@ class quantity; * @tparam ToQS a quantity specification to use for a target quantity */ template - requires Quantity> && (castable(std::remove_cvref_t::quantity_spec, ToQS)) [[nodiscard]] constexpr Quantity auto quantity_cast(Q&& q) + requires Quantity> && (castable(q.quantity_spec, ToQS)) { - constexpr reference::unit> r; - return std::forward(q).number() * r; + return make_quantity{}>(std::forward(q).number()); } } // namespace mp_units diff --git a/src/core/include/mp-units/bits/sudo_cast.h b/src/core/include/mp-units/bits/sudo_cast.h index 1195d309..a950ad51 100644 --- a/src/core/include/mp-units/bits/sudo_cast.h +++ b/src/core/include/mp-units/bits/sudo_cast.h @@ -28,67 +28,71 @@ #include #include -namespace mp_units { +namespace mp_units::detail { -template Rep> -class quantity; +// determines the best available representation type +template +[[nodiscard]] consteval auto common_rep_type(From, To) +{ + if constexpr (requires { typename std::common_type_t; }) + // returns a common type of two representation types if available + // i.e. `double` and `int` will end up with `double` precision + return std::common_type_t{}; + else + return typename From::rep{}; +} -namespace detail { +// determines the value type used by the representation type +template +[[nodiscard]] consteval auto rep_value_type(Rep) +{ + if constexpr (requires { typename Rep::value_type; }) + return typename Rep::value_type{}; + else if constexpr (requires { typename Rep::element_type; }) + return typename Rep::element_type{}; + else + return Rep{}; +} /** - * @brief Explicit cast of entire quantity + * @brief Explicit cast between different quantity types * - * @note This is too powerful to be used by users. + * @note This is a low-level facility and is too powerful to be used by the users directly. They should either use + * `value_cast` or `quantity_cast`. * * @tparam To a target quantity type to cast to */ -template - requires(castable(get_quantity_spec(R), To::quantity_spec)) && - ((get_unit(R) == To::unit && std::constructible_from) || - (get_unit(R) != To::unit)) // && scalable_with_)) +template + requires Quantity> && + (castable(std::remove_reference_t::quantity_spec, To::quantity_spec)) && + ((std::remove_reference_t::unit == To::unit && + std::constructible_from::rep>) || + (std::remove_reference_t::unit != To::unit)) // && scalable_with_)) // TODO how to constrain the second part here? -[[nodiscard]] constexpr Quantity auto sudo_cast(const quantity& q) +[[nodiscard]] constexpr Quantity auto sudo_cast(From&& q) { - if constexpr (get_unit(R) == To::unit) { + if constexpr (q.unit == To::unit) { // no scaling of the number needed - return static_cast(q.number()) * To::reference; // this is the only (and recommended) way to do + return make_quantity( + static_cast(std::forward(q).number())); // this is the only (and recommended) way to do // a truncating conversion on a number, so we are // using static_cast to suppress all the compiler // warnings on conversions } else { // scale the number - using rep_type = decltype([] { - // determines the best representation type - if constexpr (requires { typename std::common_type_t; }) - // returns a common type of two representation types if available - // i.e. `double` and `int` will end up with `double` precision - return std::common_type_t{}; - else - return Rep{}; - }()); - using multiplier_type = decltype([] { - // widen the type to prevent overflows - using wider_type = decltype(rep_type{} * std::intmax_t{}); - // check if `wider_type` supports scaling operations - if constexpr (requires(wider_type v) { v* v / v; }) - // if the `wider_type` can handle scaling operations then use it to improve accuracy - return wider_type{}; - else - // needed for example for linear algebra where `op/` on matrix types is not available - return std::intmax_t{}; - }()); - - constexpr Magnitude auto c_mag = - detail::get_canonical_unit(get_unit(R)).mag / detail::get_canonical_unit(To::unit).mag; + constexpr Magnitude auto c_mag = get_canonical_unit(q.unit).mag / get_canonical_unit(To::unit).mag; constexpr Magnitude auto num = numerator(c_mag); constexpr Magnitude auto den = denominator(c_mag); constexpr Magnitude auto irr = c_mag * (den / num); - + using c_mag_type = common_magnitude_type; + using c_rep_type = decltype(common_rep_type(q, To{})); + using v_type = decltype(rep_value_type(c_rep_type{})); + using multiplier_type = decltype(v_type{} * c_mag_type{}); constexpr auto val = [](Magnitude auto m) { return get_value(m); }; - return static_cast(static_cast(q.number()) * val(num) / val(den) * val(irr)) * + return static_cast(static_cast(std::forward(q).number()) * val(num) / val(den) * + val(irr)) * To::reference; } } -} // namespace detail -} // namespace mp_units +} // namespace mp_units::detail diff --git a/src/core/include/mp-units/bits/value_cast.h b/src/core/include/mp-units/bits/value_cast.h index 249d4629..92880e16 100644 --- a/src/core/include/mp-units/bits/value_cast.h +++ b/src/core/include/mp-units/bits/value_cast.h @@ -23,16 +23,13 @@ #pragma once #include -#include #include #include #include +#include namespace mp_units { -template Rep> -class quantity; - /** * @brief Explicit cast of a quantity's unit * @@ -43,17 +40,19 @@ class quantity; * * @tparam ToU a unit to use for a target quantity */ -template - requires(convertible(get_unit(R), ToU)) -[[nodiscard]] constexpr Quantity auto value_cast(const quantity& q) +template +[[nodiscard]] constexpr Quantity auto value_cast(Q&& q) + requires Quantity> && (convertible(q.reference, ToU)) { - if constexpr (detail::is_specialization_of_reference> || - !AssociatedUnit>) { - constexpr reference::quantity_spec, ToU> r; - return detail::sudo_cast>(q); - } else { - return detail::sudo_cast>(q); - } + using q_type = std::remove_reference_t; + constexpr auto r = [] { + if constexpr (detail::is_specialization_of_reference> || + !AssociatedUnit>) + return reference{}; + else + return ToU; + }(); + return detail::sudo_cast>(std::forward(q)); } /** @@ -66,11 +65,13 @@ template * * @tparam ToRep a representation type to use for a target quantity */ -template - requires RepresentationOf && std::constructible_from -[[nodiscard]] constexpr Quantity auto value_cast(const quantity& q) +template + requires Quantity> && + RepresentationOf::quantity_spec.character> && + std::constructible_from::rep> +[[nodiscard]] constexpr quantity::reference, ToRep> value_cast(Q&& q) { - return detail::sudo_cast>(q); + return detail::sudo_cast>(std::forward(q)); } } // namespace mp_units