From 70a18fec0cc8746ba6fee814659cdbafe3991b27 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 9 Oct 2024 17:26:13 +0200 Subject: [PATCH] refactor: :boom: `op==(U1, U2)` now checks for the same type (old behavior available as `equivalent(U1, U2)`) + `convertible` now verifies associated `quantity_spec` as well --- src/core/include/mp-units/bits/sudo_cast.h | 11 +- .../include/mp-units/framework/quantity.h | 8 +- src/core/include/mp-units/framework/unit.h | 26 ++-- test/static/unit_test.cpp | 118 +++++++++--------- 4 files changed, 91 insertions(+), 72 deletions(-) diff --git a/src/core/include/mp-units/bits/sudo_cast.h b/src/core/include/mp-units/bits/sudo_cast.h index b39280020..e3e94f925 100644 --- a/src/core/include/mp-units/bits/sudo_cast.h +++ b/src/core/include/mp-units/bits/sudo_cast.h @@ -96,13 +96,13 @@ struct conversion_value_traits { */ template> requires(castable(From::quantity_spec, To::quantity_spec)) && - ((From::unit == To::unit && std::constructible_from) || - (From::unit != To::unit)) // && scalable_with_)) + (((equivalent(From::unit, To::unit)) && std::constructible_from) || + (!equivalent(From::unit, To::unit))) // && scalable_with_)) // TODO how to constrain the second part here? [[nodiscard]] constexpr To sudo_cast(FwdFrom&& q) { constexpr auto q_unit = From::unit; - if constexpr (q_unit == To::unit) { + if constexpr (equivalent(q_unit, To::unit)) { // no scaling of the number needed return {static_cast(std::forward(q).numerical_value_is_an_implementation_detail_), To::reference}; // this is the only (and recommended) way to do a truncating conversion on a number, so we @@ -149,8 +149,9 @@ template> requires(castable(FromQP::quantity_spec, ToQP::quantity_spec)) && (detail::same_absolute_point_origins(ToQP::point_origin, FromQP::point_origin)) && - ((FromQP::unit == ToQP::unit && std::constructible_from) || - (FromQP::unit != ToQP::unit)) + (((equivalent(FromQP::unit, ToQP::unit)) && + std::constructible_from) || + (!equivalent(FromQP::unit, ToQP::unit))) [[nodiscard]] constexpr QuantityPoint auto sudo_cast(FwdFromQP&& qp) { if constexpr (is_same_v, diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index 9bfc4bf3f..67ea73ff6 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -105,7 +105,7 @@ concept CommonlyInvocableQuantities = InvocableQuantities; template -concept SameValueAs = SameReference && std::same_as; +concept SameValueAs = (equivalent(get_unit(R1), get_unit(R2))) && std::convertible_to; template using quantity_like_type = quantity::reference, typename quantity_like_traits::rep>; @@ -261,21 +261,21 @@ class quantity { // data access template - requires(U{} == unit) + requires(equivalent(U{}, unit)) [[nodiscard]] constexpr rep& numerical_value_ref_in(U) & noexcept { return numerical_value_is_an_implementation_detail_; } template - requires(U{} == unit) + requires(equivalent(U{}, unit)) [[nodiscard]] constexpr const rep& numerical_value_ref_in(U) const& noexcept { return numerical_value_is_an_implementation_detail_; } template - requires(U{} == unit) + requires(equivalent(U{}, unit)) constexpr const rep&& numerical_value_ref_in(U) const&& noexcept #if __cpp_deleted_function = delete("Can't form a reference to a temporary"); diff --git a/src/core/include/mp-units/framework/unit.h b/src/core/include/mp-units/framework/unit.h index 0e97ed9b8..804890acc 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -122,6 +122,12 @@ struct unit_less : std::bool_constant() < type_name()> {}; template using type_list_of_unit_less = expr_less; +template +concept PotentiallyConvertibleTo = Unit && Unit && + ((AssociatedUnit && AssociatedUnit && + implicitly_convertible(get_quantity_spec(From{}), get_quantity_spec(To{}))) || + (!AssociatedUnit && !AssociatedUnit)); + } // namespace detail // TODO this should really be in the `details` namespace but is used in `chrono.h` (a part of mp_units.systems) @@ -134,9 +140,11 @@ template { if constexpr (is_same_v) return true; - else + else if constexpr (detail::PotentiallyConvertibleTo) return is_same_v; + else + return false; } namespace detail { @@ -192,12 +200,16 @@ struct unit_interface { return expr_divide(lhs, rhs); } - [[nodiscard]] friend consteval bool operator==(Unit auto lhs, Unit auto rhs) + template + [[nodiscard]] friend consteval bool operator==(Lhs, Rhs) + { + return is_same_v; + } + + [[nodiscard]] friend consteval bool equivalent(Unit auto lhs, Unit auto rhs) + requires(convertible(lhs, rhs)) { - auto canonical_lhs = get_canonical_unit(lhs); - auto canonical_rhs = get_canonical_unit(rhs); - return convertible(canonical_lhs.reference_unit, canonical_rhs.reference_unit) && - canonical_lhs.mag == canonical_rhs.mag; + return get_canonical_unit(lhs).mag == get_canonical_unit(rhs).mag; } }; @@ -662,7 +674,7 @@ template { if constexpr (is_same_v) return u1; - else if constexpr (U1{} == U2{}) { + else if constexpr (equivalent(U1{}, U2{})) { if constexpr (std::derived_from) return u1; else if constexpr (std::derived_from) diff --git a/test/static/unit_test.cpp b/test/static/unit_test.cpp index 94d3d942b..1f9782aa9 100644 --- a/test/static/unit_test.cpp +++ b/test/static/unit_test.cpp @@ -23,6 +23,7 @@ #include "test_tools.h" #include #include +#include #include #ifdef MP_UNITS_IMPORT_STD import std; @@ -38,19 +39,7 @@ using namespace mp_units::detail; using one_ = struct one; using percent_ = struct percent; -// base dimensions // clang-format off -inline constexpr struct dim_length_ final : base_dimension<"L"> {} dim_length; -inline constexpr struct dim_mass_ final : base_dimension<"M"> {} dim_mass; -inline constexpr struct dim_time_ final : base_dimension<"T"> {} dim_time; -inline constexpr struct dim_thermodynamic_temperature_ final : base_dimension {} dim_thermodynamic_temperature; - -// quantities specification -QUANTITY_SPEC_(length, dim_length); -QUANTITY_SPEC_(mass, dim_mass); -QUANTITY_SPEC_(time, dim_time); -QUANTITY_SPEC_(thermodynamic_temperature, dim_thermodynamic_temperature); - // prefixes template struct milli_ final : prefixed_unit<"m", mag_power<10, -3>, U{}> {}; template struct kilo_ final : prefixed_unit<"k", mag_power<10, 3>, U{}> {}; @@ -58,21 +47,21 @@ template constexpr milli_ constexpr kilo_ kilo; // base units -inline constexpr struct second_ final : named_unit<"s", kind_of