From 1b2d77af41b51f215a7d402c9c03eec263db8e29 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sat, 28 Jan 2023 11:00:50 +0100 Subject: [PATCH] feat: quantities of the same kind can now be added, subtracted, or compared to each other --- .../mp_units/bits/external/type_traits.h | 43 ++++++++++ src/core/include/mp_units/quantity.h | 8 +- src/core/include/mp_units/quantity_spec.h | 86 ++++++++++++++++--- test/unit_test/static/quantity_spec_test.cpp | 26 +++++- 4 files changed, 143 insertions(+), 20 deletions(-) diff --git a/src/core/include/mp_units/bits/external/type_traits.h b/src/core/include/mp_units/bits/external/type_traits.h index ee896101..bc227acc 100644 --- a/src/core/include/mp_units/bits/external/type_traits.h +++ b/src/core/include/mp_units/bits/external/type_traits.h @@ -80,4 +80,47 @@ concept is_derived_from_specialization_of = requires(T* t) { detail::to_base_spe template concept one_of = (false || ... || std::same_as); +template +consteval bool contains() +{ + return (false || ... || is_same_v, T>); +} + +template typename T, typename... Ts> +consteval bool contains() +{ + return (false || ... || is_specialization_of); +} + +template auto V> +consteval auto get() +{ + return V; +} + +template +consteval auto get() +{ + if constexpr (is_same_v>) + return V1; + else + return get(); +} + +template typename T, typename T1> + requires is_specialization_of +consteval auto get() +{ + return T1{}; +} + +template typename T, typename T1, typename T2, typename... Ts> +consteval auto get() +{ + if constexpr (is_specialization_of) + return T1{}; + else + return get(); +} + } // namespace mp_units diff --git a/src/core/include/mp_units/quantity.h b/src/core/include/mp_units/quantity.h index 07267b79..fb3c0274 100644 --- a/src/core/include/mp_units/quantity.h +++ b/src/core/include/mp_units/quantity.h @@ -455,7 +455,7 @@ explicit quantity(Q) -> quantity::reference, typename qu // non-member binary operators template - requires(interconvertible(Q1::reference, Q2::reference)) && + requires(get_kind(Q1::quantity_spec) == get_kind(Q2::quantity_spec)) && invoke_result_of_, typename Q1::rep, typename Q2::rep> [[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs) @@ -466,7 +466,7 @@ template } template - requires(interconvertible(Q1::reference, Q2::reference)) && + requires(get_kind(Q1::quantity_spec) == get_kind(Q2::quantity_spec)) && invoke_result_of_, typename Q1::rep, typename Q2::rep> [[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs) @@ -505,7 +505,7 @@ template } template - requires(interconvertible(Q1::reference, Q2::reference)) && + requires(get_kind(Q1::quantity_spec) == get_kind(Q2::quantity_spec)) && std::three_way_comparable_with [[nodiscard]] constexpr auto operator<=>(const Q1& lhs, const Q2& rhs) { @@ -514,7 +514,7 @@ template } template - requires(interconvertible(Q1::reference, Q2::reference)) && + requires(get_kind(Q1::quantity_spec) == get_kind(Q2::quantity_spec)) && std::equality_comparable_with [[nodiscard]] constexpr bool operator==(const Q1& lhs, const Q2& rhs) { diff --git a/src/core/include/mp_units/quantity_spec.h b/src/core/include/mp_units/quantity_spec.h index 53461df7..126a2961 100644 --- a/src/core/include/mp_units/quantity_spec.h +++ b/src/core/include/mp_units/quantity_spec.h @@ -29,10 +29,26 @@ #include #include #include -#include namespace mp_units { +template +struct kind_of_ { + using type = Q; +}; + +template +consteval kind_of_> kind_of() +{ + return {}; +} + +template +consteval kind_of_ kind_of() +{ + return {}; +} + namespace detail { // TODO revise the note in the below comment @@ -65,8 +81,8 @@ template template [[nodiscard]] consteval quantity_character quantity_character_init(quantity_character ch) { - if constexpr (one_of...>) - return std::get(std::make_tuple(Args...)); + if constexpr (contains()) + return get(); else return ch; } @@ -411,6 +427,56 @@ inline constexpr bool is_dimensionless = true; } // namespace detail +namespace detail { + +#ifdef __cpp_explicit_this_parameter +template +[[nodiscard]] consteval bool defines_kind(quantity_spec) +#else +template +[[nodiscard]] consteval bool defines_kind(quantity_spec) +#endif +{ + return contains...>(); +} + +#ifdef __cpp_explicit_this_parameter +template +[[nodiscard]] consteval QuantitySpec auto fetch_kind(quantity_spec) +#else +template +[[nodiscard]] consteval QuantitySpec auto fetch_kind(quantity_spec) +#endif +{ + return typename decltype(get...>())::type{}; +} + +template + requires requires(Q q) { get_kind(q); } +using to_kind = std::remove_const_t; + +} // namespace detail + +template +[[nodiscard]] consteval QuantitySpec auto get_kind(Q q) +{ + if constexpr (requires { Q::_parent_; }) { + // named non-base quantity + if constexpr (detail::defines_kind(q)) + return detail::fetch_kind(q); + else + return get_kind(Q::_parent_); + } else if constexpr (requires { + typename Q::_equation_; + }) { // TODO can we just check if it is derived from the derived_quantity_spec? + // derived quantity + return detail::expr_map(typename Q::_base_{}); + } else { + // base quantity + return q; + } +} // Operators @@ -460,17 +526,13 @@ template [[nodiscard]] consteval auto common_quantity_spec(QuantitySpec auto q) { return q; } template -[[nodiscard]] consteval auto common_quantity_spec(Q1 q1, Q2 q2) - requires(interconvertible(q1, q2)) +[[nodiscard]] consteval QuantitySpec auto common_quantity_spec(Q1 q1, Q2 q2) + requires(get_kind(q1) == get_kind(q2)) { - if constexpr (std::derived_from) - return q1; - else if constexpr (std::derived_from) - return q2; - else if constexpr (NamedQuantitySpec) - return q1; + if constexpr (detail::have_common_base(q1, q2)) + return detail::find_common_base(q1, q2); else - return q2; + return get_kind(q1); } [[nodiscard]] consteval auto common_quantity_spec(QuantitySpec auto q1, QuantitySpec auto q2, QuantitySpec auto q3, diff --git a/test/unit_test/static/quantity_spec_test.cpp b/test/unit_test/static/quantity_spec_test.cpp index 88ae441d..b807fa0a 100644 --- a/test/unit_test/static/quantity_spec_test.cpp +++ b/test/unit_test/static/quantity_spec_test.cpp @@ -51,8 +51,8 @@ QUANTITY_SPEC_(distance, path_length); QUANTITY_SPEC_(position_vector, length, quantity_character::vector); QUANTITY_SPEC_(period_duration, time); -QUANTITY_SPEC_(frequency, 1 / period_duration); -QUANTITY_SPEC_(action, 1 / time); +QUANTITY_SPEC_(frequency, 1 / period_duration, kind_of()); +QUANTITY_SPEC_(action, 1 / time, kind_of()); QUANTITY_SPEC_(area, pow<2>(length)); QUANTITY_SPEC_(volume, pow<3>(length)); QUANTITY_SPEC_(velocity, position_vector / time); @@ -66,8 +66,9 @@ QUANTITY_SPEC_(stress, pressure, quantity_character::tensor); QUANTITY_SPEC_(strain, dimensionless, quantity_character::tensor); QUANTITY_SPEC_(power, force* velocity, quantity_character::scalar); QUANTITY_SPEC_(efficiency, power / power); -QUANTITY_SPEC_(potential_energy, mass* acceleration* height); -QUANTITY_SPEC_(energy, force * length); +QUANTITY_SPEC_(energy, force * length, kind_of()); +QUANTITY_SPEC_(potential_energy, mass* acceleration* height, kind_of()); +QUANTITY_SPEC_(kinetic_energy, mass* pow<2>(speed), kind_of()); // clang-format on // concepts verification @@ -208,6 +209,23 @@ concept invalid_operations = requires { }; static_assert(invalid_operations