diff --git a/src/core/include/mp-units/framework/dimension_concepts.h b/src/core/include/mp-units/framework/dimension_concepts.h index 35913606..c0a31ce0 100644 --- a/src/core/include/mp-units/framework/dimension_concepts.h +++ b/src/core/include/mp-units/framework/dimension_concepts.h @@ -97,12 +97,20 @@ concept DerivedDimension = MP_UNITS_EXPORT template concept Dimension = detail::BaseDimension || detail::DerivedDimension; +namespace detail { + +template +concept SameDimension = + Dimension && Dimension && (D1 == D2); + +} + /** * @brief A concept checking if the argument is of the same dimension. * * Satisfied when both argument satisfy a `Dimension` concept and when they compare equal. */ MP_UNITS_EXPORT template -concept DimensionOf = Dimension && Dimension && (T{} == D); +concept DimensionOf = Dimension && Dimension && detail::SameDimension; } // namespace mp_units diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index aa0b5bb7..fcbb22e5 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -56,12 +56,15 @@ template return is_integral(get_canonical_unit(from).mag / get_canonical_unit(to).mag); } +template +concept IsFloatingPoint = treat_as_floating_point; + template concept QuantityConvertibleTo = - Quantity && Quantity && implicitly_convertible(QFrom::quantity_spec, QTo::quantity_spec) && - convertible(QFrom::unit, QTo::unit) && - (treat_as_floating_point || - (!treat_as_floating_point && (integral_conversion_factor(QFrom::unit, QTo::unit)))) && + Quantity && Quantity && detail::QuantitySpecConvertibleTo && + detail::UnitConvertibleTo && + (IsFloatingPoint || + (!IsFloatingPoint && (integral_conversion_factor(QFrom::unit, QTo::unit)))) && // 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) { detail::sudo_cast(q); }; @@ -75,23 +78,28 @@ template && Quantity && InvokeResultOf; +// TODO remove the following when clang diagnostics improve +// https://github.com/llvm/llvm-project/issues/96660 +template +concept HaveCommonReferenceImpl = requires { common_reference(R1, R2); }; + +template +concept HaveCommonReference = HaveCommonReferenceImpl; + template concept CommonlyInvocableQuantities = - Quantity && Quantity && - // (Q1::quantity_spec.character == Q2::quantity_spec.character) && // TODO enable when vector quantities are handled - // correctly - requires { common_reference(Q1::reference, Q2::reference); } && + Quantity && Quantity && HaveCommonReference && InvocableQuantities; - template requires detail::CommonlyInvocableQuantities using common_quantity_for = quantity>; template -concept SameOriginalReferenceAs = DeltaReference && - Reference && (get_original_reference(T) == R); +concept SameOriginalReferenceAs = + DeltaReference && Reference && + detail::SameReference; template concept SameValueAs = detail::SameOriginalReferenceAs && std::same_as; @@ -238,14 +246,14 @@ public: #endif template U> - requires requires(quantity q) { q.in(U{}); } + requires detail::QuantityConvertibleTo> [[nodiscard]] constexpr rep numerical_value_in(U) const noexcept { return (*this).in(U{}).numerical_value_is_an_implementation_detail_; } template U> - requires requires(quantity q) { q.force_in(U{}); } + requires requires(quantity q) { value_cast(q); } [[nodiscard]] constexpr rep force_numerical_value_in(U) const noexcept { return (*this).force_in(U{}).numerical_value_is_an_implementation_detail_; diff --git a/src/core/include/mp-units/framework/quantity_cast.h b/src/core/include/mp-units/framework/quantity_cast.h index b1e4e6fc..5c03ae2b 100644 --- a/src/core/include/mp-units/framework/quantity_cast.h +++ b/src/core/include/mp-units/framework/quantity_cast.h @@ -53,7 +53,8 @@ namespace mp_units { * @tparam ToQS a quantity specification to use for a target quantity */ template - requires Quantity> && (castable(std::remove_reference_t::quantity_spec, ToQS)) + requires Quantity> && + detail::QuantitySpecCastableTo::quantity_spec, ToQS> [[nodiscard]] constexpr Quantity auto quantity_cast(Q&& q) { return quantity{std::forward(q).numerical_value_is_an_implementation_detail_, @@ -78,7 +79,8 @@ template * @tparam ToQS a quantity specification to use for a target quantity point */ template - requires QuantityPoint> && (castable(std::remove_reference_t::quantity_spec, ToQS)) + requires QuantityPoint> && + detail::QuantitySpecCastableTo::quantity_spec, ToQS> [[nodiscard]] constexpr QuantityPoint auto quantity_cast(QP&& qp) { return QP{quantity_cast(std::forward(qp).quantity_from_origin_is_an_implementation_detail_), diff --git a/src/core/include/mp-units/framework/quantity_point.h b/src/core/include/mp-units/framework/quantity_point.h index 70c3ce13..d148e07a 100644 --- a/src/core/include/mp-units/framework/quantity_point.h +++ b/src/core/include/mp-units/framework/quantity_point.h @@ -271,18 +271,18 @@ public: } // unit conversions - template U> - requires detail::QuantityConvertibleTo> - [[nodiscard]] constexpr QuantityPointOf auto in(U) const + template ToU> + requires detail::QuantityConvertibleTo> + [[nodiscard]] constexpr QuantityPointOf auto in(ToU) const { - return ::mp_units::quantity_point{quantity_ref_from(PO).in(U{}), PO}; + return ::mp_units::quantity_point{quantity_ref_from(PO).in(ToU{}), PO}; } - template U> - requires requires(quantity_type q) { value_cast(q); } - [[nodiscard]] constexpr QuantityPointOf auto force_in(U) const + template ToU> + requires requires(quantity_type q) { value_cast(q); } + [[nodiscard]] constexpr QuantityPointOf auto force_in(ToU) const { - return ::mp_units::quantity_point{quantity_ref_from(PO).force_in(U{}), PO}; + return ::mp_units::quantity_point{quantity_ref_from(PO).force_in(ToU{}), PO}; } // conversion operators diff --git a/src/core/include/mp-units/framework/quantity_spec.h b/src/core/include/mp-units/framework/quantity_spec.h index 9b4a3131..41b4b41e 100644 --- a/src/core/include/mp-units/framework/quantity_spec.h +++ b/src/core/include/mp-units/framework/quantity_spec.h @@ -126,7 +126,7 @@ struct quantity_spec_interface { template [[nodiscard]] constexpr Quantity auto operator()(this Self self, Q&& q) requires Quantity> && - (explicitly_convertible(std::remove_reference_t::quantity_spec, self)) + detail::QuantitySpecExplicitlyConvertibleTo::quantity_spec, self> { return quantity{std::forward(q).numerical_value_is_an_implementation_detail_, delta::unit)>}; @@ -140,7 +140,7 @@ struct quantity_spec_interface { template requires Quantity> && - (explicitly_convertible(std::remove_reference_t::quantity_spec, Self_{})) + detail::QuantitySpecExplicitlyConvertibleTo::quantity_spec, Self_{}> [[nodiscard]] constexpr Quantity auto operator()(Q&& q) const { return quantity{std::forward(q).numerical_value_is_an_implementation_detail_, @@ -337,7 +337,7 @@ struct quantity_spec : detail::propagate_equation, detail template requires Quantity> && - (explicitly_convertible(std::remove_reference_t::quantity_spec, Self_{})) + detail::QuantitySpecExplicitlyConvertibleTo::quantity_spec, Self_{}> [[nodiscard]] constexpr Quantity auto operator()(Q&& q) const { return quantity{std::forward(q).numerical_value_is_an_implementation_detail_, @@ -379,16 +379,16 @@ struct quantity_spec : detail::propagate_equation, detail #ifdef MP_UNITS_API_NO_CRTP template auto... Args> - requires(!requires { QS._equation_; } || (requires { - QS._equation_; - } && (explicitly_convertible(Eq, QS._equation_)))) && (... && !QuantitySpec) + requires(!requires { QS._equation_; } || + ((requires { QS._equation_; }) && detail::QuantitySpecExplicitlyConvertibleTo)) && + (... && !QuantitySpec) struct quantity_spec : detail::quantity_spec_interface { #else template auto... Args> - requires(!requires { QS._equation_; } || (requires { - QS._equation_; - } && (explicitly_convertible(Eq, QS._equation_)))) && (... && !QuantitySpec) + requires(!requires { QS._equation_; } || + ((requires { QS._equation_; }) && detail::QuantitySpecExplicitlyConvertibleTo)) && + (... && !QuantitySpec) struct quantity_spec : detail::quantity_spec_interface { #endif using _base_type_ = quantity_spec; @@ -498,7 +498,7 @@ template #ifdef MP_UNITS_API_NO_CRTP template - requires detail::QuantitySpecWithNoSpecifiers && (detail::get_kind_tree_root(Q{}) == Q{}) + requires detail::QuantitySpecWithNoSpecifiers && detail::SameQuantitySpec struct kind_of_ final : Q::_base_type_ { using _base_type_ = kind_of_; static constexpr auto _quantity_spec_ = Q{}; @@ -507,10 +507,10 @@ struct kind_of_ final : Q::_base_type_ { #if MP_UNITS_COMP_CLANG template - requires detail::QuantitySpecWithNoSpecifiers && (detail::get_kind_tree_root(Q{}) == Q{}) + requires detail::QuantitySpecWithNoSpecifiers && detail::SameQuantitySpec #else template - requires(detail::get_kind_tree_root(Q{}) == Q{}) + requires detail::SameQuantitySpec #endif struct kind_of_ final : quantity_spec, Q{}>::_base_type_ { using _base_type_ = kind_of_; @@ -519,7 +519,7 @@ struct kind_of_ final : quantity_spec, Q{}>::_base_type_ { #endif MP_UNITS_EXPORT template - requires(detail::get_kind_tree_root(Q) == Q) + requires detail::SameQuantitySpec inline constexpr kind_of_ kind_of; namespace detail { @@ -1531,8 +1531,8 @@ template template [[nodiscard]] consteval QuantitySpec auto common_quantity_spec(Q1 q1, Q2 q2) - requires(implicitly_convertible(get_kind_tree_root(q1), get_kind_tree_root(q2)) || - implicitly_convertible(get_kind_tree_root(q2), get_kind_tree_root(q1))) + requires detail::QuantitySpecConvertibleTo || + detail::QuantitySpecConvertibleTo { using QQ1 = decltype(detail::remove_kind(q1)); using QQ2 = decltype(detail::remove_kind(q2)); diff --git a/src/core/include/mp-units/framework/quantity_spec_concepts.h b/src/core/include/mp-units/framework/quantity_spec_concepts.h index dba816d0..17aaeb87 100644 --- a/src/core/include/mp-units/framework/quantity_spec_concepts.h +++ b/src/core/include/mp-units/framework/quantity_spec_concepts.h @@ -125,18 +125,41 @@ MP_UNITS_EXPORT template namespace detail { +template +concept SameQuantitySpec = QuantitySpec && + QuantitySpec && (QS1 == QS2); + template [[nodiscard]] consteval bool is_child_of(Child ch, Parent p); +template +concept ChildQuantitySpecOf = (is_child_of(Child, Parent)); + template -concept NestedQuantityKindSpecOf = QuantitySpec && QuantitySpec && - get_kind(From) != get_kind(To) && is_child_of(To, get_kind(From)._quantity_spec_); +concept NestedQuantityKindSpecOf = + QuantitySpec && QuantitySpec && + (!SameQuantitySpec)&&ChildQuantitySpecOf; + +template +concept QuantitySpecConvertibleTo = + QuantitySpec && QuantitySpec && + implicitly_convertible(From, To); + +template +concept QuantitySpecExplicitlyConvertibleTo = + QuantitySpec && QuantitySpec && + explicitly_convertible(From, To); + +template +concept QuantitySpecCastableTo = + QuantitySpec && QuantitySpec && + castable(From, To); } // namespace detail MP_UNITS_EXPORT template concept QuantitySpecOf = - QuantitySpec && QuantitySpec && implicitly_convertible(T{}, QS) && + QuantitySpec && QuantitySpec && detail::QuantitySpecConvertibleTo && // the below is to make the following work // static_assert(ReferenceOf); // static_assert(!ReferenceOf); diff --git a/src/core/include/mp-units/framework/reference_concepts.h b/src/core/include/mp-units/framework/reference_concepts.h index 0bd2a49d..79f443b8 100644 --- a/src/core/include/mp-units/framework/reference_concepts.h +++ b/src/core/include/mp-units/framework/reference_concepts.h @@ -108,4 +108,12 @@ concept AbsoluteReference = is_specialization_of; MP_UNITS_EXPORT_END +namespace detail { + +template +concept SameReference = + Reference && Reference && (R1 == R2); + +} // namespace detail + } // namespace mp_units diff --git a/src/core/include/mp-units/framework/system_reference.h b/src/core/include/mp-units/framework/system_reference.h index 5718cb1a..3df21be2 100644 --- a/src/core/include/mp-units/framework/system_reference.h +++ b/src/core/include/mp-units/framework/system_reference.h @@ -66,7 +66,7 @@ struct system_reference { static constexpr auto coherent_unit = CoU; template - requires(convertible(coherent_unit, U{})) + requires detail::UnitConvertibleTo #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 1b6cfcaa..2e661b34 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -518,12 +518,11 @@ template } // namespace detail - MP_UNITS_EXPORT [[nodiscard]] consteval bool operator==(Unit auto lhs, Unit auto 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) && + return convertible(canonical_lhs.reference_unit, canonical_rhs.reference_unit) && canonical_lhs.mag == canonical_rhs.mag; } @@ -612,11 +611,11 @@ inline constexpr auto ppm = parts_per_million; // clang-format on -// convertible_to -template -[[nodiscard]] consteval bool convertible(U1 from, U2 to) +// convertible +template +[[nodiscard]] consteval bool convertible(From from, To to) { - if constexpr (is_same_v) + if constexpr (is_same_v) return true; else return detail::have_same_canonical_reference_unit(from, to); @@ -627,7 +626,7 @@ template template [[nodiscard]] consteval Unit auto common_unit(U1 u1, U2 u2) - requires(detail::have_same_canonical_reference_unit(u1, u2)) + requires(convertible(u1, u2)) { if constexpr (is_same_v) return u1; diff --git a/src/core/include/mp-units/framework/unit_concepts.h b/src/core/include/mp-units/framework/unit_concepts.h index f044e166..2d838351 100644 --- a/src/core/include/mp-units/framework/unit_concepts.h +++ b/src/core/include/mp-units/framework/unit_concepts.h @@ -182,18 +182,22 @@ concept AssociatedUnit = Unit && detail::has_associated_quantity(U{}); * the provided quantity_spec type. */ MP_UNITS_EXPORT template -concept UnitOf = - AssociatedUnit && QuantitySpec && - implicitly_convertible(get_quantity_spec(U{}), QS) && - // the below is to make `dimensionless[radian]` invalid - (get_kind(QS) == get_kind(get_quantity_spec(U{})) || !detail::NestedQuantityKindSpecOf); +concept UnitOf = AssociatedUnit && QuantitySpec && + detail::QuantitySpecConvertibleTo && + // the below is to make `dimensionless[radian]` invalid + (detail::SameQuantitySpec || + !detail::NestedQuantityKindSpecOf); + +MP_UNITS_EXPORT template +[[nodiscard]] consteval bool convertible(From from, To to); namespace detail { -template -[[nodiscard]] consteval bool have_same_canonical_reference_unit(U1 u1, U2 u2); +template +concept UnitConvertibleTo = + Unit && Unit && (convertible(From, To)); -} +} // namespace detail /** * @brief A concept matching all units compatible with the provided unit and quantity spec @@ -201,10 +205,9 @@ template * Satisfied by all units that have the same canonical reference as `U2` and in case they * have associated quantity specification it should satisfy `UnitOf`. */ -MP_UNITS_EXPORT template +MP_UNITS_EXPORT template concept UnitCompatibleWith = - Unit && Unit && QuantitySpec && - (!AssociatedUnit || UnitOf)&&(detail::have_same_canonical_reference_unit(U{}, U2)); - + Unit && Unit && QuantitySpec && + (!AssociatedUnit || UnitOf)&&detail::UnitConvertibleTo; } // namespace mp_units