From 569f27af8b1a7399ccd44d8142ce433b08d153e9 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sat, 5 Oct 2024 17:52:52 +0200 Subject: [PATCH] fix: `common_unit` handling fixed for some corner cases --- .../mp-units/bits/get_associated_quantity.h | 17 +++++++++++++++-- src/core/include/mp-units/framework/quantity.h | 11 ++++++----- src/core/include/mp-units/framework/unit.h | 16 +++++++++++----- test/static/concepts_test.cpp | 7 +++++++ test/static/quantity_test.cpp | 10 ++++++++++ test/static/unit_symbol_test.cpp | 5 +++++ test/static/unit_test.cpp | 4 ++++ 7 files changed, 58 insertions(+), 12 deletions(-) diff --git a/src/core/include/mp-units/bits/get_associated_quantity.h b/src/core/include/mp-units/bits/get_associated_quantity.h index d976b17e..819fe90e 100644 --- a/src/core/include/mp-units/bits/get_associated_quantity.h +++ b/src/core/include/mp-units/bits/get_associated_quantity.h @@ -26,7 +26,12 @@ #include #include -namespace mp_units::detail { +namespace mp_units { + +template +struct common_unit; + +namespace detail { template [[nodiscard]] consteval auto all_are_kinds(U); @@ -61,6 +66,12 @@ template template using to_quantity_spec = decltype(get_associated_quantity_impl(U{})); +template +[[nodiscard]] consteval auto get_associated_quantity_impl(common_unit) +{ + return get_common_quantity_spec(get_associated_quantity_impl(Us{})...); +} + template [[nodiscard]] consteval auto get_associated_quantity_impl(U u) { @@ -83,4 +94,6 @@ template return get_associated_quantity_impl(u); } -} // namespace mp_units::detail +} // namespace detail + +} // namespace mp_units diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index 9e1f2c21..9bfc4bf3 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -93,16 +93,17 @@ concept HaveCommonReferenceImpl = requires { get_common_reference(R1, R2); }; template concept HaveCommonReference = HaveCommonReferenceImpl; +template +using common_quantity_for = quantity>; + template concept CommonlyInvocableQuantities = Quantity && Quantity && HaveCommonReference && + std::convertible_to> && + std::convertible_to> && InvocableQuantities; -template - requires CommonlyInvocableQuantities -using common_quantity_for = quantity>; - template concept SameValueAs = SameReference && std::same_as; diff --git a/src/core/include/mp-units/framework/unit.h b/src/core/include/mp-units/framework/unit.h index 1c1d315d..48c3cdf2 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -60,9 +60,6 @@ import std; namespace mp_units { -template -struct common_unit; - namespace detail { template @@ -784,9 +781,18 @@ constexpr Out unit_symbol_impl(Out out, const scaled_unit_impl& u, const u template [[nodiscard]] consteval Unit auto get_common_unit_in(common_unit, U u) { + auto get_magnitude = [&]() { + if constexpr (requires { common_unit::mag; }) + return common_unit::mag; + else + return mag<1>; + }; constexpr auto canonical_u = get_canonical_unit(u); - constexpr Magnitude auto mag = common_unit::mag / canonical_u.mag; - return scaled_unit{}; + constexpr Magnitude auto cmag = get_magnitude() / canonical_u.mag; + if constexpr (cmag == mag<1>) + return u; + else + return scaled_unit{}; } template Out, typename U, typename... Rest> diff --git a/test/static/concepts_test.cpp b/test/static/concepts_test.cpp index c67ff710..d4397828 100644 --- a/test/static/concepts_test.cpp +++ b/test/static/concepts_test.cpp @@ -214,6 +214,7 @@ static_assert(UnitOf); static_assert(UnitOf); static_assert(UnitOf); static_assert(UnitOf); +static_assert(UnitOf); static_assert(UnitOf); static_assert(UnitOf); static_assert(!UnitOf); @@ -247,9 +248,15 @@ static_assert(ReferenceOf); static_assert(ReferenceOf); static_assert(ReferenceOf); static_assert(ReferenceOf); +static_assert(ReferenceOf); +static_assert(ReferenceOf); static_assert(!ReferenceOf); +static_assert(!ReferenceOf); +static_assert(!ReferenceOf); static_assert(ReferenceOf); +static_assert(ReferenceOf); static_assert(!ReferenceOf); +static_assert(!ReferenceOf); static_assert(ReferenceOf); static_assert(ReferenceOf); static_assert(!ReferenceOf); diff --git a/test/static/quantity_test.cpp b/test/static/quantity_test.cpp index 23853b92..d0036051 100644 --- a/test/static/quantity_test.cpp +++ b/test/static/quantity_test.cpp @@ -273,6 +273,10 @@ static_assert(quantity(2000 * m).force_in(km).numerical_val static_assert((15. * m).in(nm).numerical_value_in(m) == 15.); static_assert((15'000. * nm).in(m).numerical_value_in(nm) == 15'000.); +// check if unit conversion works - don't bother about the actual result +static_assert((1. * rad + 1. * deg).in(rad) != 0 * rad); +static_assert((1. * rad + 1. * deg).in(deg) != 0 * deg); + #if MP_UNITS_HOSTED using namespace std::complex_literals; static_assert(((2. + 1i) * V).in(mV).numerical_value_in(mV) == 2000. + 1000i); @@ -788,6 +792,12 @@ consteval bool invalid_arithmetic(Ts... ts) static_assert(invalid_arithmetic(5 * isq::activity[Bq], 5 * isq::frequency[Hz])); static_assert(invalid_arithmetic(5 * isq::activity[Bq], 10 / (2 * isq::time[s]), 5 * isq::frequency[Hz])); +// irrational conversion factors require floating point representation +static_assert(invalid_arithmetic(1 * rad, 1 * deg)); +static_assert(is_of_type<1. * rad + 1 * deg, quantity{}, double>>); +static_assert(is_of_type<1 * rad + 1. * deg, quantity{}, double>>); +static_assert(is_of_type<1. * rad + 1. * deg, quantity{}, double>>); + // Physical constants static_assert(1 * si::si2019::speed_of_light_in_vacuum + 10 * isq::speed[m / s] == 299'792'468 * isq::speed[m / s]); diff --git a/test/static/unit_symbol_test.cpp b/test/static/unit_symbol_test.cpp index f8dc4b6c..01f9448a 100644 --- a/test/static/unit_symbol_test.cpp +++ b/test/static/unit_symbol_test.cpp @@ -20,6 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#include #include #include #include @@ -209,6 +210,10 @@ static_assert(unit_symbol(get_common_unit(kilo / hour, metre / second) / "EQUIV{[1/5 km/h], [1/18 m/s]}/s"); static_assert(unit_symbol(get_common_unit(kilo / hour, metre / second) * second) == "EQUIV{[1/5 km/h], [1/18 m/s]} s"); +static_assert(unit_symbol(get_common_unit(radian, degree)) == "EQUIV{[1/๐œ‹ยฐ], [1/180 rad]}"); +static_assert(unit_symbol(get_common_unit(angular::radian, angular::revolution)) == "EQUIV{rad, [2โปยน ๐œ‹โปยน rev]}"); +static_assert(unit_symbol(get_common_unit(angular::radian, angular::revolution)) == + "EQUIV{rad, [1/(2 ๐œ‹) rev]}"); // derived units static_assert(unit_symbol(one) == ""); // NOLINT(readability-container-size-empty) diff --git a/test/static/unit_test.cpp b/test/static/unit_test.cpp index 7af8dae0..f25b9987 100644 --- a/test/static/unit_test.cpp +++ b/test/static/unit_test.cpp @@ -69,6 +69,7 @@ inline constexpr struct nu_second_ final : named_unit<"s"> {} nu_second; // derived named units inline constexpr struct radian_ final : named_unit<"rad", metre / metre> {} radian; +inline constexpr struct revolution_ final : named_unit<"rev", mag<2> * mag * radian> {} revolution; inline constexpr struct steradian_ final : named_unit<"sr", square(metre) / square(metre)> {} steradian; inline constexpr struct hertz_ final : named_unit<"Hz", inverse(second)> {} hertz; inline constexpr struct becquerel_ final : named_unit<"Bq", inverse(second)> {} becquerel; @@ -539,6 +540,9 @@ static_assert(is_of_type); static_assert( is_of_type>>); +static_assert(is_of_type>); +static_assert(is_of_type>); + // those should return instantiations of the `common_unit` class template static_assert(is_of_type, mile_>>); static_assert(is_of_type, mile_>>);