From 2d1bf8a689f40fe4d7382ff8b3cd710d8edb6142 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 29 Apr 2025 13:26:27 +0200 Subject: [PATCH] refactor: constraints refactoring --- .../include/mp-units/framework/quantity.h | 219 +++++++++--------- .../mp-units/framework/quantity_point.h | 43 ++-- .../mp-units/framework/system_reference.h | 3 +- src/core/include/mp-units/framework/unit.h | 35 +-- .../mp-units/framework/unit_concepts.h | 28 +-- .../include/mp-units/framework/value_cast.h | 62 ++--- src/core/include/mp-units/math.h | 2 +- 7 files changed, 182 insertions(+), 210 deletions(-) diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index 3ad8f563..c73652cf 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -61,31 +61,40 @@ template return is_integral(get_canonical_unit(from).mag / get_canonical_unit(to).mag); } -template -concept ValuePreservingTo = - Unit && Unit && - std::assignable_from && +template +concept ValuePreservingConstruction = + std::constructible_from && is_value_preserving, T>; + +template +concept ValuePreservingAssignment = std::assignable_from && is_value_preserving, T>; + +template +concept ValuePreservingScaling1Rep = SaneScaling && + (treat_as_floating_point || (integral_conversion_factor(FromUnit, ToUnit))); + +template +concept ValuePreservingScaling2Reps = + // TODO consider providing constraints of sudo_cast to check if representation types can be scaled between each other + // CastableReps && + SaneScaling && (treat_as_floating_point || - (!treat_as_floating_point> && integral_conversion_factor(FromUnit, ToUnit) && - !overflows_non_zero_values(FromUnit, ToUnit))); + (!treat_as_floating_point && integral_conversion_factor(FromUnit, ToUnit))); -template -concept RepresentationValuePreservingTo = - QuantitySpec && RepresentationOf, QS> && - ValuePreservingTo; +template +concept QuantityConstructibleFrom = + Quantity && Quantity && implicitly_convertible(QFrom::quantity_spec, QTo::quantity_spec) && + ValuePreservingConstruction && + ValuePreservingScaling2Reps; -template -concept ScalarRepresentationValuePreservingTo = - ScalarRepresentation> && ValuePreservingTo; +template +concept ScalarValuePreservingTo = (!Quantity) && Scalar && is_value_preserving; -template -concept QuantityConvertibleTo = - Quantity && Quantity && implicitly_convertible(QFrom::quantity_spec, QTo::quantity_spec) && - (interconvertible(QFrom::unit, QTo::unit)) && - ValuePreservingTo && - // TODO consider providing constraints of sudo_cast here rather than testing if it can be called (its return type is - // deduced thus the function is evaluated here and may emit truncating conversion or other warnings) - requires(QFrom q) { sudo_cast(q); }; +template +concept NumberLike = Reference && + (implicitly_convertible(get_quantity_spec(R), dimensionless)) && (equivalent(get_unit(R), one)); + +template +concept NumberLikeQuantity = Quantity && NumberLike; template concept InvokeResultOf = QuantitySpec && std::regular_invocable && @@ -97,13 +106,8 @@ template && Quantity && Quantity && InvokeResultOf; -// TODO remove the following when clang diagnostics improve -// https://github.com/llvm/llvm-project/issues/96660 template -concept HaveCommonReferenceImpl = requires { get_common_reference(R1, R2); }; - -template -concept HaveCommonReference = HaveCommonReferenceImpl; +concept HaveCommonReference = requires { get_common_reference(R1, R2); }; template using common_quantity_for = quantity [[nodiscard]] consteval bool overflows_non_zero_common_values(U1 u1, U2 u2) { constexpr Unit auto cu = get_common_unit(U1{}, U2{}); - return overflows_non_zero_values(u1, cu) || overflows_non_zero_values(u2, cu); + return scaling_overflows_non_zero_values(u1, cu) || scaling_overflows_non_zero_values(u2, cu); } template @@ -125,20 +129,12 @@ concept CommonlyInvocableQuantities = (!overflows_non_zero_common_values>(Q1::unit, Q2::unit)); -template -concept SameValueAs = (equivalent(get_unit(R1), get_unit(R2))) && std::convertible_to; - template using quantity_like_type = quantity::reference, typename quantity_like_traits::rep>; template> concept Mutable = (!std::is_const_v) && std::derived_from; -template -concept ConvertibleWithNumber = - Reference && (implicitly_convertible(get_quantity_spec(R), dimensionless)) && - (equivalent(get_unit(R), one)); - } // namespace detail MP_UNITS_EXPORT_BEGIN @@ -168,19 +164,19 @@ public: [[nodiscard]] static constexpr quantity zero() noexcept requires requires { representation_values::zero(); } { - return {representation_values::zero(), R}; + return {representation_values::zero(), reference}; } [[nodiscard]] static constexpr quantity min() noexcept requires requires { representation_values::min(); } { - return {representation_values::min(), R}; + return {representation_values::min(), reference}; } [[nodiscard]] static constexpr quantity max() noexcept requires requires { representation_values::max(); } { - return {representation_values::max(), R}; + return {representation_values::max(), reference}; } // construction, assignment, destruction @@ -190,34 +186,35 @@ public: ~quantity() = default; template - requires detail::SameValueAs, Rep> + requires(equivalent(unit, get_unit(R2{}))) && detail::ValuePreservingConstruction constexpr quantity(FwdValue&& val, R2) : numerical_value_is_an_implementation_detail_(std::forward(val)) { } - template> - requires(!detail::SameValueAs) && - detail::QuantityConvertibleTo, quantity> - constexpr quantity(FwdValue&& val, R2) : quantity(quantity{std::forward(val), R2{}}) + template + requires(!equivalent(unit, get_unit(R2{}))) && + detail::QuantityConstructibleFrom>> + constexpr quantity(FwdValue&& val, R2) : quantity(::mp_units::quantity{std::forward(val), R2{}}) { } - template FwdValue> - requires detail::ConvertibleWithNumber - constexpr explicit(false) quantity(FwdValue&& val) : + template + requires detail::NumberLike && detail::ValuePreservingConstruction + constexpr explicit(!std::convertible_to) quantity(FwdValue&& val) : numerical_value_is_an_implementation_detail_(std::forward(val)) { } - template Q> + template + requires detail::QuantityConstructibleFrom> // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) - constexpr explicit(!std::convertible_to) quantity(const Q& q) : + constexpr explicit(!std::convertible_to) quantity(const quantity& q) : quantity(detail::sudo_cast(q)) { } template - requires detail::QuantityConvertibleTo, quantity> + requires detail::QuantityConstructibleFrom> constexpr explicit(quantity_like_traits::explicit_import || // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) !std::convertible_to::rep, Rep>) quantity(const Q& q) : @@ -228,52 +225,52 @@ public: quantity& operator=(const quantity&) = default; quantity& operator=(quantity&&) = default; - template FwdValue> - requires detail::ConvertibleWithNumber + template + requires detail::NumberLike && detail::ValuePreservingAssignment constexpr quantity& operator=(FwdValue&& val) { numerical_value_is_an_implementation_detail_ = std::forward(val); return *this; } - // unit conversions - template ToU> - requires detail::QuantityConvertibleTo> + template ToU> + requires detail::ValuePreservingScaling1Rep [[nodiscard]] constexpr QuantityOf auto in(ToU) const { return quantity{*this}; } template ToRep> - requires detail::QuantityConvertibleTo> + requires detail::ValuePreservingConstruction [[nodiscard]] constexpr QuantityOf auto in() const { return quantity{*this}; } - template ToRep, detail::UnitCompatibleWith ToU> - requires detail::QuantityConvertibleTo> + template ToRep, detail::WeakUnitOf ToU> + requires detail::ValuePreservingConstruction && + detail::ValuePreservingScaling2Reps [[nodiscard]] constexpr QuantityOf auto in(ToU) const { return quantity{*this}; } - template ToU> - requires requires(const quantity q) { value_cast(q); } + template ToU> + requires detail::SaneScaling [[nodiscard]] constexpr QuantityOf auto force_in(ToU) const { return value_cast(*this); } template ToRep> - requires requires(const quantity q) { value_cast(q); } + requires std::constructible_from [[nodiscard]] constexpr QuantityOf auto force_in() const { return value_cast(*this); } - template ToRep, detail::UnitCompatibleWith ToU> - requires requires(const quantity q) { value_cast(q); } + template ToRep, detail::WeakUnitOf ToU> + requires std::constructible_from && detail::SaneScaling [[nodiscard]] constexpr QuantityOf auto force_in(ToU) const { return value_cast(*this); @@ -303,32 +300,32 @@ public: = delete; #endif - template U> - requires detail::QuantityConvertibleTo> + template U> + requires detail::ValuePreservingScaling1Rep [[nodiscard]] constexpr rep numerical_value_in(U) const noexcept { - return (*this).in(U{}).numerical_value_is_an_implementation_detail_; + return in(U{}).numerical_value_is_an_implementation_detail_; } - template U> - requires requires(const quantity q) { value_cast(q); } + template U> + requires detail::SaneScaling [[nodiscard]] constexpr rep force_numerical_value_in(U) const noexcept { - return (*this).force_in(U{}).numerical_value_is_an_implementation_detail_; + return force_in(U{}).numerical_value_is_an_implementation_detail_; } // conversion operators - template Value = std::remove_cvref_t> - requires detail::ConvertibleWithNumber + template Value = std::remove_cvref_t> + requires detail::NumberLike [[nodiscard]] explicit operator V_() const& noexcept { return numerical_value_is_an_implementation_detail_; } template> - requires detail::QuantityConvertibleTo> + requires detail::QuantityConstructibleFrom, quantity> [[nodiscard]] explicit(quantity_like_traits::explicit_export || - !std::convertible_to::rep>) constexpr + !std::convertible_to::rep>) constexpr // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) operator Q_() const noexcept(noexcept(quantity_like_traits::from_numerical_value(numerical_value_is_an_implementation_detail_)) && @@ -393,9 +390,10 @@ public: // compound assignment operators template Q, auto R2, typename Rep2> - requires detail::QuantityConvertibleTo, quantity> && requires(rep& a, const Rep2 b) { - { a += b } -> std::same_as; - } + requires(implicitly_convertible(get_quantity_spec(R2), quantity_spec)) && + detail::ValuePreservingScaling2Reps && requires(rep& a, const Rep2 b) { + { a += b } -> std::same_as; + } friend constexpr decltype(auto) operator+=(Q&& lhs, const quantity& rhs) { if constexpr (equivalent(unit, get_unit(R2))) @@ -406,9 +404,10 @@ public: } template Q, auto R2, typename Rep2> - requires detail::QuantityConvertibleTo, quantity> && requires(rep& a, const Rep2 b) { - { a -= b } -> std::same_as; - } + requires(implicitly_convertible(get_quantity_spec(R2), quantity_spec)) && + detail::ValuePreservingScaling2Reps && requires(rep& a, const Rep2 b) { + { a -= b } -> std::same_as; + } friend constexpr decltype(auto) operator-=(Q&& lhs, const quantity& rhs) { if constexpr (equivalent(unit, get_unit(R2))) @@ -419,10 +418,10 @@ public: } template Q, auto R2, typename Rep2> - requires detail::QuantityConvertibleTo, quantity> && (!treat_as_floating_point) && - requires(rep& a, const Rep2 b) { - { a %= b } -> std::same_as; - } + requires(!treat_as_floating_point) && (implicitly_convertible(get_quantity_spec(R2), quantity_spec)) && + detail::ValuePreservingScaling2Reps && requires(rep& a, const Rep2 b) { + { a %= b } -> std::same_as; + } friend constexpr decltype(auto) operator%=(Q&& lhs, const quantity& rhs) { @@ -434,8 +433,8 @@ public: return std::forward(lhs); } - template Q, detail::ScalarRepresentationValuePreservingTo Value> - requires(!Quantity) && requires(rep& a, const Value b) { + template Q, detail::ScalarValuePreservingTo Value> + requires requires(rep& a, const Value b) { { a *= b } -> std::same_as; } friend constexpr decltype(auto) operator*=(Q&& lhs, const Value& val) @@ -444,18 +443,17 @@ public: return std::forward(lhs); } - template Q1, QuantityOf Q2> - requires detail::ConvertibleWithNumber && - detail::ScalarRepresentationValuePreservingTo && requires(rep& a, const Q2::rep b) { - { a *= b } -> std::same_as; - } + template Q1, detail::NumberLikeQuantity Q2> + requires detail::ScalarValuePreservingTo && requires(rep& a, const Q2::rep b) { + { a *= b } -> std::same_as; + } friend constexpr decltype(auto) operator*=(Q1&& lhs, const Q2& rhs) { return std::forward(lhs) *= rhs.numerical_value_is_an_implementation_detail_; } - template Q, detail::ScalarRepresentationValuePreservingTo Value> - requires(!Quantity) && requires(rep& a, const Value b) { + template Q, detail::ScalarValuePreservingTo Value> + requires requires(rep& a, const Value b) { { a /= b } -> std::same_as; } friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& val) @@ -465,11 +463,10 @@ public: return std::forward(lhs); } - template Q1, QuantityOf Q2> - requires detail::ConvertibleWithNumber && - detail::ScalarRepresentationValuePreservingTo && requires(rep& a, const Q2::rep b) { - { a /= b } -> std::same_as; - } + template Q1, detail::NumberLikeQuantity Q2> + requires detail::ScalarValuePreservingTo && requires(rep& a, const Q2::rep b) { + { a /= b } -> std::same_as; + } friend constexpr decltype(auto) operator/=(Q1&& lhs, const Q2& rhs) { return std::forward(lhs) /= rhs.numerical_value_is_an_implementation_detail_; @@ -488,16 +485,14 @@ public: } template Q, RepresentationOf Value> - requires detail::ConvertibleWithNumber && - detail::InvokeResultOf, Rep, const Value&> + requires detail::NumberLike && detail::InvokeResultOf, Rep, const Value&> [[nodiscard]] friend constexpr Quantity auto operator+(const Q& lhs, const Value& rhs) { return lhs + ::mp_units::quantity{rhs}; } template Q, RepresentationOf Value> - requires detail::ConvertibleWithNumber && - detail::InvokeResultOf, Rep, const Value&> + requires detail::NumberLike && detail::InvokeResultOf, Rep, const Value&> [[nodiscard]] friend constexpr Quantity auto operator+(const Value& lhs, const Q& rhs) { return ::mp_units::quantity{lhs} + rhs; @@ -515,16 +510,14 @@ public: } template Q, RepresentationOf Value> - requires detail::ConvertibleWithNumber && - detail::InvokeResultOf, Rep, const Value&> + requires detail::NumberLike && detail::InvokeResultOf, Rep, const Value&> [[nodiscard]] friend constexpr Quantity auto operator-(const Q& lhs, const Value& rhs) { return lhs - ::mp_units::quantity{rhs}; } template Q, RepresentationOf Value> - requires detail::ConvertibleWithNumber && - detail::InvokeResultOf, Rep, const Value&> + requires detail::NumberLike && detail::InvokeResultOf, Rep, const Value&> [[nodiscard]] friend constexpr Quantity auto operator-(const Value& lhs, const Q& rhs) { return ::mp_units::quantity{lhs} - rhs; @@ -544,7 +537,7 @@ public: } template Q, RepresentationOf Value> - requires detail::ConvertibleWithNumber && + requires detail::NumberLike && detail::InvokeResultOf, Rep, const Value&> [[nodiscard]] friend constexpr Quantity auto operator%(const Q& lhs, const Value& rhs) { @@ -552,7 +545,7 @@ public: } template Q, RepresentationOf Value> - requires detail::ConvertibleWithNumber && + requires detail::NumberLike && detail::InvokeResultOf, Rep, const Value&> [[nodiscard]] friend constexpr Quantity auto operator%(const Value& lhs, const Q& rhs) { @@ -568,7 +561,7 @@ public: template Q, typename Value> requires(!Quantity) && - (!Reference) && detail::InvokeResultOf, Rep, const Value&> + (!Reference) && detail::InvokeResultOf, rep, const Value&> [[nodiscard]] friend constexpr QuantityOf auto operator*(const Q& q, const Value& val) { return ::mp_units::quantity{q.numerical_value_ref_in(unit) * val, R}; @@ -576,7 +569,7 @@ public: template Q> requires(!Quantity) && - (!Reference) && detail::InvokeResultOf, const Value&, Rep> + (!Reference) && detail::InvokeResultOf, const Value&, rep> [[nodiscard]] friend constexpr QuantityOf auto operator*(const Value& val, const Q& q) { return ::mp_units::quantity{val * q.numerical_value_ref_in(unit), R}; @@ -592,7 +585,7 @@ public: template Q, typename Value> requires(!Quantity) && - (!Reference) && detail::InvokeResultOf, Rep, const Value&> + (!Reference) && detail::InvokeResultOf, rep, const Value&> [[nodiscard]] friend constexpr QuantityOf auto operator/(const Q& q, const Value& val) { MP_UNITS_EXPECTS_DEBUG(val != representation_values::zero()); @@ -601,7 +594,7 @@ public: template Q> requires(!Quantity) && - (!Reference) && detail::InvokeResultOf, const Value&, Rep> + (!Reference) && detail::InvokeResultOf, const Value&, rep> [[nodiscard]] friend constexpr Quantity auto operator/(const Value& val, const Q& q) { MP_UNITS_EXPECTS_DEBUG(is_neq_zero(q)); @@ -620,7 +613,7 @@ public: } template Q, RepresentationOf Value> - requires detail::ConvertibleWithNumber && std::equality_comparable_with + requires detail::NumberLike && std::equality_comparable_with [[nodiscard]] friend constexpr bool operator==(const Q& lhs, const Value& rhs) { return lhs.numerical_value_ref_in(unit) == rhs; @@ -638,7 +631,7 @@ public: } template Q, RepresentationOf Value> - requires detail::ConvertibleWithNumber && std::three_way_comparable_with + requires detail::NumberLike && std::three_way_comparable_with [[nodiscard]] friend constexpr auto operator<=>(const Q& lhs, const Value& rhs) { return lhs.numerical_value_ref_in(unit) <=> rhs; diff --git a/src/core/include/mp-units/framework/quantity_point.h b/src/core/include/mp-units/framework/quantity_point.h index bd39a8cd..c9d9ede4 100644 --- a/src/core/include/mp-units/framework/quantity_point.h +++ b/src/core/include/mp-units/framework/quantity_point.h @@ -330,43 +330,44 @@ public: } // unit conversions - template ToU> - requires detail::QuantityConvertibleTo> + template ToU> + requires detail::ValuePreservingScaling1Rep [[nodiscard]] constexpr QuantityPointOf auto in(ToU) const { return ::mp_units::quantity_point{quantity_ref_from(point_origin).in(ToU{}), point_origin}; } template ToRep> - requires detail::QuantityConvertibleTo> + requires detail::ValuePreservingConstruction [[nodiscard]] constexpr QuantityPointOf auto in() const { return ::mp_units::quantity_point{quantity_ref_from(point_origin).template in(), point_origin}; } - template ToRep, detail::UnitCompatibleWith ToU> - requires detail::QuantityConvertibleTo> + template ToRep, detail::WeakUnitOf ToU> + requires detail::ValuePreservingConstruction && + detail::ValuePreservingScaling2Reps [[nodiscard]] constexpr QuantityPointOf auto in(ToU) const { return ::mp_units::quantity_point{quantity_ref_from(point_origin).template in(ToU{}), point_origin}; } - template ToU> - requires requires(const quantity_type q) { value_cast(q); } + template ToU> + requires detail::SaneScaling [[nodiscard]] constexpr QuantityPointOf auto force_in(ToU) const { return ::mp_units::quantity_point{quantity_ref_from(point_origin).force_in(ToU{}), point_origin}; } template ToRep> - requires requires(const quantity_type q) { value_cast(q); } + requires std::constructible_from [[nodiscard]] constexpr QuantityPointOf auto force_in() const { return ::mp_units::quantity_point{quantity_ref_from(point_origin).template force_in(), point_origin}; } - template ToRep, detail::UnitCompatibleWith ToU> - requires requires(const quantity_type q) { value_cast(q); } + template ToRep, detail::WeakUnitOf ToU> + requires std::constructible_from && detail::SaneScaling [[nodiscard]] constexpr QuantityPointOf auto force_in(ToU) const { return ::mp_units::quantity_point{quantity_ref_from(point_origin).template force_in(ToU{}), point_origin}; @@ -440,8 +441,9 @@ public: // compound assignment operators template QP, auto R2, typename Rep2> - requires detail::QuantityConvertibleTo, quantity_type> && - requires(const quantity_type q) { quantity_from_origin_is_an_implementation_detail_ += q; } + requires(implicitly_convertible(get_quantity_spec(R2), quantity_spec)) && + detail::ValuePreservingScaling2Reps && + requires(const quantity_type q) { quantity_from_origin_is_an_implementation_detail_ += q; } friend constexpr decltype(auto) operator+=(QP&& qp, const quantity& q) { qp.quantity_from_origin_is_an_implementation_detail_ += q; @@ -449,8 +451,9 @@ public: } template QP, auto R2, typename Rep2> - requires detail::QuantityConvertibleTo, quantity_type> && - requires(const quantity_type q) { quantity_from_origin_is_an_implementation_detail_ -= q; } + requires(implicitly_convertible(get_quantity_spec(R2), quantity_spec)) && + detail::ValuePreservingScaling2Reps && + requires(const quantity_type q) { quantity_from_origin_is_an_implementation_detail_ -= q; } friend constexpr decltype(auto) operator-=(QP&& qp, const quantity& q) { qp.quantity_from_origin_is_an_implementation_detail_ -= q; @@ -458,27 +461,21 @@ public: } // binary operators on quantity points - template QP, auto R2, typename Rep2> - // TODO simplify when gcc catches up - requires ReferenceOf + template QP, ReferenceOf auto R2, typename Rep2> [[nodiscard]] friend constexpr QuantityPoint auto operator+(const QP& qp, const quantity& q) requires requires { qp.quantity_ref_from(PO) + q; } { return detail::make_quantity_point(qp.quantity_ref_from(PO) + q, PO); } - template QP> - // TODO simplify when gcc catches up - requires ReferenceOf + template auto R1, typename Rep1, std::derived_from QP> [[nodiscard]] friend constexpr QuantityPoint auto operator+(const quantity& q, const QP& qp) requires requires { q + qp.quantity_ref_from(PO); } { return qp + q; } - template QP, auto R2, typename Rep2> - // TODO simplify when gcc catches up - requires ReferenceOf + template QP, ReferenceOf auto R2, typename Rep2> [[nodiscard]] friend constexpr QuantityPoint auto operator-(const QP& qp, const quantity& q) requires requires { qp.quantity_ref_from(PO) - q; } { diff --git a/src/core/include/mp-units/framework/system_reference.h b/src/core/include/mp-units/framework/system_reference.h index 79fc9bb4..c3517917 100644 --- a/src/core/include/mp-units/framework/system_reference.h +++ b/src/core/include/mp-units/framework/system_reference.h @@ -65,8 +65,7 @@ struct system_reference { static constexpr auto quantity_spec = Q; static constexpr auto coherent_unit = CoU; - template - requires(interconvertible(coherent_unit, U{})) + template U> #if MP_UNITS_COMP_MSVC [[nodiscard]] constexpr decltype(reference{}) operator[](U) const #else diff --git a/src/core/include/mp-units/framework/unit.h b/src/core/include/mp-units/framework/unit.h index 385aceac..ae0aae78 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -124,27 +124,6 @@ constexpr auto get_canonical_unit_result = get_canonical_unit_impl(U{}, U{}); // 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_result; } -namespace detail { - -// We are using a helper concept to benefit from short-circuiting -template -concept PotentiallyInterConvertibleTo = Unit && Unit && - (!AssociatedUnit || !AssociatedUnit || - explicitly_convertible(get_quantity_spec(U1{}), get_quantity_spec(U2{}))); -} // namespace detail - -// interconvertible -template -[[nodiscard]] consteval bool interconvertible(U1 u1, U2 u2) -{ - if constexpr (is_same_v) - return true; - else if constexpr (detail::PotentiallyInterConvertibleTo) - return is_same_v; - else - return false; -} - template requires(M != detail::unit_magnitude<>{} && M != mag<1>) struct scaled_unit; @@ -427,8 +406,7 @@ struct prefixed_unit : decltype(M * U)::_base_type_ { namespace detail { -template - requires(interconvertible(U1{}, U2{})) +template U2> [[nodiscard]] consteval Unit auto get_common_scaled_unit(U1, U2) { constexpr auto canonical_lhs = get_canonical_unit(U1{}); @@ -671,8 +649,7 @@ inline constexpr auto ppm = parts_per_million; // Common unit [[nodiscard]] consteval Unit auto get_common_unit(Unit auto u) { return u; } -template - requires(interconvertible(U1{}, U2{})) +template U2> [[nodiscard]] consteval Unit auto get_common_unit(U1 u1, U2 u2) { if constexpr (is_same_v) @@ -732,8 +709,7 @@ using collapse_common_unit = type_list_unique< } // namespace detail -template - requires(interconvertible(common_unit{}, NewUnit{})) +template{}> NewUnit> [[nodiscard]] consteval Unit auto get_common_unit(common_unit, NewUnit) { using type = detail::collapse_common_unit; @@ -743,15 +719,14 @@ template return detail::type_list_map{}; } -template - requires(interconvertible(common_unit{}, NewUnit{})) +template{}> NewUnit> [[nodiscard]] consteval Unit auto get_common_unit(NewUnit nu, common_unit cu) { return get_common_unit(cu, nu); } template - requires(interconvertible(common_unit{}, common_unit{})) + requires(detail::UnitConvertibleTo, common_unit{}>) [[nodiscard]] consteval Unit auto get_common_unit(common_unit, common_unit) { if constexpr (sizeof...(Rest) == 1) diff --git a/src/core/include/mp-units/framework/unit_concepts.h b/src/core/include/mp-units/framework/unit_concepts.h index ec3b32c9..bcc8aaf2 100644 --- a/src/core/include/mp-units/framework/unit_concepts.h +++ b/src/core/include/mp-units/framework/unit_concepts.h @@ -100,25 +100,27 @@ MP_UNITS_EXPORT template concept UnitOf = AssociatedUnit && QuantitySpec && (implicitly_convertible(get_quantity_spec(U{}), QS)); -MP_UNITS_EXPORT template -[[nodiscard]] consteval bool interconvertible(U1 u1, U2 u2); - namespace detail { +template +concept UnitOf = UnitOf; + template concept WeakUnitOf = Unit && QuantitySpec && ((!AssociatedUnit) || UnitOf); -/** - * @brief A concept matching all units compatible with the provided unit and quantity spec - * - * Satisfied by all units that have the same canonical reference as `U2` and in case they - * have associated quantity specification it should satisfy `UnitOf`. - */ -template -concept UnitCompatibleWith = - Unit && Unit && QuantitySpec && - WeakUnitOf && (interconvertible(FromU, U{})); +template +concept UnitsOfCompatibleQuantities = explicitly_convertible(get_quantity_spec(U1), get_quantity_spec(U2)); + +template +concept ConvertibleUnits = (get_canonical_unit(U1).reference_unit == get_canonical_unit(U2).reference_unit); + +template +concept UnitConvertibleTo = + Unit && Unit && + ((U1{} == U2) || ((!AssociatedUnit || !AssociatedUnit || + UnitsOfCompatibleQuantities) && + ConvertibleUnits)); template concept OffsetUnit = Unit && requires { T::_point_origin_; }; diff --git a/src/core/include/mp-units/framework/value_cast.h b/src/core/include/mp-units/framework/value_cast.h index c3fa8708..01814f7a 100644 --- a/src/core/include/mp-units/framework/value_cast.h +++ b/src/core/include/mp-units/framework/value_cast.h @@ -38,7 +38,7 @@ namespace mp_units { namespace detail { template -[[nodiscard]] consteval bool overflows_non_zero_values(UFrom from, UTo to) +[[nodiscard]] consteval bool scaling_overflows_non_zero_values(UFrom from, UTo to) { if constexpr (is_same_v || treat_as_floating_point) return false; @@ -56,6 +56,10 @@ template return false; } +template +concept SaneScaling = UnitConvertibleTo && + (!detail::scaling_overflows_non_zero_values(FromU, ToU)); + } // namespace detail /** @@ -69,8 +73,8 @@ template * @tparam ToU a unit to use for a target quantity */ template> - requires detail::UnitCompatibleWith && - (!detail::overflows_non_zero_values(Q::unit, ToU)) + requires detail::WeakUnitOf && + detail::SaneScaling [[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q) { return detail::sudo_cast>( @@ -106,18 +110,18 @@ template> * @tparam ToRep a representation type to use for the target quantity */ template> - requires detail::UnitCompatibleWith && - (!detail::overflows_non_zero_values(Q::unit, ToU)) && - RepresentationOf && std::constructible_from + requires detail::WeakUnitOf && + RepresentationOf && std::constructible_from && + detail::SaneScaling [[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q) { return detail::sudo_cast>(std::forward(q)); } template> - requires detail::UnitCompatibleWith && - (!detail::overflows_non_zero_values(Q::unit, ToU)) && - RepresentationOf && std::constructible_from + requires detail::WeakUnitOf && + RepresentationOf && std::constructible_from && + detail::SaneScaling [[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q) { return value_cast(std::forward(q)); @@ -139,9 +143,10 @@ template> - requires detail::UnitCompatibleWith && - (!detail::overflows_non_zero_values(Q::unit, ToQ::unit)) && - (ToQ::quantity_spec == Q::quantity_spec) && std::constructible_from + requires(ToQ::quantity_spec == Q::quantity_spec) && + detail::WeakUnitOf && + std::constructible_from && + detail::SaneScaling [[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q) { return detail::sudo_cast(std::forward(q)); @@ -158,8 +163,8 @@ template> * @tparam ToU a unit to use for a target quantity point */ template> - requires detail::UnitCompatibleWith && - (!detail::overflows_non_zero_values(QP::unit, ToU)) + requires detail::WeakUnitOf && + detail::SaneScaling [[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp) { return quantity_point{value_cast(std::forward(qp).quantity_from_origin_is_an_implementation_detail_), @@ -196,9 +201,9 @@ template> - requires detail::UnitCompatibleWith && - (!detail::overflows_non_zero_values(QP::unit, ToU)) && - RepresentationOf && std::constructible_from + requires detail::WeakUnitOf && + RepresentationOf && std::constructible_from && + detail::SaneScaling [[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp) { return quantity_point{ @@ -207,9 +212,9 @@ template> - requires detail::UnitCompatibleWith && - (!detail::overflows_non_zero_values(QP::unit, ToU)) && - RepresentationOf && std::constructible_from + requires detail::WeakUnitOf && + RepresentationOf && std::constructible_from && + detail::SaneScaling [[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp) { return value_cast(std::forward(qp)); @@ -232,9 +237,10 @@ template> - requires detail::UnitCompatibleWith && - (!detail::overflows_non_zero_values(QP::unit, ToQ::unit)) && - (ToQ::quantity_spec == QP::quantity_spec) && std::constructible_from + requires(ToQ::quantity_spec == QP::quantity_spec) && + detail::WeakUnitOf && + std::constructible_from && + detail::SaneScaling [[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp) { return quantity_point{value_cast(std::forward(qp).quantity_from_origin_is_an_implementation_detail_), @@ -270,11 +276,11 @@ template> - requires detail::UnitCompatibleWith && - (!detail::overflows_non_zero_values(QP::unit, ToQP::unit)) && - (ToQP::quantity_spec == QP::quantity_spec) && - (detail::same_absolute_point_origins(ToQP::point_origin, QP::point_origin)) && - std::constructible_from + requires(ToQP::quantity_spec == QP::quantity_spec) && + detail::WeakUnitOf && + (detail::same_absolute_point_origins(ToQP::point_origin, QP::point_origin)) && + std::constructible_from && + detail::SaneScaling [[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp) { return detail::sudo_cast(std::forward(qp)); diff --git a/src/core/include/mp-units/math.h b/src/core/include/mp-units/math.h index efb6ad59..ae91637e 100644 --- a/src/core/include/mp-units/math.h +++ b/src/core/include/mp-units/math.h @@ -406,7 +406,7 @@ template */ template [[nodiscard]] constexpr Quantity auto inverse(const quantity& q) - requires(!detail::overflows_non_zero_values(one / get_unit(R), To)) && requires { + requires(!detail::scaling_overflows_non_zero_values(one / get_unit(R), To)) && requires { representation_values::one(); value_cast(representation_values::one() / q); }