From 08b7716f5191571aa33484e3adc0a82c34b2782e Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sat, 22 Oct 2022 20:59:44 +0200 Subject: [PATCH] feat: `std::common_type` support for unit added --- src/core/include/units/magnitude.h | 7 +++--- src/core/include/units/unit.h | 36 +++++++++++++++++++++++++---- test/unit_test/static/unit_test.cpp | 36 +++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 8 deletions(-) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index 234a16d5..52e4e789 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -679,6 +679,7 @@ template { return (magnitude<>{} * ... * remove_positive_power(magnitude{})); } + } // namespace detail // Base cases, for when either (or both) inputs are the identity. @@ -698,16 +699,16 @@ template { using detail::remove_positive_power; - if constexpr (get_base(H1) < get_base(H2)) { + if constexpr (detail::get_base(H1) < detail::get_base(H2)) { // When H1 has the smaller base, prepend to result from recursion. return remove_positive_power(magnitude

{}) * common_magnitude(magnitude{}, magnitude{}); - } else if constexpr (get_base(H2) < get_base(H1)) { + } else if constexpr (detail::get_base(H2) < detail::get_base(H1)) { // When H2 has the smaller base, prepend to result from recursion. return remove_positive_power(magnitude

{}) * common_magnitude(magnitude{}, magnitude{}); } else { // When the bases are equal, pick whichever has the lower power. constexpr auto common_tail = common_magnitude(magnitude{}, magnitude{}); - if constexpr (get_exponent(H1) < get_exponent(H2)) { + if constexpr (detail::get_exponent(H1) < detail::get_exponent(H2)) { return magnitude

{} * common_tail; } else { return magnitude

{} * common_tail; diff --git a/src/core/include/units/unit.h b/src/core/include/units/unit.h index 244621b7..2f899b34 100644 --- a/src/core/include/units/unit.h +++ b/src/core/include/units/unit.h @@ -395,12 +395,12 @@ using type_list_of_unit_less = expr_less; * Multiplication by `1` returns the same unit, otherwise `scaled_unit` is being returned. */ template -[[nodiscard]] consteval Unit auto operator*(M mag, U u) +[[nodiscard]] consteval Unit auto operator*(M mag, const U u) { if constexpr (mag == units::mag<1>) return u; else - return scaled_unit>{}; + return scaled_unit{}; } template @@ -700,16 +700,42 @@ template return buffer; } +namespace detail { + + +template +[[nodiscard]] consteval auto common_type_impl(const U1 u1, const U2 u2) +{ + if constexpr (U1{} == U2{}) { + if constexpr (std::derived_from) + return u1; + else + return u2; + } else { + constexpr auto canonical_lhs = detail::get_canonical_unit(U1{}); + constexpr auto canonical_rhs = detail::get_canonical_unit(U2{}); + + if constexpr (is_integral(canonical_lhs.mag / canonical_rhs.mag)) + return u2; + else if constexpr (is_integral(canonical_rhs.mag / canonical_lhs.mag)) + return u1; + else { + constexpr auto cm = common_magnitude(canonical_lhs.mag, canonical_rhs.mag); + return scaled_unit>{}; + } + } +} + +} // namespace detail + } // namespace units namespace std { -// TODO implement this template requires(units::convertible(U1{}, U2{})) struct common_type { - using type = ::units::conditional, std::remove_const_t>, - std::remove_const_t, std::remove_const_t>; + using type = std::remove_const_t; }; } // namespace std diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index 9d55eb61..eac02572 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -64,6 +64,10 @@ inline constexpr struct tonne_ : named_unit<"t", mag<1000> * kilogram> {} tonne; inline constexpr struct dalton_ : named_unit<"Da", mag * mag_power<10, -27> * kilogram> {} dalton; inline constexpr struct electronvolt_ : named_unit<"eV", mag * mag_power<10, -19> * joule> {} electronvolt; +inline constexpr struct yard_ : named_unit<"yd", mag * metre> {} yard; +inline constexpr struct foot_ : named_unit<"ft", mag * yard> {} foot; +inline constexpr struct mile_ : named_unit<"mi", mag<1760> * yard> {} mile; + inline constexpr struct kilometre_ : decltype(si::kilo) {} kilometre; inline constexpr struct kilojoule_ : decltype(si::kilo) {} kilojoule; // clang-format on @@ -353,6 +357,37 @@ static_assert(joule == newton * metre); static_assert(watt == joule / second); static_assert(watt == kilogram * square / cubic); +// common_type +static_assert(std::is_same_v, gram_>); +static_assert(std::is_same_v, kilogram_>); +static_assert(std::is_same_v), decltype(kilogram)>, kilogram_>); +static_assert(std::is_same_v)>, kilogram_>); +static_assert(std::is_same_v * gram), decltype(kilogram)>, kilogram_>); +static_assert(std::is_same_v * gram)>, kilogram_>); +static_assert(std::is_same_v, hertz_>); +static_assert(std::is_same_v, hertz_>); +static_assert(std::is_same_v, gram_>); +static_assert(std::is_same_v, gram_>); +static_assert(std::is_same_v, second_>); +static_assert(std::is_same_v, second_>); +static_assert(std::is_same_v, minute_>); +static_assert(std::is_same_v, minute_>); +static_assert(std::is_same_v), decltype(si::milli)>, + std::remove_const_t)>>); +static_assert(std::is_same_v), decltype(si::kilo)>, + std::remove_const_t)>>); +static_assert(std::is_same_v, yard_>); +static_assert(std::is_same_v, yard_>); +// TODO The below have long/unreadable magnitude types +static_assert(std::is_same_v, + scaled_unit, derived_unit>>>); +static_assert(std::is_same_v, + scaled_unit, derived_unit>>>); +static_assert( + std::is_same_v, scaled_unit, metre_>>); +static_assert( + std::is_same_v, scaled_unit, metre_>>); + // unit symbols #ifdef __cpp_lib_constexpr_string @@ -379,6 +414,7 @@ static_assert(unit_symbol(mag<100> * metre, {.encoding = ascii}) == "x 10^2 m"); static_assert(unit_symbol(mag<60> * second) == "[6 × 10¹] s"); static_assert(unit_symbol(mag<60> * second, {.encoding = ascii}) == "[6 x 10^1] s"); +// derived units static_assert(unit_symbol(one) == ""); static_assert(unit_symbol(square) == "m²"); static_assert(unit_symbol(square, {.encoding = ascii}) == "m^2");