From 760d48502dc9f364a8bb786ba5c82842c333947b Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 5 Jun 2024 08:24:33 +0200 Subject: [PATCH] refactor: unit-related `constexpr` functions compile-time evaluation limited to only one that determines the return type --- src/core/include/mp-units/bits/sudo_cast.h | 9 +- .../include/mp-units/framework/quantity.h | 5 +- src/core/include/mp-units/framework/unit.h | 169 +++++++----------- .../mp-units/framework/unit_concepts.h | 13 +- .../include/mp-units/systems/si/chrono.h | 6 +- 5 files changed, 79 insertions(+), 123 deletions(-) diff --git a/src/core/include/mp-units/bits/sudo_cast.h b/src/core/include/mp-units/bits/sudo_cast.h index 322423b4..ae3aa781 100644 --- a/src/core/include/mp-units/bits/sudo_cast.h +++ b/src/core/include/mp-units/bits/sudo_cast.h @@ -64,10 +64,11 @@ template // warnings on conversions } else { // scale the number - 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); + constexpr Magnitude auto c_mag = + decltype(decltype(get_canonical_unit(q_unit))::mag / decltype(get_canonical_unit(To::unit))::mag){}; + constexpr Magnitude auto num = decltype(numerator(c_mag)){}; + constexpr Magnitude auto den = decltype(denominator(c_mag)){}; + constexpr Magnitude auto irr = decltype(c_mag * decltype(den / num){}){}; using c_rep_type = maybe_common_type::rep, typename To::rep>; using c_mag_type = common_magnitude_type; using multiplier_type = conditional< diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index fb63eb82..2822ac87 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -46,8 +46,9 @@ namespace mp_units { namespace detail { template -concept IntegralConversionFactor = Unit && Unit && - is_integral(get_canonical_unit(UFrom).mag / get_canonical_unit(UTo).mag); +concept IntegralConversionFactor = + Unit && Unit && + is_integral(decltype(decltype(get_canonical_unit(UFrom))::mag / decltype(get_canonical_unit(UTo))::mag){}); template concept QuantityConvertibleTo = diff --git a/src/core/include/mp-units/framework/unit.h b/src/core/include/mp-units/framework/unit.h index 03dde4f0..c8ea6a98 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -321,22 +321,12 @@ struct is_one : std::true_type {}; * @tparam U a unit to use as a `reference_unit` * @tparam M a Magnitude representing an absolute scaling factor of this unit */ -template +template struct canonical_unit { - M mag; - U reference_unit; + static constexpr auto mag = M; + static constexpr auto reference_unit = U; }; -#if MP_UNITS_COMP_CLANG - -template -canonical_unit(M, U) -> canonical_unit; - -#endif - -template -[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit&); - template [[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit&); @@ -352,64 +342,61 @@ template template [[nodiscard]] consteval auto get_canonical_unit_impl(T, const scaled_unit&) { - auto base = get_canonical_unit_impl(U{}, U{}); - return canonical_unit{M * base.mag, base.reference_unit}; -} - -template -[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit&) -{ - return canonical_unit{mag<1>, t}; + using base = decltype(get_canonical_unit_impl(U{}, U{})); + return canonical_unit{}; } template -[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit&) +[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit&) { - return canonical_unit{mag<1>, t}; + return canonical_unit, T{}>{}; } template [[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit&) { - return get_canonical_unit_impl(U, U); + return decltype(get_canonical_unit_impl(U, U)){}; } template [[nodiscard]] consteval auto get_canonical_unit_impl(const power&, const type_list&) { - auto mag = (mp_units::mag<1> * ... * pow(get_canonical_unit_impl(Us{}, Us{}).mag)); - auto u = (one * ... * pow(get_canonical_unit_impl(Us{}, Us{}).reference_unit)); - return canonical_unit{mag, u}; + using mag = decltype((mp_units::mag<1> * ... * pow(decltype(get_canonical_unit_impl(Us{}, Us{}))::mag))); + using u = decltype((one * ... * pow(decltype(get_canonical_unit_impl(Us{}, Us{}))::reference_unit))); + return canonical_unit{}; } template [[nodiscard]] consteval auto get_canonical_unit_impl(T, const power&) { - auto base = get_canonical_unit_impl(F{}, F{}); - if constexpr (requires { typename decltype(base.reference_unit)::_num_; }) { - auto num = get_canonical_unit_impl(power{}, typename decltype(base.reference_unit)::_num_{}); - auto den = get_canonical_unit_impl(power{}, typename decltype(base.reference_unit)::_den_{}); - return canonical_unit{pow(base.mag) * num.mag / den.mag, num.reference_unit / den.reference_unit}; + using base = decltype(get_canonical_unit_impl(F{}, F{})); + if constexpr (requires { typename decltype(base::reference_unit)::_num_; }) { + using num = + decltype(get_canonical_unit_impl(power{}, typename decltype(base::reference_unit)::_num_{})); + using den = + decltype(get_canonical_unit_impl(power{}, typename decltype(base::reference_unit)::_den_{})); + return canonical_unit(base::mag) * num::mag){} / den::mag){}, + decltype(num::reference_unit / den::reference_unit){}>{}; } else { - return canonical_unit{pow(base.mag), - derived_unit>{}}; + return canonical_unit(base::mag)){}, + derived_unit, Num, Den...>>{}>{}; } } template [[nodiscard]] consteval auto get_canonical_unit_impl(const type_list&) { - auto m = (mp_units::mag<1> * ... * get_canonical_unit_impl(Us{}, Us{}).mag); - auto u = (one * ... * get_canonical_unit_impl(Us{}, Us{}).reference_unit); - return canonical_unit{m, u}; + using m = decltype((mp_units::mag<1> * ... * decltype(get_canonical_unit_impl(Us{}, Us{}))::mag)); + using u = decltype((one * ... * decltype(get_canonical_unit_impl(Us{}, Us{}))::reference_unit)); + return canonical_unit{}; } template [[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit&) { - auto num = get_canonical_unit_impl(typename derived_unit::_num_{}); - auto den = get_canonical_unit_impl(typename derived_unit::_den_{}); - return canonical_unit{num.mag / den.mag, num.reference_unit / den.reference_unit}; + using num = decltype(get_canonical_unit_impl(typename derived_unit::_num_{})); + using den = decltype(get_canonical_unit_impl(typename derived_unit::_den_{})); + return canonical_unit{}; } template @@ -422,7 +409,10 @@ using type_list_of_unit_less = expr_less; // TODO this should really be in the `details` namespace but is used in `chrono.h` (a part of mp_units.systems) // Even though it is not exported, it is visible to the other module via ADL -[[nodiscard]] consteval auto get_canonical_unit(Unit auto u) { return detail::get_canonical_unit_impl(u, u); } +[[nodiscard]] consteval auto get_canonical_unit(Unit auto u) +{ + return decltype(detail::get_canonical_unit_impl(u, u)){}; +} MP_UNITS_EXPORT_BEGIN @@ -448,7 +438,7 @@ template template [[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator/(M mag, U u) { - return mag * inverse(u); + return decltype(mag * inverse(u)){}; } /** @@ -460,13 +450,13 @@ template [[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator*(Lhs lhs, Rhs rhs) { if constexpr (detail::is_specialization_of_scaled_unit && detail::is_specialization_of_scaled_unit) - return (Lhs::mag * Rhs::mag) * (Lhs::reference_unit * Rhs::reference_unit); + return decltype(Lhs::mag * Rhs::mag){} * decltype(Lhs::reference_unit * Rhs::reference_unit){}; else if constexpr (detail::is_specialization_of_scaled_unit) - return Lhs::mag * (Lhs::reference_unit * rhs); + return decltype(Lhs::mag * decltype(Lhs::reference_unit * rhs){}){}; else if constexpr (detail::is_specialization_of_scaled_unit) - return Rhs::mag * (lhs * Rhs::reference_unit); + return decltype(Rhs::mag * decltype(lhs * Rhs::reference_unit){}){}; else - return detail::expr_multiply(lhs, rhs); + return decltype(detail::expr_multiply(lhs, rhs)){}; } /** @@ -478,69 +468,36 @@ template [[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator/(Lhs lhs, Rhs rhs) { if constexpr (detail::is_specialization_of_scaled_unit && detail::is_specialization_of_scaled_unit) - return (Lhs::mag / Rhs::mag) * (Lhs::reference_unit / Rhs::reference_unit); + return decltype(Lhs::mag / Rhs::mag){} * decltype(Lhs::reference_unit / Rhs::reference_unit){}; else if constexpr (detail::is_specialization_of_scaled_unit) - return Lhs::mag * (Lhs::reference_unit / rhs); + return Lhs::mag * decltype(Lhs::reference_unit / rhs){}; else if constexpr (detail::is_specialization_of_scaled_unit) - return mag<1> / Rhs::mag * (lhs / Rhs::reference_unit); + return decltype(mag<1> / Rhs::mag){} * decltype(lhs / Rhs::reference_unit){}; else return detail::expr_divide(lhs, rhs); } -[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto inverse(Unit auto u) { return one / u; } +[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto inverse(Unit auto u) { return decltype(one / u){}; } MP_UNITS_EXPORT_END namespace detail { -[[nodiscard]] consteval bool have_same_canonical_reference_unit_impl(...) { return false; } - -template -[[nodiscard]] consteval bool have_same_canonical_reference_unit_impl(const named_unit&, - const named_unit&) +[[nodiscard]] consteval auto have_same_canonical_reference_unit(Unit auto u1, Unit auto u2) { - return true; -} - -template -[[nodiscard]] consteval bool have_same_canonical_reference_unit_impl(const power&, const power&) -{ - return have_same_canonical_reference_unit_impl(F1{}, F2{}); -} - -template - requires(sizeof...(Us1) == sizeof...(Us2)) -[[nodiscard]] consteval bool have_same_canonical_reference_unit_impl(const type_list&, const type_list&) -{ - return (... && have_same_canonical_reference_unit_impl(Us1{}, Us2{})); -} - -template -[[nodiscard]] consteval bool have_same_canonical_reference_unit_impl(const derived_unit&, - const derived_unit&) -{ - return have_same_canonical_reference_unit_impl(typename derived_unit::_num_{}, - typename derived_unit::_num_{}) && - have_same_canonical_reference_unit_impl(typename derived_unit::_den_{}, - typename derived_unit::_den_{}); -} - -[[nodiscard]] consteval bool have_same_canonical_reference_unit(Unit auto u1, Unit auto u2) -{ - auto canonical_lhs = get_canonical_unit(u1); - auto canonical_rhs = get_canonical_unit(u2); - return have_same_canonical_reference_unit_impl(canonical_lhs.reference_unit, canonical_rhs.reference_unit); + using canonical_lhs = decltype(get_canonical_unit(u1)); + using canonical_rhs = decltype(get_canonical_unit(u2)); + return std::is_same{}; } } // namespace detail -MP_UNITS_EXPORT [[nodiscard]] consteval bool operator==(Unit auto lhs, Unit auto rhs) +MP_UNITS_EXPORT template +[[nodiscard]] consteval bool operator==(U1 lhs, U2 rhs) { - auto canonical_lhs = get_canonical_unit(lhs); - auto canonical_rhs = get_canonical_unit(rhs); - return detail::have_same_canonical_reference_unit(canonical_lhs.reference_unit, canonical_rhs.reference_unit) && - canonical_lhs.mag == canonical_rhs.mag; + return decltype(detail::have_same_canonical_reference_unit(lhs, rhs))::value && + decltype(get_canonical_unit(lhs))::mag == decltype(get_canonical_unit(rhs))::mag; } namespace detail { @@ -575,7 +532,7 @@ template else if constexpr (detail::is_specialization_of_scaled_unit) return scaled_unit(U::mag), decltype(pow(U::reference_unit))>{}; else if constexpr (detail::is_specialization_of_derived_unit) - return detail::expr_pow(u); + return decltype(detail::expr_pow(u)){}; else if constexpr (Den == 1) return derived_unit>{}; else @@ -589,7 +546,7 @@ template * * @return Unit The result of computation */ -[[nodiscard]] consteval Unit auto sqrt(Unit auto u) { return pow<1, 2>(u); } +[[nodiscard]] consteval Unit auto sqrt(Unit auto u) { return decltype(pow<1, 2>(u)){}; } /** * @brief Computes the cubic root of a unit @@ -598,7 +555,7 @@ template * * @return Unit The result of computation */ -[[nodiscard]] consteval Unit auto cbrt(Unit auto u) { return pow<1, 3>(u); } +[[nodiscard]] consteval Unit auto cbrt(Unit auto u) { return decltype(pow<1, 3>(u)){}; } /** * @brief Computes the square power of a unit @@ -607,7 +564,7 @@ template * * @return Unit The result of computation */ -[[nodiscard]] consteval Unit auto square(Unit auto u) { return pow<2>(u); } +[[nodiscard]] consteval Unit auto square(Unit auto u) { return decltype(pow<2>(u)){}; } /** * @brief Computes the cubic power of a unit @@ -616,7 +573,7 @@ template * * @return Unit The result of computation */ -[[nodiscard]] consteval Unit auto cubic(Unit auto u) { return pow<3>(u); } +[[nodiscard]] consteval Unit auto cubic(Unit auto u) { return decltype(pow<3>(u)){}; } // common dimensionless units @@ -631,7 +588,7 @@ inline constexpr auto ppm = parts_per_million; // convertible_to [[nodiscard]] consteval bool convertible(Unit auto from, Unit auto to) { - return detail::have_same_canonical_reference_unit(from, to); + return decltype(detail::have_same_canonical_reference_unit(from, to))::value; } // Common unit @@ -639,7 +596,7 @@ inline constexpr auto ppm = parts_per_million; template [[nodiscard]] consteval Unit auto common_unit(U1 u1, U2 u2) - requires(detail::have_same_canonical_reference_unit(u1, u2)) + requires(decltype(detail::have_same_canonical_reference_unit(u1, u2))::value) { if constexpr (U1{} == U2{}) { if constexpr (std::derived_from) @@ -648,18 +605,18 @@ template return u2; else // TODO Check if there is a better choice here - return detail::better_type_name(u1, u2); + return decltype(detail::better_type_name(u1, u2)){}; } else { - constexpr auto canonical_lhs = get_canonical_unit(U1{}); - constexpr auto canonical_rhs = get_canonical_unit(U2{}); + using canonical_lhs = decltype(get_canonical_unit(U1{})); + using canonical_rhs = decltype(get_canonical_unit(U2{})); - if constexpr (is_integral(canonical_lhs.mag / canonical_rhs.mag)) + if constexpr (is_integral(decltype(canonical_lhs::mag / canonical_rhs::mag){})) return u2; - else if constexpr (is_integral(canonical_rhs.mag / canonical_lhs.mag)) + else if constexpr (is_integral(decltype(canonical_rhs::mag / canonical_lhs::mag){})) return u1; else { - constexpr auto cm = detail::common_magnitude(canonical_lhs.mag, canonical_rhs.mag); - return scaled_unit{}; + constexpr auto cm = decltype(detail::common_magnitude(canonical_lhs::mag, canonical_rhs::mag)){}; + return scaled_unit>{}; } } } @@ -667,7 +624,7 @@ template [[nodiscard]] consteval Unit auto common_unit(Unit auto u1, Unit auto u2, Unit auto u3, Unit auto... rest) requires requires { common_unit(common_unit(u1, u2), u3, rest...); } { - return common_unit(common_unit(u1, u2), u3, rest...); + return decltype(common_unit(common_unit(u1, u2), u3, rest...)){}; } diff --git a/src/core/include/mp-units/framework/unit_concepts.h b/src/core/include/mp-units/framework/unit_concepts.h index 02a00c7a..bd42dc27 100644 --- a/src/core/include/mp-units/framework/unit_concepts.h +++ b/src/core/include/mp-units/framework/unit_concepts.h @@ -33,9 +33,8 @@ namespace mp_units { namespace detail { -// do not refactor below to a variable template - GCC-11 does not like it template -struct is_unit : std::false_type {}; +inline constexpr bool is_unit = false; } // namespace detail @@ -45,7 +44,7 @@ struct is_unit : std::false_type {}; * Satisfied by all unit types provided by the library. */ MP_UNITS_EXPORT template -concept Unit = detail::is_unit::value; +concept Unit = detail::is_unit; template struct scaled_unit; @@ -144,8 +143,8 @@ inline constexpr bool is_specialization_of_prefixed_unit requires requires(T* t) { is_unit_impl(t); } && (!is_specialization_of_named_unit) && - (!is_specialization_of_prefixed_unit) -struct is_unit : std::true_type {}; + (!is_specialization_of_prefixed_unit) +inline constexpr bool is_unit = true; template [[nodiscard]] consteval bool has_associated_quantity(U); @@ -197,7 +196,7 @@ concept UnitOf = namespace detail { -[[nodiscard]] consteval bool have_same_canonical_reference_unit(Unit auto u1, Unit auto u2); +[[nodiscard]] consteval auto have_same_canonical_reference_unit(Unit auto u1, Unit auto u2); } @@ -210,7 +209,7 @@ namespace detail { MP_UNITS_EXPORT template concept UnitCompatibleWith = Unit && Unit && QuantitySpec && - (!AssociatedUnit || UnitOf)&&detail::have_same_canonical_reference_unit(U{}, U2); + (!AssociatedUnit || UnitOf)&&decltype(detail::have_same_canonical_reference_unit(U{}, U2))::value; } // namespace mp_units diff --git a/src/systems/include/mp-units/systems/si/chrono.h b/src/systems/include/mp-units/systems/si/chrono.h index 2ddd71a1..6028e95c 100644 --- a/src/systems/include/mp-units/systems/si/chrono.h +++ b/src/systems/include/mp-units/systems/si/chrono.h @@ -116,8 +116,7 @@ struct quantity_point_like_traits Q> [[nodiscard]] constexpr auto to_chrono_duration(const Q& q) { - constexpr auto canonical = get_canonical_unit(Q::unit); - constexpr detail::ratio r = detail::as_ratio(canonical.mag); + constexpr detail::ratio r = detail::as_ratio(decltype(get_canonical_unit(Q::unit))::mag); return std::chrono::duration>{q}; } @@ -127,8 +126,7 @@ template QP> { using clock = MP_UNITS_TYPENAME decltype(QP::absolute_point_origin)::clock; using rep = MP_UNITS_TYPENAME QP::rep; - constexpr auto canonical = get_canonical_unit(QP::unit); - constexpr detail::ratio r = detail::as_ratio(canonical.mag); + constexpr detail::ratio r = detail::as_ratio(decltype(get_canonical_unit(QP::unit))::mag); using ret_type = std::chrono::time_point>>; return ret_type(to_chrono_duration(qp - qp.absolute_point_origin)); }