From bc98254e7a007fd0e1ec14ade3d4bca67d899a57 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Sat, 9 Apr 2022 17:35:15 +0000 Subject: [PATCH 01/35] Support `-` and `<=>` in `ratio` --- src/core/include/units/ratio.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/include/units/ratio.h b/src/core/include/units/ratio.h index 0fb2f428..52270e2d 100644 --- a/src/core/include/units/ratio.h +++ b/src/core/include/units/ratio.h @@ -78,6 +78,10 @@ struct ratio { return ratio{lhs.num * rhs.den + lhs.den * rhs.num, lhs.den * rhs.den, common_exp}; } + [[nodiscard]] friend constexpr ratio operator-(const ratio& lhs, const ratio& rhs) { return lhs + (-rhs); } + + [[nodiscard]] friend constexpr auto operator<=>(const ratio& lhs, const ratio& rhs) { return (lhs - rhs).num <=> 0; } + [[nodiscard]] friend constexpr ratio operator*(const ratio& lhs, const ratio& rhs) { const std::intmax_t gcd1 = std::gcd(lhs.num, rhs.den); From af8eec11022230756896f430740eebf0237e6fb6 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Sat, 9 Apr 2022 17:41:08 +0000 Subject: [PATCH 02/35] Implement "common Magnitude" of two Magnitudes --- src/core/include/units/magnitude.h | 68 +++++++++++++++++++++++ test/unit_test/runtime/magnitude_test.cpp | 21 +++++++ 2 files changed, 89 insertions(+) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index 88d003e7..545b7d99 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -525,6 +525,74 @@ constexpr ratio as_ratio(Magnitude auto m) } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common Magnitude. +// +// The "common Magnitude" C, of two Magnitudes M1 and M2, is the largest Magnitude such that each of its inputs is +// expressible by only positive basis powers relative to C. That is, both (M1 / C) and (M2 / C) contain only positive +// powers in the expansion on our basis. +// +// For rational Magnitudes (or, more precisely, Magnitudes that are rational _relative to each other_), this reduces to +// the familiar convention from the std::chrono library: it is the largest Magnitude C such that each input Magnitude is +// an _integer multiple_ of C. The connection can be seen by considering the definition in the above paragraph, and +// recognizing that both the bases and the powers are all integers for rational Magnitudes. +// +// For relatively _irrational_ Magnitudes (whether from irrational bases, or fractional powers of integer bases), the +// notion of a "common type" becomes less important, because there is no way to preserve pure integer multiplication. +// When we go to retrieve our value, we'll be stuck with a floating point approximation no matter what choice we make. +// Thus, we make the _simplest_ choice which reproduces the correct convention in the rational case: namely, taking the +// minimum power for each base (where absent bases implicitly have a power of 0). + +namespace detail { +template +constexpr auto remove_positive_power(magnitude m) +{ + if constexpr (numerator(BP.power) < 0) { + return m; + } else { + return magnitude<>{}; + } +} + +template +constexpr auto remove_positive_powers(magnitude) +{ + return (magnitude<>{} * ... * remove_positive_power(magnitude{})); +} +} // namespace detail + +// Base cases, for when either (or both) inputs are the identity. +constexpr auto common_magnitude(magnitude<>, magnitude<>) { return magnitude<>{}; } +constexpr auto common_magnitude(magnitude<>, Magnitude auto m) { return detail::remove_positive_powers(m); } +constexpr auto common_magnitude(Magnitude auto m, magnitude<>) { return detail::remove_positive_powers(m); } + +// Recursive case for the common Magnitude of any two non-identity Magnitudes. +template +constexpr auto common_magnitude(magnitude m1, magnitude m2) +{ + using namespace detail; + + // Case for when H1 has the smaller base. + if constexpr (H1.get_base() < H2.get_base()) { + return remove_positive_power(magnitude

{}) * common_magnitude(magnitude{}, m2); + } + + // Case for when H2 has the smaller base. + if constexpr (H2.get_base() < H1.get_base()) { + return remove_positive_power(magnitude

{}) * common_magnitude(m1, magnitude{}); + } + + // Case for equal bases. + if constexpr (H1.get_base() == H2.get_base()) { + constexpr auto common_tail = common_magnitude(magnitude{}, magnitude{}); + if constexpr (H1.power < H2.power) { + return magnitude

{} * common_tail; + } else { + return magnitude

{} * common_tail; + } + } +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // `as_magnitude()` implementation. diff --git a/test/unit_test/runtime/magnitude_test.cpp b/test/unit_test/runtime/magnitude_test.cpp index e5d46f26..0715df8f 100644 --- a/test/unit_test/runtime/magnitude_test.cpp +++ b/test/unit_test/runtime/magnitude_test.cpp @@ -292,6 +292,27 @@ TEST_CASE("Multiplication works for magnitudes") } } +TEST_CASE("Common Magnitude") +{ + SECTION("Identity for identical magnitudes") + { + CHECK(common_magnitude(as_magnitude<1>(), as_magnitude<1>()) == as_magnitude<1>()); + CHECK(common_magnitude(as_magnitude<15>(), as_magnitude<15>()) == as_magnitude<15>()); + CHECK(common_magnitude(pi_to_the(), pi_to_the()) == pi_to_the()); + } + + SECTION("Greatest Common Factor for integers") + { + CHECK(common_magnitude(as_magnitude<24>(), as_magnitude<36>()) == as_magnitude<12>()); + CHECK(common_magnitude(as_magnitude<24>(), as_magnitude<37>()) == as_magnitude<1>()); + } + + SECTION("Handles fractions") + { + CHECK(common_magnitude(as_magnitude(), as_magnitude()) == as_magnitude()); + } +} + TEST_CASE("Division works for magnitudes") { SECTION("Dividing anything by itself reduces to null magnitude") From 85d6e35d79d668a2e65d695429d39a092a3ae4d2 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Sat, 9 Apr 2022 17:42:59 +0000 Subject: [PATCH 03/35] Loosen requirements on Rep for Magnitude This is necessary to be able to use Magnitudes in Units with a Rep that is not `std::is_arithmetic`. --- src/core/include/units/magnitude.h | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index 545b7d99..e3f16666 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -137,13 +137,14 @@ constexpr auto inverse(BasePower auto bp) // `widen_t` gives the widest arithmetic type in the same category, for intermediate computations. template - requires std::is_arithmetic_v -using widen_t = std::conditional_t, long double, - std::conditional_t, std::intmax_t, std::uintmax_t>>; +using widen_t = + std::conditional_t, + std::conditional_t, long double, + std::conditional_t, std::intmax_t, std::uintmax_t>>, + T>; // Raise an arbitrary arithmetic type to a positive integer power at compile time. template - requires std::is_arithmetic_v constexpr T int_power(T base, std::integral auto exp) { // As this function should only be called at compile time, the exceptions herein function as @@ -166,7 +167,7 @@ constexpr T int_power(T base, std::integral auto exp) // template parameter, rather than a function parameter. if (exp == 0) { - return 1; + return T{1}; } if (exp % 2 == 1) { @@ -178,7 +179,6 @@ constexpr T int_power(T base, std::integral auto exp) template - requires std::is_arithmetic_v constexpr widen_t compute_base_power(BasePower auto bp) { // This utility can only handle integer powers. To compute rational powers at compile time, we'll @@ -197,11 +197,11 @@ constexpr widen_t compute_base_power(BasePower auto bp) if constexpr (std::is_integral_v) { throw std::invalid_argument{"Cannot represent reciprocal as integer"}; } else { - return 1 / compute_base_power(inverse(bp)); + return T{1} / compute_base_power(inverse(bp)); } } - auto power = bp.power.num * int_power(10, bp.power.exp); + auto power = numerator(bp.power); return int_power(static_cast>(bp.get_base()), power); } @@ -210,8 +210,8 @@ constexpr widen_t compute_base_power(BasePower auto bp) // The input is the desired result, but in a (wider) intermediate type. The point of this function // is to cast to the desired type, but avoid overflow in doing so. template - requires std::is_arithmetic_v && std::is_arithmetic_v && - (std::is_integral_v == std::is_integral_v) +// TODO(chogg): Migrate this to use `treat_as_floating_point`. + requires(!std::is_integral_v || std::is_integral_v) constexpr To checked_static_cast(From x) { // This function should only ever be called at compile time. The purpose of these exceptions is @@ -220,10 +220,6 @@ constexpr To checked_static_cast(From x) if (!std::in_range(x)) { throw std::invalid_argument{"Cannot represent magnitude in this type"}; } - } else { - if (x < std::numeric_limits::min() || x > std::numeric_limits::max()) { - throw std::invalid_argument{"Cannot represent magnitude in this type"}; - } } return static_cast(x); @@ -388,7 +384,8 @@ concept Magnitude = detail::is_magnitude; * @brief The value of a Magnitude in a desired type T. */ template - requires std::is_floating_point_v || (std::is_integral_v && is_integral(magnitude{})) +// TODO(chogg): Migrate this to use `treat_as_floating_point`. + requires(!std::is_integral_v || is_integral(magnitude{})) constexpr T get_value(const magnitude&) { // Force the expression to be evaluated in a constexpr context, to catch, e.g., overflow. From 5c0eb0667c97cfa4e48eea011c5259e9e88211e0 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Sat, 9 Apr 2022 17:48:04 +0000 Subject: [PATCH 04/35] Remove `inverse_if_negative()` Fixes #332. --- src/core/include/units/bits/derived_unit.h | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/core/include/units/bits/derived_unit.h b/src/core/include/units/bits/derived_unit.h index f078c26c..01ebbf23 100644 --- a/src/core/include/units/bits/derived_unit.h +++ b/src/core/include/units/bits/derived_unit.h @@ -23,6 +23,7 @@ #pragma once #include +#include namespace units::detail { @@ -35,23 +36,20 @@ inline constexpr bool same_scaled_units, Us...> = (UnitOf -constexpr ratio inverse_if_negative(const ratio& r) +template +constexpr Magnitude auto derived_mag(exponent_list) { - if constexpr (E::num * E::den > 0) - return r; - else - return inverse(r); + return (as_magnitude<1>() * ... * + pow(Us::mag / dimension_unit::mag)); } template -constexpr ratio derived_ratio(exponent_list) +constexpr ratio derived_ratio(exponent_list es) { - return (... * inverse_if_negative( - pow(Us::ratio / dimension_unit::ratio))); + return as_ratio(derived_mag(es)); } template -using derived_unit = scaled_unit(typename D::recipe()), typename D::coherent_unit::reference>; +using derived_unit = scaled_unit(typename D::recipe()), typename D::coherent_unit::reference>; } // namespace units::detail From 5afe7766e97f47ae9866e69503bfd9fe9e13a146 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Sat, 9 Apr 2022 17:49:39 +0000 Subject: [PATCH 05/35] Use Magnitude implementation for `base_units_ratio` This resolves a TODO and lets us use arbitrary exponent denominators. I also attempt to clarify the semantics. This is based on my best effort of understanding pre-existing concepts in the library, so I hope I got it right! --- .../include/units/bits/base_units_ratio.h | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/core/include/units/bits/base_units_ratio.h b/src/core/include/units/bits/base_units_ratio.h index c29658c2..dad86a67 100644 --- a/src/core/include/units/bits/base_units_ratio.h +++ b/src/core/include/units/bits/base_units_ratio.h @@ -24,29 +24,35 @@ #include #include +#include #include namespace units::detail { -template - requires(E::den == 1 || E::den == 2) // TODO provide support for any den -constexpr ratio exp_ratio() +/** + * @brief Calculates the "absolute" magnitude of the derived dimension defined by this list. + * + * "Absolute" magnitudes are not physically observable: only ratios of magnitudes are. For example: if we multiplied + * all magnitudes in the system by the same constant, no meaningful results would change. However, in practice, we need + * to make some global choice for the "absolute" values of magnitudes, so that we can compute their ratios. + * + * The point of this function is to compute the absolute magnitude of a derived dimension, in terms of the absolute + * magnitudes of its constituent dimensions. + */ +template +constexpr Magnitude auto absolute_magnitude(exponent_list) { - const ratio base_ratio = E::dimension::base_unit::ratio; - const ratio positive_ratio = - E::num * E::den < 0 ? ratio(base_ratio.den, base_ratio.num, -base_ratio.exp) : base_ratio; - const std::intmax_t N = E::num * E::den < 0 ? -E::num : E::num; - const ratio ratio_pow = pow(positive_ratio); - return E::den == 2 ? sqrt(ratio_pow) : ratio_pow; + return (pow(Es::dimension::base_unit::mag) * ... * magnitude<>{}); } /** * @brief Calculates the common ratio of all the references of base units in the derived dimension */ template -constexpr ratio base_units_ratio(exponent_list) +constexpr ratio base_units_ratio(exponent_list es) { - return (exp_ratio() * ... * ratio(1)); + return as_ratio(absolute_magnitude(es)); } + } // namespace units::detail From d19b2803ceba94ecbab15b604e21d21e5557158f Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Sat, 9 Apr 2022 17:55:44 +0000 Subject: [PATCH 06/35] Migrate units from ratio to Magnitude This commit is huge, but hopefully the cognitive load is not too bad. The bulk of this commit is just some fairly mechanical updates from `ratio` to `Magnitude`. Other things to call out: - `UnitRatio` goes away. We don't need this concept, because Magnitude can't even _represent_ anything that doesn't satisfy it. - I commented out some formatting test cases where the precise expression changes, but the number is completely equivalent. We will need to decide how we want to handle Magnitude formatting as a separate, follow-on task. But at least Magnitude gives us all the tools we'll need to do so! --- example/custom_systems.cpp | 6 +-- example/literals/custom_systems.cpp | 6 +-- .../include/units/bits/base_units_ratio.h | 10 ----- src/core/include/units/bits/basic_concepts.h | 17 ++------ src/core/include/units/bits/common_type.h | 12 +++--- src/core/include/units/bits/derived_unit.h | 6 --- src/core/include/units/chrono.h | 4 +- src/core/include/units/derived_dimension.h | 3 +- .../include/units/generic/dimensionless.h | 2 +- src/core/include/units/math.h | 6 +-- src/core/include/units/prefix.h | 2 + src/core/include/units/quantity.h | 2 +- src/core/include/units/quantity_cast.h | 43 +++++++++---------- src/core/include/units/reference.h | 8 ++-- src/core/include/units/unit.h | 30 ++++++------- .../units/isq/iec80000/storage_capacity.h | 2 +- .../si-fps/include/units/isq/si/fps/force.h | 3 +- .../si-fps/include/units/isq/si/fps/length.h | 14 +++--- .../si-fps/include/units/isq/si/fps/mass.h | 19 ++++---- .../si-fps/include/units/isq/si/fps/power.h | 3 +- .../include/units/isq/si/fps/pressure.h | 5 ++- .../si-hep/include/units/isq/si/hep/area.h | 2 +- .../si-hep/include/units/isq/si/hep/mass.h | 15 +++++-- .../include/units/isq/si/hep/momentum.h | 2 +- .../si-iau/include/units/isq/si/iau/length.h | 6 +-- .../include/units/isq/si/imperial/length.h | 4 +- .../units/isq/si/international/length.h | 14 +++--- .../include/units/isq/si/typographic/length.h | 12 ++++-- .../include/units/isq/si/uscs/length.h | 6 +-- .../include/units/isq/si/catalytic_activity.h | 2 +- src/systems/si/include/units/isq/si/energy.h | 3 +- src/systems/si/include/units/isq/si/length.h | 3 +- .../include/units/isq/si/magnetic_induction.h | 2 +- src/systems/si/include/units/isq/si/mass.h | 3 +- src/systems/si/include/units/isq/si/time.h | 6 +-- test/unit_test/runtime/fmt_test.cpp | 10 +++-- test/unit_test/runtime/fmt_units_test.cpp | 17 +++++--- test/unit_test/static/concepts_test.cpp | 9 ---- test/unit_test/static/quantity_kind_test.cpp | 6 +-- test/unit_test/static/quantity_test.cpp | 37 +++++++++------- test/unit_test/static/si_test.cpp | 4 +- test/unit_test/static/unit_test.cpp | 30 +++++++------ 42 files changed, 197 insertions(+), 199 deletions(-) diff --git a/example/custom_systems.cpp b/example/custom_systems.cpp index f8dfd28f..ecb5f0d4 100644 --- a/example/custom_systems.cpp +++ b/example/custom_systems.cpp @@ -33,7 +33,7 @@ using namespace units; namespace fps { struct foot : named_unit {}; -struct yard : named_scaled_unit {}; +struct yard : named_scaled_unit(), foot> {}; struct dim_length : base_dimension<"L", foot> {}; @@ -54,8 +54,8 @@ using length = quantity; namespace fps { -struct foot : named_scaled_unit {}; -struct yard : named_scaled_unit {}; +struct foot : named_scaled_unit(), metre> {}; +struct yard : named_scaled_unit(), foot> {}; struct dim_length : base_dimension<"L", foot> {}; diff --git a/example/literals/custom_systems.cpp b/example/literals/custom_systems.cpp index f8fa8a3b..d0f80915 100644 --- a/example/literals/custom_systems.cpp +++ b/example/literals/custom_systems.cpp @@ -33,7 +33,7 @@ using namespace units; namespace fps { struct foot : named_unit {}; -struct yard : named_scaled_unit {}; +struct yard : named_scaled_unit(), foot> {}; struct dim_length : base_dimension<"L", foot> {}; @@ -54,8 +54,8 @@ using length = quantity; namespace fps { -struct foot : named_scaled_unit {}; -struct yard : named_scaled_unit {}; +struct foot : named_scaled_unit(), metre> {}; +struct yard : named_scaled_unit(), foot> {}; struct dim_length : base_dimension<"L", foot> {}; diff --git a/src/core/include/units/bits/base_units_ratio.h b/src/core/include/units/bits/base_units_ratio.h index dad86a67..2d9703e3 100644 --- a/src/core/include/units/bits/base_units_ratio.h +++ b/src/core/include/units/bits/base_units_ratio.h @@ -45,14 +45,4 @@ constexpr Magnitude auto absolute_magnitude(exponent_list) return (pow(Es::dimension::base_unit::mag) * ... * magnitude<>{}); } -/** - * @brief Calculates the common ratio of all the references of base units in the derived dimension - */ -template -constexpr ratio base_units_ratio(exponent_list es) -{ - return as_ratio(absolute_magnitude(es)); -} - - } // namespace units::detail diff --git a/src/core/include/units/bits/basic_concepts.h b/src/core/include/units/bits/basic_concepts.h index ecff7a2f..54f328aa 100644 --- a/src/core/include/units/bits/basic_concepts.h +++ b/src/core/include/units/bits/basic_concepts.h @@ -28,7 +28,7 @@ #include #include #include -#include +#include // IWYU pragma: end_exports #include @@ -67,24 +67,15 @@ void to_prefix_base(const volatile prefix_base*); template concept Prefix = requires(T* t) { detail::to_prefix_base(t); }; -/** - * @brief A concept matching unit's ratio - * - * Satisfied by all ratio values for which `R.num > 0` and `R.den > 0`. - */ -template -concept UnitRatio = (R.num > 0) && (R.den > 0); - // Unit -template - requires UnitRatio +template struct scaled_unit; // TODO: Remove when P1985 accepted namespace detail { -template -void to_base_scaled_unit(const volatile scaled_unit*); +template +void to_base_scaled_unit(const volatile scaled_unit*); } // namespace detail diff --git a/src/core/include/units/bits/common_type.h b/src/core/include/units/bits/common_type.h index 5dd8568b..40880927 100644 --- a/src/core/include/units/bits/common_type.h +++ b/src/core/include/units/bits/common_type.h @@ -55,22 +55,22 @@ struct common_quantity_reference_impl, reference> { template struct common_quantity_reference_impl, reference> { - using type = reference>; + using type = reference>; }; template requires(same_unit_reference, dimension_unit>::value) struct common_quantity_reference_impl, reference> { - using type = reference>; + using type = reference>; }; template struct common_quantity_reference_impl, reference> { using dimension = conditional, D2, D1>; - static constexpr ratio r1 = D1::base_units_ratio * U1::ratio; - static constexpr ratio r2 = D2::base_units_ratio * U2::ratio; - static constexpr ratio cr = common_ratio(r1, r2); - using unit = downcast_unit; + static constexpr Magnitude auto m1 = D1::base_units_ratio * U1::mag; + static constexpr Magnitude auto m2 = D2::base_units_ratio * U2::mag; + static constexpr Magnitude auto cm = common_magnitude(m1, m2); + using unit = downcast_unit; using type = reference; }; diff --git a/src/core/include/units/bits/derived_unit.h b/src/core/include/units/bits/derived_unit.h index 01ebbf23..61a83986 100644 --- a/src/core/include/units/bits/derived_unit.h +++ b/src/core/include/units/bits/derived_unit.h @@ -43,12 +43,6 @@ constexpr Magnitude auto derived_mag(exponent_list) pow(Us::mag / dimension_unit::mag)); } -template -constexpr ratio derived_ratio(exponent_list es) -{ - return as_ratio(derived_mag(es)); -} - template using derived_unit = scaled_unit(typename D::recipe()), typename D::coherent_unit::reference>; diff --git a/src/core/include/units/chrono.h b/src/core/include/units/chrono.h index d19660de..f5be9ea0 100644 --- a/src/core/include/units/chrono.h +++ b/src/core/include/units/chrono.h @@ -34,7 +34,7 @@ namespace units { template struct quantity_like_traits> { using dimension = isq::si::dim_time; - using unit = downcast_unit; + using unit = downcast_unit()>; using rep = Rep; [[nodiscard]] static constexpr rep number(const std::chrono::duration& q) { return q.count(); } }; @@ -45,7 +45,7 @@ struct clock_origin : point_origin {}; template struct quantity_point_like_traits>> { using origin = clock_origin; - using unit = downcast_unit; + using unit = downcast_unit()>; using rep = Rep; [[nodiscard]] static constexpr auto relative(const std::chrono::time_point>& qp) { diff --git a/src/core/include/units/derived_dimension.h b/src/core/include/units/derived_dimension.h index 0ea8f23a..76c8dc9f 100644 --- a/src/core/include/units/derived_dimension.h +++ b/src/core/include/units/derived_dimension.h @@ -85,7 +85,8 @@ template struct derived_dimension : downcast_dispatch> { using recipe = exponent_list; using coherent_unit = U; - static constexpr ratio base_units_ratio = detail::base_units_ratio(typename derived_dimension::exponents()); + static constexpr Magnitude auto base_units_ratio = + detail::absolute_magnitude(typename derived_dimension::exponents()); }; } // namespace units diff --git a/src/core/include/units/generic/dimensionless.h b/src/core/include/units/generic/dimensionless.h index d1d34c2e..ee96af8e 100644 --- a/src/core/include/units/generic/dimensionless.h +++ b/src/core/include/units/generic/dimensionless.h @@ -31,7 +31,7 @@ namespace units { struct one : named_unit {}; -struct percent : named_scaled_unit {}; +struct percent : named_scaled_unit(), one> {}; /** * @brief Dimension one diff --git a/src/core/include/units/math.h b/src/core/include/units/math.h index d2c2728e..0de45139 100644 --- a/src/core/include/units/math.h +++ b/src/core/include/units/math.h @@ -57,7 +57,7 @@ template return rep(1); } else { using dim = dimension_pow; - using unit = downcast_unit(Q::unit::ratio)>; + using unit = downcast_unit(Q::unit::mag)>; using std::pow; return quantity( static_cast(pow(q.number(), static_cast(Num) / static_cast(Den)))); @@ -77,7 +77,7 @@ template requires requires { sqrt(q.number()); } || requires { std::sqrt(q.number()); } { using dim = dimension_pow; - using unit = downcast_unit; + using unit = downcast_unit(Q::unit::mag)>; using rep = TYPENAME Q::rep; using std::sqrt; return quantity(static_cast(sqrt(q.number()))); @@ -96,7 +96,7 @@ template requires requires { cbrt(q.number()); } || requires { std::cbrt(q.number()); } { using dim = dimension_pow; - using unit = downcast_unit; + using unit = downcast_unit(Q::unit::mag)>; using rep = TYPENAME Q::rep; using std::cbrt; return quantity(static_cast(cbrt(q.number()))); diff --git a/src/core/include/units/prefix.h b/src/core/include/units/prefix.h index 0a9386d1..7b9b3305 100644 --- a/src/core/include/units/prefix.h +++ b/src/core/include/units/prefix.h @@ -25,6 +25,7 @@ #include #include // IWYU pragma: begin_exports +#include #include #include // IWYU pragma: end_exports @@ -52,6 +53,7 @@ template struct prefix_base : downcast_base> { using prefix_family = PF; static constexpr ::units::ratio ratio = R; + static constexpr Magnitude auto mag = as_magnitude(); }; } // namespace detail diff --git a/src/core/include/units/quantity.h b/src/core/include/units/quantity.h index 9eb2722b..de2741d8 100644 --- a/src/core/include/units/quantity.h +++ b/src/core/include/units/quantity.h @@ -402,7 +402,7 @@ public: { gsl_ExpectsAudit(q.number() != quantity_values::zero()); using dim = dim_invert; - using ret_unit = downcast_unit; + using ret_unit = downcast_unit(U::mag)>; using ret = quantity, Value, rep>>; return ret(v / q.number()); } diff --git a/src/core/include/units/quantity_cast.h b/src/core/include/units/quantity_cast.h index e4f8cb62..a2664339 100644 --- a/src/core/include/units/quantity_cast.h +++ b/src/core/include/units/quantity_cast.h @@ -27,6 +27,7 @@ #include #include #include +#include UNITS_DIAGNOSTIC_PUSH // warning C4244: 'argument': conversion from 'intmax_t' to 'T', possible loss of data with T=int @@ -49,25 +50,31 @@ class quantity_point_kind; namespace detail { template -inline constexpr ratio quantity_ratio = std::enable_if_t>{}; +inline constexpr Magnitude auto quantity_magnitude = std::enable_if_t, magnitude<>>{}; template -inline constexpr ratio quantity_ratio> = [] { +inline constexpr Magnitude auto quantity_magnitude> = [] { if constexpr (BaseDimension) { - return U::ratio; + return U::mag; } else { - return D::base_units_ratio * U::ratio / D::coherent_unit::ratio; + return D::base_units_ratio * U::mag / D::coherent_unit::mag; } }(); +template +inline constexpr ratio quantity_ratio = std::enable_if_t>{}; + +template +inline constexpr ratio quantity_ratio> = as_ratio(quantity_magnitude>); + template -inline constexpr ratio cast_ratio = [] { +inline constexpr Magnitude auto cast_magnitude = [] { using FromU = TYPENAME QFrom::unit; using ToU = TYPENAME QTo::unit; if constexpr (same_unit_reference::value) { - return FromU::ratio / ToU::ratio; + return FromU::mag / ToU::mag; } else { - return quantity_ratio / quantity_ratio; + return quantity_magnitude / quantity_magnitude; } }(); @@ -112,24 +119,16 @@ template R using traits = detail::cast_traits; using ratio_type = TYPENAME traits::ratio_type; using rep_type = TYPENAME traits::rep_type; - constexpr auto c_ratio = detail::cast_ratio, To>; + constexpr Magnitude auto c_mag = detail::cast_magnitude, To>; if constexpr (treat_as_floating_point) { return To( - static_cast(static_cast(q.number()) * - (static_cast(c_ratio.num) * detail::fpow10(c_ratio.exp) / - static_cast(c_ratio.den)))); + static_cast(static_cast(q.number()) * (get_value(numerator(c_mag)) / + get_value(denominator(c_mag))))); } else { - if constexpr (c_ratio.exp > 0) { - return To(static_cast( - static_cast(q.number()) * - (static_cast(c_ratio.num) * static_cast(detail::ipow10(c_ratio.exp))) / - static_cast(c_ratio.den))); - } else { - return To(static_cast( - static_cast(q.number()) * static_cast(c_ratio.num) / - (static_cast(c_ratio.den) * static_cast(detail::ipow10(-c_ratio.exp))))); - } + return To( + static_cast(static_cast(q.number()) * get_value(numerator(c_mag)) / + get_value(denominator(c_mag)))); } } @@ -149,7 +148,7 @@ template requires equivalent [[nodiscard]] constexpr auto quantity_cast(const quantity& q) { - return quantity_cast, Rep>>(q); + return quantity_cast, Rep>>(q); } /** diff --git a/src/core/include/units/reference.h b/src/core/include/units/reference.h index cca74b79..200cc2fb 100644 --- a/src/core/include/units/reference.h +++ b/src/core/include/units/reference.h @@ -37,13 +37,13 @@ namespace detail { template using reference_multiply_impl = - reference::ratio) * (U2::ratio / dimension_unit::ratio) * - dimension_unit::ratio>>; + reference::mag) * (U2::mag / dimension_unit::mag) * + dimension_unit::mag>>; template using reference_divide_impl = - reference::ratio) / (U2::ratio / dimension_unit::ratio) * - dimension_unit::ratio>>; + reference::mag) / (U2::mag / dimension_unit::mag) * + dimension_unit::mag>>; } // namespace detail diff --git a/src/core/include/units/unit.h b/src/core/include/units/unit.h index 81cec37b..62a37d47 100644 --- a/src/core/include/units/unit.h +++ b/src/core/include/units/unit.h @@ -28,6 +28,7 @@ // IWYU pragma: begin_exports #include #include +#include #include #include #include @@ -47,20 +48,18 @@ namespace units { * (i.e. all length units are expressed in terms of meter, all mass units are expressed in * terms of gram, ...) * - * @tparam R a ratio of a reference unit + * @tparam M a Magnitude representing the (relative) size of this unit * @tparam U a unit to use as a reference for this dimension */ -template - requires UnitRatio -struct scaled_unit : downcast_base> { - static constexpr ::units::ratio ratio = R; +template +struct scaled_unit : downcast_base> { + static constexpr ::units::ratio ratio = as_ratio(M); + static constexpr Magnitude auto mag = M; using reference = U; }; -template -// template // TODO: GCC crash!!! - requires UnitRatio -using downcast_unit = downcast::reference>>; +template +using downcast_unit = downcast::reference>>; template struct same_unit_reference : is_same {}; @@ -74,7 +73,7 @@ struct same_unit_reference : is_same -struct unit : downcast_dispatch> { +struct unit : downcast_dispatch(), Child>> { static constexpr bool is_named = false; using prefix_family = no_prefix; }; @@ -92,7 +91,7 @@ struct unit : downcast_dispatch> { * @tparam PF no_prefix or a type of prefix family */ template -struct named_unit : downcast_dispatch> { +struct named_unit : downcast_dispatch(), Child>> { static constexpr bool is_named = true; static constexpr auto symbol = Symbol; using prefix_family = PF; @@ -109,12 +108,11 @@ struct named_unit : downcast_dispatch> { * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) * @tparam Symbol a short text representation of the unit * @tparam PF no_prefix or a type of prefix family - * @tparam R a scale to apply to U + * @tparam M the Magnitude by which to scale U * @tparam U a reference unit to scale */ -template - requires UnitRatio -struct named_scaled_unit : downcast_dispatch> { +template +struct named_scaled_unit : downcast_dispatch> { static constexpr bool is_named = true; static constexpr auto symbol = Symbol; using prefix_family = PF; @@ -133,7 +131,7 @@ struct named_scaled_unit : downcast_dispatch requires U::is_named && std::same_as -struct prefixed_unit : downcast_dispatch> { +struct prefixed_unit : downcast_dispatch> { static constexpr bool is_named = true; static constexpr auto symbol = P::symbol + U::symbol; using prefix_family = no_prefix; diff --git a/src/systems/isq-iec80000/include/units/isq/iec80000/storage_capacity.h b/src/systems/isq-iec80000/include/units/isq/iec80000/storage_capacity.h index c8ea04f7..3e107d20 100644 --- a/src/systems/isq-iec80000/include/units/isq/iec80000/storage_capacity.h +++ b/src/systems/isq-iec80000/include/units/isq/iec80000/storage_capacity.h @@ -53,7 +53,7 @@ struct tebibit : prefixed_unit {}; struct pebibit : prefixed_unit {}; struct exbibit : prefixed_unit {}; -struct byte : named_scaled_unit {}; +struct byte : named_scaled_unit(), bit> {}; struct kilobyte : prefixed_unit {}; struct megabyte : prefixed_unit {}; struct gigabyte : prefixed_unit {}; diff --git a/src/systems/si-fps/include/units/isq/si/fps/force.h b/src/systems/si-fps/include/units/isq/si/fps/force.h index 8958ca44..330ad453 100644 --- a/src/systems/si-fps/include/units/isq/si/fps/force.h +++ b/src/systems/si-fps/include/units/isq/si/fps/force.h @@ -40,7 +40,8 @@ namespace units::isq::si::fps { struct poundal : named_unit {}; // https://en.wikipedia.org/wiki/Pound_(force) -struct pound_force : named_scaled_unit {}; +struct pound_force : + named_scaled_unit(), poundal> {}; struct kilopound_force : prefixed_unit {}; diff --git a/src/systems/si-fps/include/units/isq/si/fps/length.h b/src/systems/si-fps/include/units/isq/si/fps/length.h index c037afe1..12b83ae5 100644 --- a/src/systems/si-fps/include/units/isq/si/fps/length.h +++ b/src/systems/si-fps/include/units/isq/si/fps/length.h @@ -35,24 +35,24 @@ namespace units::isq::si::fps { // https://en.wikipedia.org/wiki/Foot_(unit) -struct foot : named_scaled_unit {}; +struct foot : named_scaled_unit(), si::metre> {}; -struct inch : named_scaled_unit {}; +struct inch : named_scaled_unit(), foot> {}; // thousandth of an inch -struct thousandth : named_scaled_unit {}; +struct thousandth : named_scaled_unit(), inch> {}; struct thou : alias_unit {}; struct mil : alias_unit {}; -struct yard : named_scaled_unit {}; +struct yard : named_scaled_unit(), foot> {}; -struct fathom : named_scaled_unit {}; +struct fathom : named_scaled_unit(), foot> {}; struct kiloyard : prefixed_unit {}; -struct mile : named_scaled_unit {}; +struct mile : named_scaled_unit(), foot> {}; -struct nautical_mile : named_scaled_unit {}; +struct nautical_mile : named_scaled_unit(), yard> {}; struct dim_length : isq::dim_length {}; diff --git a/src/systems/si-fps/include/units/isq/si/fps/mass.h b/src/systems/si-fps/include/units/isq/si/fps/mass.h index 559593d0..bc669e7f 100644 --- a/src/systems/si-fps/include/units/isq/si/fps/mass.h +++ b/src/systems/si-fps/include/units/isq/si/fps/mass.h @@ -35,28 +35,29 @@ namespace units::isq::si::fps { // https://en.wikipedia.org/wiki/Pound_(mass) -struct pound : named_scaled_unit {}; +struct pound : + named_scaled_unit(), si::kilogram> {}; struct dim_mass : isq::dim_mass {}; template U, Representation Rep = double> using mass = quantity; -struct grain : named_scaled_unit {}; +struct grain : named_scaled_unit(), pound> {}; -struct dram : named_scaled_unit {}; +struct dram : named_scaled_unit(), pound> {}; -struct ounce : named_scaled_unit {}; +struct ounce : named_scaled_unit(), pound> {}; -struct stone : named_scaled_unit {}; +struct stone : named_scaled_unit(), pound> {}; -struct quarter : named_scaled_unit {}; +struct quarter : named_scaled_unit(), pound> {}; -struct hundredweight : named_scaled_unit {}; +struct hundredweight : named_scaled_unit(), pound> {}; -struct short_ton : named_scaled_unit {}; +struct short_ton : named_scaled_unit(), pound> {}; -struct long_ton : named_scaled_unit {}; +struct long_ton : named_scaled_unit(), pound> {}; #ifndef UNITS_NO_LITERALS diff --git a/src/systems/si-fps/include/units/isq/si/fps/power.h b/src/systems/si-fps/include/units/isq/si/fps/power.h index d48d1f31..ac589004 100644 --- a/src/systems/si-fps/include/units/isq/si/fps/power.h +++ b/src/systems/si-fps/include/units/isq/si/fps/power.h @@ -41,7 +41,8 @@ struct dim_power : isq::dim_power {}; -struct horse_power : named_scaled_unit {}; +struct horse_power : + named_scaled_unit(), foot_pound_force_per_second> {}; template U, Representation Rep = double> using power = quantity; diff --git a/src/systems/si-fps/include/units/isq/si/fps/pressure.h b/src/systems/si-fps/include/units/isq/si/fps/pressure.h index 2c87269f..93c48eaf 100644 --- a/src/systems/si-fps/include/units/isq/si/fps/pressure.h +++ b/src/systems/si-fps/include/units/isq/si/fps/pressure.h @@ -44,11 +44,12 @@ template U, Representation Rep = double> using pressure = quantity; struct pound_force_per_foot_sq : - named_scaled_unit(), poundal_per_foot_sq> {}; struct pound_force_per_inch_sq : - named_scaled_unit {}; + named_scaled_unit(), + pound_force_per_foot_sq> {}; struct kilopound_force_per_inch_sq : prefixed_unit {}; diff --git a/src/systems/si-hep/include/units/isq/si/hep/area.h b/src/systems/si-hep/include/units/isq/si/hep/area.h index 9718fb2d..e1e7a297 100644 --- a/src/systems/si-hep/include/units/isq/si/hep/area.h +++ b/src/systems/si-hep/include/units/isq/si/hep/area.h @@ -37,7 +37,7 @@ namespace units::isq::si::hep { // effective cross-sectional area according to EU council directive 80/181/EEC // https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:01980L0181-20090527#page=10 // https://www.fedlex.admin.ch/eli/cc/1994/3109_3109_3109/de -struct barn : named_scaled_unit {}; +struct barn : named_scaled_unit(), square_metre> {}; struct yocto_barn : prefixed_unit {}; struct zepto_barn : prefixed_unit {}; struct atto_barn : prefixed_unit {}; diff --git a/src/systems/si-hep/include/units/isq/si/hep/mass.h b/src/systems/si-hep/include/units/isq/si/hep/mass.h index 2f617544..cdffd952 100644 --- a/src/systems/si-hep/include/units/isq/si/hep/mass.h +++ b/src/systems/si-hep/include/units/isq/si/hep/mass.h @@ -32,11 +32,15 @@ #include #include +// Necessary to factor `1'672'621'923'695`, which appears in the proton mass. +template<> +inline constexpr std::optional units::known_first_factor<334'524'384'739> = 334'524'384'739; + namespace units::isq::si::hep { struct eV_per_c2 : named_scaled_unit {}; + as_magnitude(), kilogram> {}; struct feV_per_c2 : prefixed_unit {}; struct peV_per_c2 : prefixed_unit {}; struct neV_per_c2 : prefixed_unit {}; @@ -52,11 +56,14 @@ struct PeV_per_c2 : prefixed_unit {}; struct EeV_per_c2 : prefixed_unit {}; struct YeV_per_c2 : prefixed_unit {}; struct electron_mass : - named_scaled_unit {}; + named_scaled_unit(), + kilogram> {}; struct proton_mass : - named_scaled_unit {}; + named_scaled_unit(), + kilogram> {}; struct neutron_mass : - named_scaled_unit {}; + named_scaled_unit(), + kilogram> {}; struct dim_mass : isq::dim_mass {}; diff --git a/src/systems/si-hep/include/units/isq/si/hep/momentum.h b/src/systems/si-hep/include/units/isq/si/hep/momentum.h index 285f9d21..9cabfb15 100644 --- a/src/systems/si-hep/include/units/isq/si/hep/momentum.h +++ b/src/systems/si-hep/include/units/isq/si/hep/momentum.h @@ -36,7 +36,7 @@ namespace units::isq::si::hep { struct eV_per_c : - named_scaled_unit(), ::units::isq::si::kilogram_metre_per_second> {}; struct feV_per_c : prefixed_unit {}; struct peV_per_c : prefixed_unit {}; diff --git a/src/systems/si-iau/include/units/isq/si/iau/length.h b/src/systems/si-iau/include/units/isq/si/iau/length.h index ce0d1059..22350735 100644 --- a/src/systems/si-iau/include/units/isq/si/iau/length.h +++ b/src/systems/si-iau/include/units/isq/si/iau/length.h @@ -36,13 +36,13 @@ namespace units::isq::si::iau { // https://en.wikipedia.org/wiki/Light-year -struct light_year : named_scaled_unit {}; +struct light_year : named_scaled_unit(), si::metre> {}; // https://en.wikipedia.org/wiki/Parsec -struct parsec : named_scaled_unit {}; +struct parsec : named_scaled_unit(), si::metre> {}; // https://en.wikipedia.org/wiki/Angstrom -struct angstrom : named_scaled_unit {}; +struct angstrom : named_scaled_unit(), si::metre> {}; #ifndef UNITS_NO_LITERALS diff --git a/src/systems/si-imperial/include/units/isq/si/imperial/length.h b/src/systems/si-imperial/include/units/isq/si/imperial/length.h index eb5978c3..4136595a 100644 --- a/src/systems/si-imperial/include/units/isq/si/imperial/length.h +++ b/src/systems/si-imperial/include/units/isq/si/imperial/length.h @@ -35,10 +35,10 @@ namespace units::isq::si::imperial { // https://en.wikipedia.org/wiki/Chain_(unit) -struct chain : named_scaled_unit {}; +struct chain : named_scaled_unit(), si::international::yard> {}; // https://en.wikipedia.org/wiki/Rod_(unit) -struct rod : named_scaled_unit {}; +struct rod : named_scaled_unit(), chain> {}; #ifndef UNITS_NO_LITERALS diff --git a/src/systems/si-international/include/units/isq/si/international/length.h b/src/systems/si-international/include/units/isq/si/international/length.h index 38b7eac3..7ea729b7 100644 --- a/src/systems/si-international/include/units/isq/si/international/length.h +++ b/src/systems/si-international/include/units/isq/si/international/length.h @@ -37,30 +37,30 @@ namespace units::isq::si::international { // si::international yard // https://en.wikipedia.org/wiki/International_yard_and_pound -struct yard : named_scaled_unit {}; +struct yard : named_scaled_unit(), si::metre> {}; // si::international foot // https://en.wikipedia.org/wiki/Foot_(unit)#International_foot -struct foot : named_scaled_unit {}; +struct foot : named_scaled_unit(), yard> {}; // https://en.wikipedia.org/wiki/Fathom#International_fathom -struct fathom : named_scaled_unit {}; +struct fathom : named_scaled_unit(), yard> {}; // si::international inch // https://en.wikipedia.org/wiki/Inch#Equivalences -struct inch : named_scaled_unit {}; +struct inch : named_scaled_unit(), yard> {}; // intrnational mile // https://en.wikipedia.org/wiki/Mile#International_mile -struct mile : named_scaled_unit {}; +struct mile : named_scaled_unit(), si::kilometre> {}; // si::international nautical mile // https://en.wikipedia.org/wiki/Nautical_mile -struct nautical_mile : named_scaled_unit {}; +struct nautical_mile : named_scaled_unit(), si::metre> {}; // thou // https://en.wikipedia.org/wiki/Thousandth_of_an_inch -struct thou : named_scaled_unit {}; +struct thou : named_scaled_unit(), inch> {}; // mil - different name for thou // https://en.wikipedia.org/wiki/Thousandth_of_an_inch diff --git a/src/systems/si-typographic/include/units/isq/si/typographic/length.h b/src/systems/si-typographic/include/units/isq/si/typographic/length.h index cb3c691e..e8b4b5a8 100644 --- a/src/systems/si-typographic/include/units/isq/si/typographic/length.h +++ b/src/systems/si-typographic/include/units/isq/si/typographic/length.h @@ -37,10 +37,14 @@ namespace units::isq::si::typographic { // TODO Conflicts with (https://en.wikipedia.org/wiki/Pica_(typography)), verify correctness of below conversion factors // and provide hyperlinks to definitions -struct pica_comp : named_scaled_unit {}; -struct pica_prn : named_scaled_unit {}; -struct point_comp : named_scaled_unit {}; -struct point_prn : named_scaled_unit {}; +struct pica_comp : + named_scaled_unit(), si::metre> {}; +struct pica_prn : + named_scaled_unit(), si::metre> {}; +struct point_comp : + named_scaled_unit(), si::metre> {}; +struct point_prn : + named_scaled_unit(), si::metre> {}; #ifndef UNITS_NO_LITERALS diff --git a/src/systems/si-uscs/include/units/isq/si/uscs/length.h b/src/systems/si-uscs/include/units/isq/si/uscs/length.h index b1c06b5f..83281090 100644 --- a/src/systems/si-uscs/include/units/isq/si/uscs/length.h +++ b/src/systems/si-uscs/include/units/isq/si/uscs/length.h @@ -36,14 +36,14 @@ namespace units::isq::si::uscs { // https://en.wikipedia.org/wiki/Foot_(unit)#US_survey_foot // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 -struct foot : named_scaled_unit {}; +struct foot : named_scaled_unit(), si::metre> {}; // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 -struct fathom : named_scaled_unit {}; +struct fathom : named_scaled_unit(), foot> {}; // https://en.wikipedia.org/wiki/Mile#U.S._survey_mile // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 -struct mile : named_scaled_unit {}; +struct mile : named_scaled_unit(), foot> {}; #ifndef UNITS_NO_LITERALS diff --git a/src/systems/si/include/units/isq/si/catalytic_activity.h b/src/systems/si/include/units/isq/si/catalytic_activity.h index 9c348d88..ad52a9d2 100644 --- a/src/systems/si/include/units/isq/si/catalytic_activity.h +++ b/src/systems/si/include/units/isq/si/catalytic_activity.h @@ -58,7 +58,7 @@ struct exakatal : prefixed_unit {}; struct zettakatal : prefixed_unit {}; struct yottakatal : prefixed_unit {}; -struct enzyme_unit : named_scaled_unit {}; +struct enzyme_unit : named_scaled_unit(), katal> {}; struct dim_catalytic_activity : isq::dim_catalytic_activity {}; diff --git a/src/systems/si/include/units/isq/si/energy.h b/src/systems/si/include/units/isq/si/energy.h index 2ca2fc9b..6efa1478 100644 --- a/src/systems/si/include/units/isq/si/energy.h +++ b/src/systems/si/include/units/isq/si/energy.h @@ -55,7 +55,8 @@ struct yottajoule : prefixed_unit {}; // N.B. electron charge (and eV) is an exact constant: // https://www.bipm.org/documents/20126/41483022/SI-Brochure-9.pdf#page=147 -struct electronvolt : named_scaled_unit {}; +struct electronvolt : + named_scaled_unit(), joule> {}; struct gigaelectronvolt : prefixed_unit {}; struct dim_energy : isq::dim_energy {}; diff --git a/src/systems/si/include/units/isq/si/length.h b/src/systems/si/include/units/isq/si/length.h index eee2c9d2..582a9ae3 100644 --- a/src/systems/si/include/units/isq/si/length.h +++ b/src/systems/si/include/units/isq/si/length.h @@ -56,7 +56,8 @@ struct exametre : prefixed_unit {}; struct zettametre : prefixed_unit {}; struct yottametre : prefixed_unit {}; -struct astronomical_unit : named_scaled_unit {}; +struct astronomical_unit : + named_scaled_unit(), metre> {}; struct dim_length : isq::dim_length {}; diff --git a/src/systems/si/include/units/isq/si/magnetic_induction.h b/src/systems/si/include/units/isq/si/magnetic_induction.h index 353a3af0..bdb33ef7 100644 --- a/src/systems/si/include/units/isq/si/magnetic_induction.h +++ b/src/systems/si/include/units/isq/si/magnetic_induction.h @@ -56,7 +56,7 @@ struct exatesla : prefixed_unit {}; struct zettatesla : prefixed_unit {}; struct yottatesla : prefixed_unit {}; -struct gauss : named_scaled_unit {}; +struct gauss : named_scaled_unit(), tesla> {}; struct dim_magnetic_induction : isq::dim_magnetic_induction {}; diff --git a/src/systems/si/include/units/isq/si/mass.h b/src/systems/si/include/units/isq/si/mass.h index 620381b3..d9d7fdf7 100644 --- a/src/systems/si/include/units/isq/si/mass.h +++ b/src/systems/si/include/units/isq/si/mass.h @@ -79,7 +79,8 @@ struct zettatonne : prefixed_unit {}; struct yottatonne : prefixed_unit {}; struct dalton : - named_scaled_unit {}; + named_scaled_unit(), + kilogram> {}; struct dim_mass : isq::dim_mass {}; diff --git a/src/systems/si/include/units/isq/si/time.h b/src/systems/si/include/units/isq/si/time.h index 7c1d4d5c..34d7c025 100644 --- a/src/systems/si/include/units/isq/si/time.h +++ b/src/systems/si/include/units/isq/si/time.h @@ -43,9 +43,9 @@ struct picosecond : prefixed_unit {}; struct nanosecond : prefixed_unit {}; struct microsecond : prefixed_unit {}; struct millisecond : prefixed_unit {}; -struct minute : named_scaled_unit {}; -struct hour : named_scaled_unit {}; -struct day : named_scaled_unit {}; +struct minute : named_scaled_unit(), second> {}; +struct hour : named_scaled_unit(), minute> {}; +struct day : named_scaled_unit(), hour> {}; struct dim_time : isq::dim_time {}; diff --git a/test/unit_test/runtime/fmt_test.cpp b/test/unit_test/runtime/fmt_test.cpp index abc595e6..3ec5211e 100644 --- a/test/unit_test/runtime/fmt_test.cpp +++ b/test/unit_test/runtime/fmt_test.cpp @@ -84,7 +84,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") { SECTION("in terms of base units") { - const length> q(123); + const length(as_magnitude<10>()), metre>> q(123); os << q; SECTION("iostream") { CHECK(os.str() == "123 Mm"); } @@ -96,7 +96,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("in terms of derived units") { - const energy> q(60); + const energy(as_magnitude<10>()), joule>> q(60); os << q; SECTION("iostream") { CHECK(os.str() == "60 cJ"); } @@ -257,7 +257,8 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 60_q_kJ / 2_q_min; os << q; - SECTION("iostream") { CHECK(os.str() == "30 [1/6 × 10²] W"); } + // TODO(chogg): Reinstate after format/Magnitude design. + // SECTION("iostream") { CHECK(os.str() == "30 [1/6 × 10²] W"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } @@ -390,7 +391,8 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 60_q_min / 2_q_km; os << q; - SECTION("iostream") { CHECK(os.str() == "30 [6 × 10⁻²] 1/m ⋅ s"); } + // TODO(chogg): Reinstate after format/Magnitude design. + // SECTION("iostream") { CHECK(os.str() == "30 [6 × 10⁻²] 1/m ⋅ s"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } diff --git a/test/unit_test/runtime/fmt_units_test.cpp b/test/unit_test/runtime/fmt_units_test.cpp index c94c3734..a3068eac 100644 --- a/test/unit_test/runtime/fmt_units_test.cpp +++ b/test/unit_test/runtime/fmt_units_test.cpp @@ -30,6 +30,7 @@ #include #include #include +#include using namespace units::isq::si; using namespace units::isq::si::references; @@ -315,16 +316,18 @@ TEST_CASE("std::format on synthesized unit symbols", "[text][fmt]") SECTION("incoherent units with powers") { - CHECK(STD_FMT::format("{}", 1_q_mi * 1_q_mi * 1_q_mi) == "1 [15900351812136/3814697265625 × 10⁹] m³"); - CHECK(STD_FMT::format("{}", 1_q_au * 1_q_au) == "1 [2237952291797391849 × 10⁴] m²"); - - CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_mi * 1_q_mi * 1_q_mi) == "1 [15900351812136/3814697265625 x 10^9] m^3"); - CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_au * 1_q_au) == "1 [2237952291797391849 x 10^4] m^2"); + // TODO(chogg): Reinstate after format/Magnitude redesign. + // CHECK(STD_FMT::format("{}", 1_q_mi * 1_q_mi * 1_q_mi) == "1 [15900351812136/3814697265625 × 10⁹] m³"); + // CHECK(STD_FMT::format("{}", 1_q_au * 1_q_au) == "1 [2237952291797391849 × 10⁴] m²"); + // + // CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_mi * 1_q_mi * 1_q_mi) == "1 [15900351812136/3814697265625 x 10^9] m^3"); + // CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_au * 1_q_au) == "1 [2237952291797391849 x 10^4] m^2"); } SECTION("unknown scaled unit with reference different than the dimension's coherent unit") { - CHECK(STD_FMT::format("{}", mass>(1)) == "1 [2/3 × 10⁻³] kg"); - CHECK(STD_FMT::format("{:%Q %Aq}", mass>(1)) == "1 [2/3 x 10^-3] kg"); + constexpr auto mag = units::as_magnitude(); + CHECK(STD_FMT::format("{}", mass>(1)) == "1 [2/3 × 10⁻³] kg"); + CHECK(STD_FMT::format("{:%Q %Aq}", mass>(1)) == "1 [2/3 x 10^-3] kg"); } } diff --git a/test/unit_test/static/concepts_test.cpp b/test/unit_test/static/concepts_test.cpp index d1c1e2bf..a97a1e13 100644 --- a/test/unit_test/static/concepts_test.cpp +++ b/test/unit_test/static/concepts_test.cpp @@ -53,15 +53,6 @@ static_assert(Prefix); static_assert(!Prefix); static_assert(!Prefix); -// UnitRatio - -static_assert(UnitRatio); -static_assert(!UnitRatio); -// static_assert(UnitRatio); // static_assert in ratio -static_assert(UnitRatio); -static_assert(!UnitRatio); -static_assert(!UnitRatio); - // BaseDimension static_assert(BaseDimension); diff --git a/test/unit_test/static/quantity_kind_test.cpp b/test/unit_test/static/quantity_kind_test.cpp index adbfeb8d..f7d4f642 100644 --- a/test/unit_test/static/quantity_kind_test.cpp +++ b/test/unit_test/static/quantity_kind_test.cpp @@ -456,9 +456,9 @@ concept invalid_compound_assignments = requires !requires { w *= m; }; requires !requires { w /= m; }; requires !requires { w %= m; }; - requires !requires { w *= quantity_kind, scaled_unit, int>{1}; }; - requires !requires { w /= quantity_kind, scaled_unit, int>{1}; }; - requires !requires { w %= quantity_kind, scaled_unit, int>{1}; }; + requires !requires { w *= quantity_kind, scaled_unit(), one>, int>{1}; }; + requires !requires { w /= quantity_kind, scaled_unit(), one>, int>{1}; }; + requires !requires { w %= quantity_kind, scaled_unit(), one>, int>{1}; }; requires !requires { w %= 1.0; }; requires !requires { w %= quantity(1.0); }; requires !requires { w %= 1.0 * (w / w); }; diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index 0f810ccf..73985198 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -496,7 +496,7 @@ static_assert(compare> static_assert(compare>); static_assert(compare>); static_assert(compare(1) / 1_q_s), - frequency, std::int64_t>>); + frequency(), hertz>, std::int64_t>>); static_assert(is_same_v); static_assert(is_same_v); @@ -527,7 +527,7 @@ static_assert(compare> static_assert(compare>); static_assert(compare>); static_assert(compare(1) / 1._q_s), - frequency, long double>>); + frequency(), hertz>, long double>>); static_assert(compare>); static_assert(compare>); static_assert(compare(1)), length>); @@ -549,7 +549,7 @@ static_assert(compare> static_assert(compare>); static_assert(compare>); static_assert(compare(1) / 1_q_s), - frequency, long double>>); + frequency(), hertz>, long double>>); // different units static_assert(is_same_v>); @@ -577,22 +577,25 @@ static_assert(is_same_v>); -static_assert(compare, std::int64_t>>); +static_assert( + compare(), metre>, std::int64_t>>); static_assert( compare, exponent>, - scaled_unit, std::int64_t>>); + scaled_unit(), unknown_coherent_unit>, std::int64_t>>); static_assert(compare>); -static_assert(compare, std::int64_t>>); -static_assert(compare>); static_assert( - compare>, - scaled_unit, std::int64_t>>); -static_assert(compare, std::int64_t>>); + compare(), hertz>, std::int64_t>>); +static_assert(compare>); +static_assert(compare>, + scaled_unit(), unknown_coherent_unit>, std::int64_t>>); +static_assert(compare(), one>, std::int64_t>>); static_assert(compare>); -static_assert(compare, std::int64_t>>); +static_assert( + compare(), metre_per_second>, std::int64_t>>); static_assert( compare, exponent>, - scaled_unit, std::int64_t>>); + scaled_unit(), unknown_coherent_unit>, std::int64_t>>); static_assert((1_q_m + 1_q_m).number() == 2); static_assert((1_q_m + 1_q_km).number() == 1001); @@ -882,8 +885,9 @@ static_assert(!is_same_v(2_q_dm3)), volume, units::exponent>, - scaled_unit, std::int64_t>>); -static_assert(is_same_v, std::int64_t>>); + scaled_unit(), unknown_coherent_unit>, std::int64_t>>); +static_assert( + is_same_v(), metre>, std::int64_t>>); #else @@ -913,7 +917,8 @@ static_assert(same(quotient_remainder_theorem(3'000 * m, 400), 3'000 * m)); static_assert(same(quotient_remainder_theorem(3'000 * m, quantity(400)), 3'000 * m)); static_assert(same(quotient_remainder_theorem(3 * km, quantity(400)), 3 * km)); static_assert(same(quotient_remainder_theorem(3 * km, quantity(2)), 3 * km)); -static_assert(same(quotient_remainder_theorem(3 * km, dimensionless, int>(400)), - 3 * km)); +static_assert( + same(quotient_remainder_theorem(3 * km, dimensionless(), one>, int>(400)), + 3 * km)); } // namespace diff --git a/test/unit_test/static/si_test.cpp b/test/unit_test/static/si_test.cpp index a69c1f1b..9070110d 100644 --- a/test/unit_test/static/si_test.cpp +++ b/test/unit_test/static/si_test.cpp @@ -43,7 +43,7 @@ static_assert(1_q_au == 149'597'870'700_q_m); static_assert(1_q_km + 1_q_m == 1001_q_m); static_assert(10_q_km / 5_q_km == 2); static_assert(10_q_km / 5_q_km < 3); -static_assert(100_q_mm / 5_q_cm == dimensionless>(20)); +static_assert(100_q_mm / 5_q_cm == dimensionless(), one>>(20)); static_assert(100_q_mm / 5_q_cm == dimensionless(2)); static_assert(10_q_km / 2 == 5_q_km); @@ -107,7 +107,7 @@ static_assert(1000 / 1_q_s == 1_q_kHz); static_assert(1 / 1_q_ms == 1_q_kHz); static_assert(3.2_q_GHz == 3'200'000'000_q_Hz); static_assert((10_q_Hz * 1_q_min).number() == 10); -static_assert(10_q_Hz * 1_q_min == dimensionless>(10)); +static_assert(10_q_Hz * 1_q_min == dimensionless(), one>>(10)); static_assert(10_q_Hz * 1_q_min == dimensionless(600)); static_assert(2 / 1_q_Hz == 2_q_s); diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index 4856b09a..a185a150 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -36,12 +36,12 @@ using namespace units::isq; struct metre : named_unit {}; struct centimetre : prefixed_unit {}; struct kilometre : prefixed_unit {}; -struct yard : named_scaled_unit {}; -struct foot : named_scaled_unit {}; +struct yard : named_scaled_unit(), metre> {}; +struct foot : named_scaled_unit(), yard> {}; struct dim_length : base_dimension<"length", metre> {}; struct second : named_unit {}; -struct hour : named_scaled_unit {}; +struct hour : named_scaled_unit(), second> {}; struct dim_time : base_dimension<"time", second> {}; struct kelvin : named_unit {}; @@ -59,17 +59,21 @@ struct kilometre_per_hour : derived_unit); static_assert(equivalent); -static_assert(compare>, metre>); -static_assert(compare>, centimetre>); -static_assert(compare>, yard>); -static_assert(compare>, foot>); -static_assert(compare>, kilometre_per_hour>); +static_assert(compare(), metre>>, metre>); +static_assert(compare(), metre>>, centimetre>); +static_assert( + compare(), metre>>, + yard>); +static_assert(compare(), metre>>, foot>); +static_assert(compare>, kilometre_per_hour>); -#if !UNITS_COMP_MSVC -static_assert([]() { - return !requires { typename scaled_unit; }; -}.template operator()()); // negative unit ratio -#endif +// We should delete this test case, because we are switching from ratio to Magnitude, and a negative Magnitude cannot +// even be formed. +// #if !UNITS_COMP_MSVC +// static_assert([]() { +// return !requires { typename scaled_unit; }; +// }.template operator()()>()); // negative unit ratio +// #endif static_assert(centimetre::symbol == "cm"); static_assert(kilometre::symbol == "km"); From 5e365f190599691e6253a675fc44e8a9a0455153 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Sat, 9 Apr 2022 17:56:01 +0000 Subject: [PATCH 07/35] Delete obsolete test case --- test/unit_test/static/unit_test.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index a185a150..eaaba74a 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -67,14 +67,6 @@ static_assert( static_assert(compare(), metre>>, foot>); static_assert(compare>, kilometre_per_hour>); -// We should delete this test case, because we are switching from ratio to Magnitude, and a negative Magnitude cannot -// even be formed. -// #if !UNITS_COMP_MSVC -// static_assert([]() { -// return !requires { typename scaled_unit; }; -// }.template operator()()>()); // negative unit ratio -// #endif - static_assert(centimetre::symbol == "cm"); static_assert(kilometre::symbol == "km"); static_assert(kilometre_per_hour::symbol == "km/h"); From 7a03692e14cbff55b6b274f35ee0248ceecf6296 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Tue, 12 Apr 2022 00:32:24 +0000 Subject: [PATCH 08/35] Annotate 225'653'407'801 as prime --- src/systems/si-hep/include/units/isq/si/hep/mass.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/systems/si-hep/include/units/isq/si/hep/mass.h b/src/systems/si-hep/include/units/isq/si/hep/mass.h index cdffd952..824c2262 100644 --- a/src/systems/si-hep/include/units/isq/si/hep/mass.h +++ b/src/systems/si-hep/include/units/isq/si/hep/mass.h @@ -36,11 +36,15 @@ template<> inline constexpr std::optional units::known_first_factor<334'524'384'739> = 334'524'384'739; +// Necessary to factor `17'826'619'216'279`, which appears in the value for eV/c^2. +template<> +inline constexpr std::optional units::known_first_factor<225'653'407'801> = 225'653'407'801; + namespace units::isq::si::hep { struct eV_per_c2 : named_scaled_unit(), kilogram> {}; + as_magnitude(), kilogram> {}; struct feV_per_c2 : prefixed_unit {}; struct peV_per_c2 : prefixed_unit {}; struct neV_per_c2 : prefixed_unit {}; From 78bcb3b0195d4b14c7dd5f4bf163c1baf2dc41e7 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Tue, 12 Apr 2022 00:33:13 +0000 Subject: [PATCH 09/35] Remove troublesome `::ratio` members For some reason, MSVC seems to want to instantiate these, even though nobody ever asks for them (as evidenced by the fact that the builds passed on other architectures). --- example/custom_systems.cpp | 2 +- example/literals/custom_systems.cpp | 2 +- src/core/include/units/bits/equivalent.h | 4 ++-- src/core/include/units/bits/unit_text.h | 4 ++-- src/core/include/units/chrono.h | 4 ++-- src/core/include/units/prefix.h | 1 - src/core/include/units/unit.h | 1 - test/unit_test/runtime/fmt_units_test.cpp | 7 ++++--- test/unit_test/static/cgs_test.cpp | 2 +- test/unit_test/static/fps_test.cpp | 4 ++-- test/unit_test/static/unit_test.cpp | 4 +--- 11 files changed, 16 insertions(+), 19 deletions(-) diff --git a/example/custom_systems.cpp b/example/custom_systems.cpp index ecb5f0d4..5c8bc2a0 100644 --- a/example/custom_systems.cpp +++ b/example/custom_systems.cpp @@ -103,7 +103,7 @@ template std::ostream& operator<<(std::ostream& os, const U& u) { using unit_type = std::remove_cvref_t; - return os << unit_type::ratio << " x " << unit_type::reference::symbol.standard(); + return os << as_ratio(unit_type::mag) << " x " << unit_type::reference::symbol.standard(); } void what_is_your_ratio() diff --git a/example/literals/custom_systems.cpp b/example/literals/custom_systems.cpp index d0f80915..ef683b98 100644 --- a/example/literals/custom_systems.cpp +++ b/example/literals/custom_systems.cpp @@ -96,7 +96,7 @@ template std::ostream& operator<<(std::ostream& os, const U& u) { using unit_type = std::remove_cvref_t; - return os << unit_type::ratio << " x " << unit_type::reference::symbol.standard(); + return os << as_ratio(unit_type::mag) << " x " << unit_type::reference::symbol.standard(); } void what_is_your_ratio() diff --git a/src/core/include/units/bits/equivalent.h b/src/core/include/units/bits/equivalent.h index 25d399cb..5bdac612 100644 --- a/src/core/include/units/bits/equivalent.h +++ b/src/core/include/units/bits/equivalent.h @@ -72,8 +72,8 @@ struct equivalent_impl : // additionally accounts for unknown dimensions template struct equivalent_unit : - std::disjunction, std::bool_constant::ratio == - U2::ratio / dimension_unit::ratio>> {}; + std::disjunction, + std::bool_constant::mag == U2::mag / dimension_unit::mag>> {}; // point origins diff --git a/src/core/include/units/bits/unit_text.h b/src/core/include/units/bits/unit_text.h index ec6675bc..09193f70 100644 --- a/src/core/include/units/bits/unit_text.h +++ b/src/core/include/units/bits/unit_text.h @@ -151,14 +151,14 @@ constexpr auto unit_text() // use predefined coherent unit symbol constexpr auto symbol_text = coherent_unit::symbol; constexpr auto prefix_txt = - prefix_or_ratio_text(); return prefix_txt + symbol_text; } else { // use derived dimension ingredients to create a unit symbol constexpr auto symbol_text = derived_dimension_unit_text(); constexpr auto prefix_txt = - prefix_or_ratio_text(); return prefix_txt + symbol_text; } diff --git a/src/core/include/units/chrono.h b/src/core/include/units/chrono.h index f5be9ea0..33cd2b5b 100644 --- a/src/core/include/units/chrono.h +++ b/src/core/include/units/chrono.h @@ -86,13 +86,13 @@ using to_std_ratio = decltype(detail::to_std_ratio_impl()); template [[nodiscard]] constexpr auto to_std_duration(const quantity& q) { - return std::chrono::duration>(q.number()); + return std::chrono::duration>(q.number()); } template [[nodiscard]] constexpr auto to_std_time_point(const quantity_point, U, Rep>& qp) { - using ret_type = std::chrono::time_point>>; + using ret_type = std::chrono::time_point>>; return ret_type(to_std_duration(qp.relative())); } diff --git a/src/core/include/units/prefix.h b/src/core/include/units/prefix.h index 7b9b3305..83adcb99 100644 --- a/src/core/include/units/prefix.h +++ b/src/core/include/units/prefix.h @@ -52,7 +52,6 @@ namespace detail { template struct prefix_base : downcast_base> { using prefix_family = PF; - static constexpr ::units::ratio ratio = R; static constexpr Magnitude auto mag = as_magnitude(); }; diff --git a/src/core/include/units/unit.h b/src/core/include/units/unit.h index 62a37d47..efae76ba 100644 --- a/src/core/include/units/unit.h +++ b/src/core/include/units/unit.h @@ -53,7 +53,6 @@ namespace units { */ template struct scaled_unit : downcast_base> { - static constexpr ::units::ratio ratio = as_ratio(M); static constexpr Magnitude auto mag = M; using reference = U; }; diff --git a/test/unit_test/runtime/fmt_units_test.cpp b/test/unit_test/runtime/fmt_units_test.cpp index a3068eac..58531174 100644 --- a/test/unit_test/runtime/fmt_units_test.cpp +++ b/test/unit_test/runtime/fmt_units_test.cpp @@ -326,8 +326,9 @@ TEST_CASE("std::format on synthesized unit symbols", "[text][fmt]") SECTION("unknown scaled unit with reference different than the dimension's coherent unit") { - constexpr auto mag = units::as_magnitude(); - CHECK(STD_FMT::format("{}", mass>(1)) == "1 [2/3 × 10⁻³] kg"); - CHECK(STD_FMT::format("{:%Q %Aq}", mass>(1)) == "1 [2/3 x 10^-3] kg"); + // TODO(chogg): Reinstate after format/Magnitude redesign. + // constexpr auto mag = units::as_magnitude(); + // CHECK(STD_FMT::format("{}", mass>(1)) == "1 [2/3 × 10⁻³] kg"); + // CHECK(STD_FMT::format("{:%Q %Aq}", mass>(1)) == "1 [2/3 x 10^-3] kg"); } } diff --git a/test/unit_test/static/cgs_test.cpp b/test/unit_test/static/cgs_test.cpp index 35dfd87b..4a0dc7bb 100644 --- a/test/unit_test/static/cgs_test.cpp +++ b/test/unit_test/static/cgs_test.cpp @@ -53,7 +53,7 @@ static_assert(10_q_cm == 2_q_cm_per_s * 5_q_s); static_assert(detail::unit_text() == "cm/s"); // area -static_assert(centimetre::ratio / dimension_unit::ratio == ratio(1)); +static_assert(as_ratio(centimetre::mag / dimension_unit::mag) == ratio(1)); static_assert((1_q_cm * 1_q_cm).number() == 1); static_assert((1_q_cm2).number() == 1); diff --git a/test/unit_test/static/fps_test.cpp b/test/unit_test/static/fps_test.cpp index 8f241525..4304f120 100644 --- a/test/unit_test/static/fps_test.cpp +++ b/test/unit_test/static/fps_test.cpp @@ -52,7 +52,7 @@ static_assert(10_q_ft == 2_q_ft_per_s * 5_q_s); static_assert(detail::unit_text() == "ft/s"); // area -static_assert(foot::ratio / dimension_unit::ratio == ratio(1)); +static_assert(as_ratio(foot::mag / dimension_unit::mag) == ratio(1)); static_assert(1_q_ft * 1_q_ft == 1_q_ft2); static_assert(100_q_ft2 / 10_q_ft == 10_q_ft); @@ -61,7 +61,7 @@ static_assert(detail::unit_text() == basic_symbol_text("f // volume static_assert(1_q_yd * 1_q_yd * 1_q_yd == 1_q_yd3); -static_assert(cubic_yard::ratio / cubic_foot::ratio == ratio(27)); +static_assert(as_ratio(cubic_yard::mag / cubic_foot::mag) == ratio(27)); /* ************** DERIVED DIMENSIONS WITH NAMED UNITS **************** */ diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index eaaba74a..cac1c622 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -61,9 +61,7 @@ static_assert(equivalent); static_assert(equivalent); static_assert(compare(), metre>>, metre>); static_assert(compare(), metre>>, centimetre>); -static_assert( - compare(), metre>>, - yard>); +static_assert(compare>, yard>); static_assert(compare(), metre>>, foot>); static_assert(compare>, kilometre_per_hour>); From c805a9390f67543bc1d7d639dfa898248fbe8250 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Tue, 12 Apr 2022 00:40:42 +0000 Subject: [PATCH 10/35] Implement suggestion --- src/core/include/units/magnitude.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index e3f16666..60cd5b85 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -567,7 +567,7 @@ constexpr auto common_magnitude(Magnitude auto m, magnitude<>) { return detail:: template constexpr auto common_magnitude(magnitude m1, magnitude m2) { - using namespace detail; + using detail::remove_positive_power; // Case for when H1 has the smaller base. if constexpr (H1.get_base() < H2.get_base()) { From 94fe48dd64ce4c93ae63d0104a52448b9d0e7ab5 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Tue, 12 Apr 2022 00:44:53 +0000 Subject: [PATCH 11/35] Add to satisfy clang --- src/core/include/units/ratio.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/include/units/ratio.h b/src/core/include/units/ratio.h index 52270e2d..c5e03d5c 100644 --- a/src/core/include/units/ratio.h +++ b/src/core/include/units/ratio.h @@ -32,6 +32,7 @@ #include #include +#include #include namespace units { From 2b37bc7b3e8c239a1d26cd614119b74d709ada2f Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Tue, 19 Apr 2022 15:42:43 +0000 Subject: [PATCH 12/35] Address review feedback --- src/core/include/units/magnitude.h | 26 +++++++++++++++++--------- src/core/include/units/quantity_cast.h | 22 ++++++++-------------- src/core/include/units/ratio.h | 4 ++-- test/unit_test/static/ratio_test.cpp | 6 ++++++ test/unit_test/static/si_fps_test.cpp | 18 ++++++++++++++---- 5 files changed, 47 insertions(+), 29 deletions(-) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index 60cd5b85..cde3a4b7 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -427,6 +427,18 @@ constexpr auto pow(magnitude) } } +template +constexpr auto sqrt(magnitude m) +{ + return pow(m); +} + +template +constexpr auto cbrt(magnitude m) +{ + return pow(m); +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Magnitude product implementation. @@ -569,18 +581,14 @@ constexpr auto common_magnitude(magnitude m1, magnitude m2 { using detail::remove_positive_power; - // Case for when H1 has the smaller base. if constexpr (H1.get_base() < H2.get_base()) { + // When H1 has the smaller base, prepend to result from recursion. return remove_positive_power(magnitude

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

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

{} * common_tail; diff --git a/src/core/include/units/quantity_cast.h b/src/core/include/units/quantity_cast.h index a2664339..8c40a87f 100644 --- a/src/core/include/units/quantity_cast.h +++ b/src/core/include/units/quantity_cast.h @@ -62,10 +62,7 @@ inline constexpr Magnitude auto quantity_magnitude> = [] { }(); template -inline constexpr ratio quantity_ratio = std::enable_if_t>{}; - -template -inline constexpr ratio quantity_ratio> = as_ratio(quantity_magnitude>); +inline constexpr ratio quantity_ratio = as_ratio(quantity_magnitude); template inline constexpr Magnitude auto cast_magnitude = [] { @@ -119,17 +116,14 @@ template R using traits = detail::cast_traits; using ratio_type = TYPENAME traits::ratio_type; using rep_type = TYPENAME traits::rep_type; - constexpr Magnitude auto c_mag = detail::cast_magnitude, To>; - if constexpr (treat_as_floating_point) { - return To( - static_cast(static_cast(q.number()) * (get_value(numerator(c_mag)) / - get_value(denominator(c_mag))))); - } else { - return To( - static_cast(static_cast(q.number()) * get_value(numerator(c_mag)) / - get_value(denominator(c_mag)))); - } + constexpr Magnitude auto c_mag = detail::cast_magnitude, To>; + constexpr Magnitude auto num = numerator(c_mag); + constexpr Magnitude auto den = denominator(c_mag); + constexpr Magnitude auto irr = c_mag * (den / num); + + constexpr auto val = [](Magnitude auto m) { return get_value(m); }; + return To(static_cast(static_cast(q.number()) * val(num) / val(den) * val(irr))); } /** diff --git a/src/core/include/units/ratio.h b/src/core/include/units/ratio.h index c5e03d5c..b57ce06d 100644 --- a/src/core/include/units/ratio.h +++ b/src/core/include/units/ratio.h @@ -61,6 +61,8 @@ struct ratio { [[nodiscard]] friend constexpr bool operator==(const ratio&, const ratio&) = default; + [[nodiscard]] friend constexpr auto operator<=>(const ratio& lhs, const ratio& rhs) { return (lhs - rhs).num <=> 0; } + [[nodiscard]] friend constexpr ratio operator-(const ratio& r) { return ratio(-r.num, r.den, r.exp); } [[nodiscard]] friend constexpr ratio operator+(ratio lhs, ratio rhs) @@ -81,8 +83,6 @@ struct ratio { [[nodiscard]] friend constexpr ratio operator-(const ratio& lhs, const ratio& rhs) { return lhs + (-rhs); } - [[nodiscard]] friend constexpr auto operator<=>(const ratio& lhs, const ratio& rhs) { return (lhs - rhs).num <=> 0; } - [[nodiscard]] friend constexpr ratio operator*(const ratio& lhs, const ratio& rhs) { const std::intmax_t gcd1 = std::gcd(lhs.num, rhs.den); diff --git a/test/unit_test/static/ratio_test.cpp b/test/unit_test/static/ratio_test.cpp index 4abe5094..ad966950 100644 --- a/test/unit_test/static/ratio_test.cpp +++ b/test/unit_test/static/ratio_test.cpp @@ -108,4 +108,10 @@ static_assert(numerator(ratio(3, 7, 2)) == 300); static_assert(denominator(ratio(3, 4)) == 4); static_assert(denominator(ratio(3, 7, -2)) == 700); +// comparison +static_assert((ratio(3, 4) <=> ratio(6, 8)) == (0 <=> 0)); +static_assert((ratio(3, 4) <=> ratio(-3, 4)) == (0 <=> -1)); +static_assert((ratio(-3, 4) <=> ratio(3, -4)) == (0 <=> 0)); +static_assert((ratio(1, 1, 1) <=> ratio(10)) == (0 <=> 0)); + } // namespace diff --git a/test/unit_test/static/si_fps_test.cpp b/test/unit_test/static/si_fps_test.cpp index a3b0aeb5..bb023c8c 100644 --- a/test/unit_test/static/si_fps_test.cpp +++ b/test/unit_test/static/si_fps_test.cpp @@ -31,6 +31,7 @@ #include #include #include +#include namespace { @@ -113,6 +114,13 @@ static_assert(1_q_pdl_per_ft2 > 1.4881639435_q_Pa && 1_q_pdl_per_ft2 < 1.4881639 } // namespace fps_plus_si_literals namespace fps_test { +namespace { +constexpr bool is_near(auto a, auto b, auto tol) +{ + const auto diff = a - b; + return (diff <= tol) && (-diff <= tol); +} +} // namespace using namespace units::isq::si::fps::literals; using namespace units::isq::si::fps::references; @@ -121,10 +129,12 @@ using namespace units::isq::si::fps::references; static_assert(si::length(1) + 1 * ft == si::length(1.3048)); static_assert(1 * ft + si::length(1) == si::length(1.3048)); -static_assert(quantity_cast>(1. * ft / 0.3048) + si::length(1) == - si::length(2)); // 1 m in ft + 1 m -static_assert(si::length(1) + quantity_cast>(1. * ft / 0.3048) == - si::length(2)); // 1 m + 1 m in ft +static_assert(is_near(quantity_cast>(1. * ft / 0.3048) + si::length(1), + si::length(2), + si::length(1))); // 1 m in ft + 1 m +static_assert(is_near(si::length(1) + quantity_cast>(1. * ft / 0.3048), + si::length(2), + si::length(1))); // 1 m + 1 m in ft static_assert(1 * ft + quantity_cast>(si::length(0.3048)) == 2 * ft); // 1 ft + 1 ft in m static_assert(quantity_cast>(si::length(0.3048)) + 1 * ft == From bd5a476babaaa8ad03aedccae2f36dbf249b2005 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 May 2022 22:52:03 +0200 Subject: [PATCH 13/35] fix: `pow<>` requires a `ratio` as a template argument Thank you @Nostracodus and @TartanLlama! --- src/core/include/units/magnitude.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index cde3a4b7..159282f9 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -652,8 +652,7 @@ template requires(R.num > 0) constexpr Magnitude auto as_magnitude() { - return pow(detail::prime_factorization_v<10>) * detail::prime_factorization_v / - detail::prime_factorization_v; + return pow(detail::prime_factorization_v<10>) * detail::prime_factorization_v / detail::prime_factorization_v; } } // namespace units From 104923d1aa0e9e7d041dff474a537b5a3d8d568f Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 May 2022 22:52:40 +0200 Subject: [PATCH 14/35] refactor: `as_ratio()` constrain simplified --- src/core/include/units/magnitude.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index 159282f9..c51285e2 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -525,7 +525,7 @@ constexpr auto denominator(Magnitude auto m) { return numerator(pow<-1>(m)); } // Implementation of conversion to ratio goes here, because it needs `numerator()` and `denominator()`. constexpr ratio as_ratio(Magnitude auto m) - requires(is_rational(decltype(m){})) + requires(is_rational(m)) { return ratio{ get_value(numerator(m)), From b3afd61b2d70a656c263cbc6378b3d06f7921c06 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 May 2022 22:55:55 +0200 Subject: [PATCH 15/35] refactor: duplicated concepts checks removed to improve compile times `magnitude` template parameters are already checked during `magnitude` type instantiation. --- src/core/include/units/magnitude.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index c51285e2..4b95c6e4 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -370,7 +370,7 @@ struct magnitude { namespace detail { template inline constexpr bool is_magnitude = false; -template +template inline constexpr bool is_magnitude> = true; } // namespace detail @@ -383,7 +383,7 @@ concept Magnitude = detail::is_magnitude; /** * @brief The value of a Magnitude in a desired type T. */ -template +template // TODO(chogg): Migrate this to use `treat_as_floating_point`. requires(!std::is_integral_v || is_integral(magnitude{})) constexpr T get_value(const magnitude&) @@ -404,7 +404,7 @@ struct pi_base { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Magnitude equality implementation. -template +template constexpr bool operator==(magnitude, magnitude) { if constexpr (sizeof...(LeftBPs) == sizeof...(RightBPs)) { @@ -417,7 +417,7 @@ constexpr bool operator==(magnitude, magnitude) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Magnitude rational powers implementation. -template +template constexpr auto pow(magnitude) { if constexpr (E == 0) { @@ -427,13 +427,13 @@ constexpr auto pow(magnitude) } } -template +template constexpr auto sqrt(magnitude m) { return pow(m); } -template +template constexpr auto cbrt(magnitude m) { return pow(m); @@ -448,7 +448,7 @@ constexpr auto operator*(magnitude<>, Magnitude auto m) { return m; } constexpr auto operator*(Magnitude auto m, magnitude<>) { return m; } // Recursive case for the product of any two non-identity Magnitudes. -template +template constexpr auto operator*(magnitude, magnitude) { // Case for when H1 has the smaller base. @@ -495,7 +495,7 @@ constexpr auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1 namespace detail { // The largest integer which can be extracted from any magnitude with only a single basis vector. -template +template constexpr auto integer_part(magnitude) { constexpr auto power_num = numerator(BP.power); @@ -515,7 +515,7 @@ constexpr auto integer_part(magnitude) } // namespace detail -template +template constexpr auto numerator(magnitude) { return (detail::integer_part(magnitude{}) * ... * magnitude<>{}); @@ -553,7 +553,7 @@ constexpr ratio as_ratio(Magnitude auto m) // minimum power for each base (where absent bases implicitly have a power of 0). namespace detail { -template +template constexpr auto remove_positive_power(magnitude m) { if constexpr (numerator(BP.power) < 0) { @@ -563,7 +563,7 @@ constexpr auto remove_positive_power(magnitude m) } } -template +template constexpr auto remove_positive_powers(magnitude) { return (magnitude<>{} * ... * remove_positive_power(magnitude{})); @@ -576,7 +576,7 @@ constexpr auto common_magnitude(magnitude<>, Magnitude auto m) { return detail:: constexpr auto common_magnitude(Magnitude auto m, magnitude<>) { return detail::remove_positive_powers(m); } // Recursive case for the common Magnitude of any two non-identity Magnitudes. -template +template constexpr auto common_magnitude(magnitude m1, magnitude m2) { using detail::remove_positive_power; From 54489a57b2ed9e9742c137c8260bf3a1b98f1bd3 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 May 2022 22:56:58 +0200 Subject: [PATCH 16/35] refactor: missing constraints on a return type added Implementation verification and interface documentation purposes --- src/core/include/units/magnitude.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index 4b95c6e4..bf729945 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -443,13 +443,13 @@ constexpr auto cbrt(magnitude m) // Magnitude product implementation. // Base cases, for when either (or both) inputs are the identity. -constexpr auto operator*(magnitude<>, magnitude<>) { return magnitude<>{}; } -constexpr auto operator*(magnitude<>, Magnitude auto m) { return m; } -constexpr auto operator*(Magnitude auto m, magnitude<>) { return m; } +constexpr Magnitude auto operator*(magnitude<>, magnitude<>) { return magnitude<>{}; } +constexpr Magnitude auto operator*(magnitude<>, Magnitude auto m) { return m; } +constexpr Magnitude auto operator*(Magnitude auto m, magnitude<>) { return m; } // Recursive case for the product of any two non-identity Magnitudes. template -constexpr auto operator*(magnitude, magnitude) +constexpr Magnitude auto operator*(magnitude, magnitude) { // Case for when H1 has the smaller base. if constexpr (H1.get_base() < H2.get_base()) { From 0ed43d30871574b6de41ea622bf5346994150364 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 May 2022 23:58:30 +0200 Subject: [PATCH 17/35] fix: restoring original `as_magnitude` implementation as it was correct --- src/core/include/units/magnitude.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index bf729945..01c00e0c 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -652,7 +652,7 @@ template requires(R.num > 0) constexpr Magnitude auto as_magnitude() { - return pow(detail::prime_factorization_v<10>) * detail::prime_factorization_v / detail::prime_factorization_v; + return pow(detail::prime_factorization_v<10>) * detail::prime_factorization_v / detail::prime_factorization_v; } } // namespace units From 0765fb26a2801d5bf09a69a2fbae86e28b6b79f7 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 12 May 2022 10:55:48 +0200 Subject: [PATCH 18/35] fix: MSVC complains that an address of NTTP is taken here --- src/core/include/units/magnitude.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index 01c00e0c..ad878fa6 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -420,7 +420,7 @@ constexpr bool operator==(magnitude, magnitude) template constexpr auto pow(magnitude) { - if constexpr (E == 0) { + if constexpr (E.num == 0) { return magnitude<>{}; } else { return magnitude{}; From 59fa420272ce2772b804dee56dfd5c5bc363097e Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Thu, 12 May 2022 16:52:05 +0000 Subject: [PATCH 19/35] Fix remaining builds/tests --- src/core/include/units/unit.h | 14 +++++++------- .../si-fps/include/units/isq/si/fps/length.h | 3 +-- test/unit_test/runtime/fmt_units_test.cpp | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/core/include/units/unit.h b/src/core/include/units/unit.h index f5d14220..c429f837 100644 --- a/src/core/include/units/unit.h +++ b/src/core/include/units/unit.h @@ -126,7 +126,7 @@ struct prefixed_unit : downcast_dispatch -struct derived_unit : downcast_dispatch> {}; +struct derived_unit : downcast_dispatch(), Child>> {}; /** * @brief A unit with a deduced ratio and symbol @@ -195,8 +195,8 @@ namespace detail { template void is_named_impl(const volatile named_unit*); -template -void is_named_impl(const volatile named_scaled_unit*); +template +void is_named_impl(const volatile named_scaled_unit*); template void is_named_impl(const volatile prefixed_unit*); @@ -213,8 +213,8 @@ inline constexpr bool is_named = requires(U * u) { is_named_impl(u); }; template void can_be_prefixed_impl(const volatile named_unit*); -template -void can_be_prefixed_impl(const volatile named_scaled_unit*); +template +void can_be_prefixed_impl(const volatile named_scaled_unit*); template void can_be_prefixed_impl(const volatile alias_unit*); @@ -222,8 +222,8 @@ void can_be_prefixed_impl(const volatile alias_unit*); template inline constexpr bool can_be_prefixed = requires(U * u) { can_be_prefixed_impl(u); }; -template -inline constexpr bool can_be_prefixed> = can_be_prefixed; +template +inline constexpr bool can_be_prefixed> = can_be_prefixed; } // namespace detail diff --git a/src/systems/si-fps/include/units/isq/si/fps/length.h b/src/systems/si-fps/include/units/isq/si/fps/length.h index 35f51d7f..04a9e930 100644 --- a/src/systems/si-fps/include/units/isq/si/fps/length.h +++ b/src/systems/si-fps/include/units/isq/si/fps/length.h @@ -39,6 +39,7 @@ using si::international::fathom; using si::international::foot; using si::international::inch; using si::international::mil; +using si::international::mile; using si::international::thou; using si::international::yard; @@ -47,8 +48,6 @@ struct thousandth : alias_unit {}; struct kiloyard : prefixed_unit {}; -struct mile : named_scaled_unit(), foot> {}; - struct nautical_mile : named_scaled_unit(), yard> {}; struct dim_length : isq::dim_length {}; diff --git a/test/unit_test/runtime/fmt_units_test.cpp b/test/unit_test/runtime/fmt_units_test.cpp index 22c04fe2..58531174 100644 --- a/test/unit_test/runtime/fmt_units_test.cpp +++ b/test/unit_test/runtime/fmt_units_test.cpp @@ -66,7 +66,7 @@ TEST_CASE("std::format on synthesized unit symbols", "[text][fmt]") CHECK(STD_FMT::format("{}", 1_q_fathom_us) == "1 fathom(us)"); CHECK(STD_FMT::format("{}", 1_q_mi) == "1 mi"); CHECK(STD_FMT::format("{}", 1_q_mi_us) == "1 mi(us)"); - CHECK(STD_FMT::format("{}", 1_q_naut_mi) == "1 nmi"); + CHECK(STD_FMT::format("{}", 1_q_naut_mi) == "1 mi(naut)"); CHECK(STD_FMT::format("{}", 1_q_ch) == "1 ch"); CHECK(STD_FMT::format("{}", 1_q_rd) == "1 rd"); CHECK(STD_FMT::format("{}", 1_q_thou) == "1 thou"); From 88a69cecd88090ebba5e27eeb3ade8e086778438 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Thu, 12 May 2022 18:02:47 +0000 Subject: [PATCH 20/35] Try fixing build Unfortunately, my local build hangs, so I can't tell whether this works. --- src/core/include/units/magnitude.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index ad878fa6..271662f4 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -525,7 +525,7 @@ constexpr auto denominator(Magnitude auto m) { return numerator(pow<-1>(m)); } // Implementation of conversion to ratio goes here, because it needs `numerator()` and `denominator()`. constexpr ratio as_ratio(Magnitude auto m) - requires(is_rational(m)) + requires(is_rational(decltype(m){})) { return ratio{ get_value(numerator(m)), @@ -652,7 +652,8 @@ template requires(R.num > 0) constexpr Magnitude auto as_magnitude() { - return pow(detail::prime_factorization_v<10>) * detail::prime_factorization_v / detail::prime_factorization_v; + return pow(detail::prime_factorization_v<10>) * detail::prime_factorization_v / + detail::prime_factorization_v; } } // namespace units From f36205f08d032c523180ebb0e5303842279f10ba Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Thu, 12 May 2022 18:17:14 +0000 Subject: [PATCH 21/35] Capture NTTP in variable This should make it OK for MSVC to take the address, working around a compiler bug. --- src/core/include/units/magnitude.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index 271662f4..cc0028c3 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -420,7 +420,8 @@ constexpr bool operator==(magnitude, magnitude) template constexpr auto pow(magnitude) { - if constexpr (E.num == 0) { + constexpr auto exp = E; + if constexpr (exp.num == 0) { return magnitude<>{}; } else { return magnitude{}; From 2bdc778ab6785b84cd42bb3f3be062d715f51009 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Thu, 12 May 2022 18:19:20 +0000 Subject: [PATCH 22/35] Constrain template --- src/core/include/units/quantity_cast.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/include/units/quantity_cast.h b/src/core/include/units/quantity_cast.h index 8c40a87f..fdb90aef 100644 --- a/src/core/include/units/quantity_cast.h +++ b/src/core/include/units/quantity_cast.h @@ -61,8 +61,8 @@ inline constexpr Magnitude auto quantity_magnitude> = [] { } }(); -template -inline constexpr ratio quantity_ratio = as_ratio(quantity_magnitude); +template +inline constexpr ratio quantity_ratio = as_ratio(quantity_magnitude); template inline constexpr Magnitude auto cast_magnitude = [] { From 86b81c9384bcd8160fc8bb6dc0b80a4b4e7e7d98 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Thu, 12 May 2022 21:53:51 +0000 Subject: [PATCH 23/35] Undo misguided fix attempt --- src/core/include/units/magnitude.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index cc0028c3..271662f4 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -420,8 +420,7 @@ constexpr bool operator==(magnitude, magnitude) template constexpr auto pow(magnitude) { - constexpr auto exp = E; - if constexpr (exp.num == 0) { + if constexpr (E.num == 0) { return magnitude<>{}; } else { return magnitude{}; From 6c1062e166238b560c78e789122e1badd3c5971e Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sat, 14 May 2022 11:22:09 +0200 Subject: [PATCH 24/35] fix: `UNITS_MSVC_WORKAROUND()` added --- src/core/include/units/bits/common_type.h | 6 +++--- src/core/include/units/bits/external/hacks.h | 10 ++++++++++ src/core/include/units/derived_dimension.h | 2 +- src/core/include/units/prefix.h | 2 +- src/core/include/units/unit.h | 2 +- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/core/include/units/bits/common_type.h b/src/core/include/units/bits/common_type.h index 40880927..46f7cb5f 100644 --- a/src/core/include/units/bits/common_type.h +++ b/src/core/include/units/bits/common_type.h @@ -67,9 +67,9 @@ struct common_quantity_reference_impl, reference> { template struct common_quantity_reference_impl, reference> { using dimension = conditional, D2, D1>; - static constexpr Magnitude auto m1 = D1::base_units_ratio * U1::mag; - static constexpr Magnitude auto m2 = D2::base_units_ratio * U2::mag; - static constexpr Magnitude auto cm = common_magnitude(m1, m2); + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto m1 = D1::base_units_ratio * U1::mag; + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto m2 = D2::base_units_ratio * U2::mag; + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto cm = common_magnitude(m1, m2); using unit = downcast_unit; using type = reference; }; diff --git a/src/core/include/units/bits/external/hacks.h b/src/core/include/units/bits/external/hacks.h index 5a983540..169718ba 100644 --- a/src/core/include/units/bits/external/hacks.h +++ b/src/core/include/units/bits/external/hacks.h @@ -100,6 +100,16 @@ #endif +#if UNITS_COMP_MSVC + +#define UNITS_MSVC_WORKAROUND(X) + +#else + +#define UNITS_MSVC_WORKAROUND(X) (X) + +#endif + namespace std { diff --git a/src/core/include/units/derived_dimension.h b/src/core/include/units/derived_dimension.h index 76c8dc9f..cfe0830c 100644 --- a/src/core/include/units/derived_dimension.h +++ b/src/core/include/units/derived_dimension.h @@ -85,7 +85,7 @@ template struct derived_dimension : downcast_dispatch> { using recipe = exponent_list; using coherent_unit = U; - static constexpr Magnitude auto base_units_ratio = + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto base_units_ratio = detail::absolute_magnitude(typename derived_dimension::exponents()); }; diff --git a/src/core/include/units/prefix.h b/src/core/include/units/prefix.h index e460b4de..0a95499c 100644 --- a/src/core/include/units/prefix.h +++ b/src/core/include/units/prefix.h @@ -36,7 +36,7 @@ namespace detail { template struct prefix_base : downcast_base> { - static constexpr Magnitude auto mag = as_magnitude(); + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = as_magnitude(); }; } // namespace detail diff --git a/src/core/include/units/unit.h b/src/core/include/units/unit.h index c429f837..3e4f7253 100644 --- a/src/core/include/units/unit.h +++ b/src/core/include/units/unit.h @@ -60,7 +60,7 @@ inline constexpr bool can_be_prefixed = false; */ template struct scaled_unit : downcast_base> { - static constexpr Magnitude auto mag = M; + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = M; using reference = U; }; From b559a5f98f755099d30aee4270b4ee01faf6be94 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sat, 14 May 2022 11:24:00 +0200 Subject: [PATCH 25/35] fix: explicit `ratio` creation added as a workaround for MSVC --- src/core/include/units/magnitude.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index 271662f4..61b67611 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -652,7 +652,7 @@ template requires(R.num > 0) constexpr Magnitude auto as_magnitude() { - return pow(detail::prime_factorization_v<10>) * detail::prime_factorization_v / + return pow(detail::prime_factorization_v<10>) * detail::prime_factorization_v / detail::prime_factorization_v; } From 43844c484cd6384b3538cdd3b22b9b3f41707a11 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sat, 14 May 2022 11:27:12 +0200 Subject: [PATCH 26/35] fix: `UNITS_MSVC_WORKAROUND()` fixed for other compilers --- src/core/include/units/bits/external/hacks.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/include/units/bits/external/hacks.h b/src/core/include/units/bits/external/hacks.h index 169718ba..e08faec9 100644 --- a/src/core/include/units/bits/external/hacks.h +++ b/src/core/include/units/bits/external/hacks.h @@ -106,7 +106,7 @@ #else -#define UNITS_MSVC_WORKAROUND(X) (X) +#define UNITS_MSVC_WORKAROUND(X) X #endif From db09a065ab7a88f28d934ec1f7ac8f477aee176f Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 24 May 2022 12:48:07 +0200 Subject: [PATCH 27/35] =?UTF-8?q?refactor:=20:boom:=20!=20"=20=E2=8B=85=20?= =?UTF-8?q?"=20replaced=20with=20"=E2=8B=85"=20in=20derived=20unit=20symbo?= =?UTF-8?q?ls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/framework/units.rst | 8 ++++---- .../include/units/bits/derived_symbol_text.h | 2 +- test/unit_test/runtime/fmt_test.cpp | 16 ++++++++-------- test/unit_test/runtime/fmt_units_test.cpp | 12 ++++++------ test/unit_test/static/fps_test.cpp | 8 ++++---- test/unit_test/static/si_test.cpp | 6 +++--- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/framework/units.rst b/docs/framework/units.rst index 8e37e3e2..8de1b8b3 100644 --- a/docs/framework/units.rst +++ b/docs/framework/units.rst @@ -303,15 +303,15 @@ will result in a different unnamed unit symbol: struct dim_momentum : derived_dimension, exponent, - exponent> {}; // kg ⋅ m/s + exponent> {}; // kg⋅m/s struct dim_momentum : derived_dimension, exponent, - exponent> {}; // m ⋅ kg/s + exponent> {}; // m⋅kg/s struct dim_momentum : derived_dimension, exponent, - exponent> {}; // 1/s ⋅ m ⋅ kg + exponent> {}; // 1/s⋅m⋅kg where ``kilogram_metre_per_second`` is defined as:: @@ -325,7 +325,7 @@ However, the easiest way to define momentum is just to use the struct dim_momentum : derived_dimension, - exponent> {}; // kg ⋅ m/s + exponent> {}; // kg⋅m/s In such a case the library will do its magic and will automatically unpack a provided derived dimension to its base dimensions in order to diff --git a/src/core/include/units/bits/derived_symbol_text.h b/src/core/include/units/bits/derived_symbol_text.h index 10a08de3..2d358725 100644 --- a/src/core/include/units/bits/derived_symbol_text.h +++ b/src/core/include/units/bits/derived_symbol_text.h @@ -42,7 +42,7 @@ constexpr auto operator_text() if constexpr (Divide && NegativeExpCount == 1) { return basic_fixed_string("/"); } else { - return basic_symbol_text(" ⋅ ", " "); + return basic_symbol_text("⋅", " "); } } } diff --git a/test/unit_test/runtime/fmt_test.cpp b/test/unit_test/runtime/fmt_test.cpp index 3f729638..3a28fbcf 100644 --- a/test/unit_test/runtime/fmt_test.cpp +++ b/test/unit_test/runtime/fmt_test.cpp @@ -318,7 +318,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 2_q_s * 2_q_m * 2_q_kg; os << q; - SECTION("iostream") { CHECK(os.str() == "8 m ⋅ kg ⋅ s"); } + SECTION("iostream") { CHECK(os.str() == "8 m⋅kg⋅s"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } @@ -330,7 +330,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 2._q_s * si::cgs::length(2) * si::cgs::mass(2); os << q; - SECTION("iostream") { CHECK(os.str() == "8 cm ⋅ g ⋅ s"); } + SECTION("iostream") { CHECK(os.str() == "8 cm⋅g⋅s"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } @@ -343,7 +343,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 4_q_km * 2_q_s; os << q; - SECTION("iostream") { CHECK(os.str() == "8 × 10³ m ⋅ s"); } + SECTION("iostream") { CHECK(os.str() == "8 × 10³ m⋅s"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } @@ -379,7 +379,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 2._q_s * si::cgs::length(2) * si::cgs::mass(2); os << q; - SECTION("iostream") { CHECK(os.str() == "8 × 10⁵ cm ⋅ g ⋅ s"); } + SECTION("iostream") { CHECK(os.str() == "8 × 10⁵ cm⋅g⋅s"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } @@ -392,7 +392,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") os << q; // TODO(chogg): Reinstate after format/Magnitude design. - // SECTION("iostream") { CHECK(os.str() == "30 [6 × 10⁻²] 1/m ⋅ s"); } + // SECTION("iostream") { CHECK(os.str() == "30 [6 × 10⁻²] 1/m⋅s"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } @@ -404,7 +404,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 4_q_m * 2_q_s; os << q; - SECTION("iostream") { CHECK(os.str() == "8 m ⋅ s"); } + SECTION("iostream") { CHECK(os.str() == "8 m⋅s"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } @@ -416,7 +416,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 4_q_m * 2_q_s * 2_q_s; os << q; - SECTION("iostream") { CHECK(os.str() == "16 m ⋅ s²"); } + SECTION("iostream") { CHECK(os.str() == "16 m⋅s²"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } @@ -428,7 +428,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 8_q_s / 2_q_m / 2_q_m; os << q; - SECTION("iostream") { CHECK(os.str() == "2 1/m² ⋅ s"); } + SECTION("iostream") { CHECK(os.str() == "2 1/m²⋅s"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } diff --git a/test/unit_test/runtime/fmt_units_test.cpp b/test/unit_test/runtime/fmt_units_test.cpp index 58531174..0f611eed 100644 --- a/test/unit_test/runtime/fmt_units_test.cpp +++ b/test/unit_test/runtime/fmt_units_test.cpp @@ -167,7 +167,7 @@ TEST_CASE("std::format on synthesized unit symbols", "[text][fmt]") SECTION("momentum") { - CHECK(STD_FMT::format("{}", 1_q_kg_m_per_s) == "1 kg ⋅ m/s"); + CHECK(STD_FMT::format("{}", 1_q_kg_m_per_s) == "1 kg⋅m/s"); CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_kg_m_per_s) == "1 kg m/s"); } @@ -244,7 +244,7 @@ TEST_CASE("std::format on synthesized unit symbols", "[text][fmt]") SECTION("dynamic viscosity") { - CHECK(STD_FMT::format("{}", 1_q_Pa_s) == "1 Pa ⋅ s"); + CHECK(STD_FMT::format("{}", 1_q_Pa_s) == "1 Pa⋅s"); CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_Pa_s) == "1 Pa s"); } @@ -252,19 +252,19 @@ TEST_CASE("std::format on synthesized unit symbols", "[text][fmt]") SECTION("specific heat capacity") { - CHECK(STD_FMT::format("{}", 1_q_J_per_kg_K) == "1 J ⋅ K⁻¹ ⋅ kg⁻¹"); + CHECK(STD_FMT::format("{}", 1_q_J_per_kg_K) == "1 J⋅K⁻¹⋅kg⁻¹"); CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_J_per_kg_K) == "1 J K^-1 kg^-1"); } SECTION("molar heath capacity") { - CHECK(STD_FMT::format("{}", 1_q_J_per_mol_K) == "1 J ⋅ K⁻¹ ⋅ mol⁻¹"); + CHECK(STD_FMT::format("{}", 1_q_J_per_mol_K) == "1 J⋅K⁻¹⋅mol⁻¹"); CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_J_per_mol_K) == "1 J K^-1 mol^-1"); } SECTION("thermal conductivity") { - CHECK(STD_FMT::format("{}", 1_q_W_per_m_K) == "1 W ⋅ m⁻¹ ⋅ K⁻¹"); + CHECK(STD_FMT::format("{}", 1_q_W_per_m_K) == "1 W⋅m⁻¹⋅K⁻¹"); CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_W_per_m_K) == "1 W m^-1 K^-1"); } @@ -284,7 +284,7 @@ TEST_CASE("std::format on synthesized unit symbols", "[text][fmt]") SECTION("molar energy") { CHECK(STD_FMT::format("{}", 1_q_J_per_mol) == "1 J/mol"); } - SECTION("torque") { CHECK(STD_FMT::format("{}", 1_q_N_m_per_rad) == "1 N ⋅ m/rad"); } + SECTION("torque") { CHECK(STD_FMT::format("{}", 1_q_N_m_per_rad) == "1 N⋅m/rad"); } SECTION("storage_capacity") { diff --git a/test/unit_test/static/fps_test.cpp b/test/unit_test/static/fps_test.cpp index 4304f120..774971e7 100644 --- a/test/unit_test/static/fps_test.cpp +++ b/test/unit_test/static/fps_test.cpp @@ -88,8 +88,8 @@ static_assert(10_q_pdl * 10_q_ft == 100_q_ft_pdl); static_assert(100_q_ft_pdl / 10_q_ft == 10_q_pdl); static_assert(100_q_ft_pdl / 10_q_pdl == 10_q_ft); -static_assert(detail::unit_text() == basic_symbol_text("ft ⋅ pdl", "ft pdl")); -static_assert(detail::unit_text() == basic_symbol_text("ft ⋅ lbf", "ft lbf")); +static_assert(detail::unit_text() == basic_symbol_text("ft⋅pdl", "ft pdl")); +static_assert(detail::unit_text() == basic_symbol_text("ft⋅lbf", "ft lbf")); /* ************** DERIVED DIMENSIONS IN TERMS OF OTHER UNITS **************** */ @@ -99,8 +99,8 @@ static_assert(10_q_ft_pdl / 10_q_s == 1_q_ft_pdl_per_s); static_assert(1_q_ft_pdl_per_s * 10_q_s == 10_q_ft_pdl); static_assert(10_q_ft_pdl / 1_q_ft_pdl_per_s == 10_q_s); -static_assert(detail::unit_text() == basic_symbol_text("ft ⋅ pdl/s", "ft pdl/s")); +static_assert(detail::unit_text() == basic_symbol_text("ft⋅pdl/s", "ft pdl/s")); static_assert(detail::unit_text() == - basic_symbol_text("ft ⋅ lbf/s", "ft lbf/s")); + basic_symbol_text("ft⋅lbf/s", "ft lbf/s")); } // namespace diff --git a/test/unit_test/static/si_test.cpp b/test/unit_test/static/si_test.cpp index bbd81a8e..91baf7c3 100644 --- a/test/unit_test/static/si_test.cpp +++ b/test/unit_test/static/si_test.cpp @@ -334,7 +334,7 @@ static_assert(detail::unit_text() == basic_ // dynamic viscosity static_assert(1_q_Pa_s == 1_q_N * 1_q_s / 1_q_m2); -static_assert(detail::unit_text() == basic_symbol_text("Pa ⋅ s", "Pa s")); +static_assert(detail::unit_text() == basic_symbol_text("Pa⋅s", "Pa s")); // [specific|molar] heath capacity @@ -344,13 +344,13 @@ static_assert(1_q_J_per_mol_K == 1_q_J_per_K / 1_q_mol); static_assert(detail::unit_text() == "J/K"); static_assert(detail::unit_text() == - basic_symbol_text("J ⋅ K⁻¹ ⋅ kg⁻¹", "J K^-1 kg^-1")); + basic_symbol_text("J⋅K⁻¹⋅kg⁻¹", "J K^-1 kg^-1")); // thermal conductivity static_assert(20_q_W_per_m_K * 10_q_m * 300_q_K == 60'000_q_W); static_assert(detail::unit_text() == - basic_symbol_text("W ⋅ m⁻¹ ⋅ K⁻¹", "W m^-1 K^-1")); + basic_symbol_text("W⋅m⁻¹⋅K⁻¹", "W m^-1 K^-1")); // electric field strength From 46b378174309142517ee186464218914c22c83ad Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 24 May 2022 14:20:17 +0200 Subject: [PATCH 28/35] =?UTF-8?q?Revert=20"refactor:=20:boom:=20!=20"=20?= =?UTF-8?q?=E2=8B=85=20"=20replaced=20with=20"=E2=8B=85"=20in=20derived=20?= =?UTF-8?q?unit=20symbols"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit db09a065ab7a88f28d934ec1f7ac8f477aee176f. --- docs/framework/units.rst | 8 ++++---- .../include/units/bits/derived_symbol_text.h | 2 +- test/unit_test/runtime/fmt_test.cpp | 16 ++++++++-------- test/unit_test/runtime/fmt_units_test.cpp | 12 ++++++------ test/unit_test/static/fps_test.cpp | 8 ++++---- test/unit_test/static/si_test.cpp | 6 +++--- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/framework/units.rst b/docs/framework/units.rst index 8de1b8b3..8e37e3e2 100644 --- a/docs/framework/units.rst +++ b/docs/framework/units.rst @@ -303,15 +303,15 @@ will result in a different unnamed unit symbol: struct dim_momentum : derived_dimension, exponent, - exponent> {}; // kg⋅m/s + exponent> {}; // kg ⋅ m/s struct dim_momentum : derived_dimension, exponent, - exponent> {}; // m⋅kg/s + exponent> {}; // m ⋅ kg/s struct dim_momentum : derived_dimension, exponent, - exponent> {}; // 1/s⋅m⋅kg + exponent> {}; // 1/s ⋅ m ⋅ kg where ``kilogram_metre_per_second`` is defined as:: @@ -325,7 +325,7 @@ However, the easiest way to define momentum is just to use the struct dim_momentum : derived_dimension, - exponent> {}; // kg⋅m/s + exponent> {}; // kg ⋅ m/s In such a case the library will do its magic and will automatically unpack a provided derived dimension to its base dimensions in order to diff --git a/src/core/include/units/bits/derived_symbol_text.h b/src/core/include/units/bits/derived_symbol_text.h index 2d358725..10a08de3 100644 --- a/src/core/include/units/bits/derived_symbol_text.h +++ b/src/core/include/units/bits/derived_symbol_text.h @@ -42,7 +42,7 @@ constexpr auto operator_text() if constexpr (Divide && NegativeExpCount == 1) { return basic_fixed_string("/"); } else { - return basic_symbol_text("⋅", " "); + return basic_symbol_text(" ⋅ ", " "); } } } diff --git a/test/unit_test/runtime/fmt_test.cpp b/test/unit_test/runtime/fmt_test.cpp index 3a28fbcf..3f729638 100644 --- a/test/unit_test/runtime/fmt_test.cpp +++ b/test/unit_test/runtime/fmt_test.cpp @@ -318,7 +318,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 2_q_s * 2_q_m * 2_q_kg; os << q; - SECTION("iostream") { CHECK(os.str() == "8 m⋅kg⋅s"); } + SECTION("iostream") { CHECK(os.str() == "8 m ⋅ kg ⋅ s"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } @@ -330,7 +330,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 2._q_s * si::cgs::length(2) * si::cgs::mass(2); os << q; - SECTION("iostream") { CHECK(os.str() == "8 cm⋅g⋅s"); } + SECTION("iostream") { CHECK(os.str() == "8 cm ⋅ g ⋅ s"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } @@ -343,7 +343,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 4_q_km * 2_q_s; os << q; - SECTION("iostream") { CHECK(os.str() == "8 × 10³ m⋅s"); } + SECTION("iostream") { CHECK(os.str() == "8 × 10³ m ⋅ s"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } @@ -379,7 +379,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 2._q_s * si::cgs::length(2) * si::cgs::mass(2); os << q; - SECTION("iostream") { CHECK(os.str() == "8 × 10⁵ cm⋅g⋅s"); } + SECTION("iostream") { CHECK(os.str() == "8 × 10⁵ cm ⋅ g ⋅ s"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } @@ -392,7 +392,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") os << q; // TODO(chogg): Reinstate after format/Magnitude design. - // SECTION("iostream") { CHECK(os.str() == "30 [6 × 10⁻²] 1/m⋅s"); } + // SECTION("iostream") { CHECK(os.str() == "30 [6 × 10⁻²] 1/m ⋅ s"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } @@ -404,7 +404,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 4_q_m * 2_q_s; os << q; - SECTION("iostream") { CHECK(os.str() == "8 m⋅s"); } + SECTION("iostream") { CHECK(os.str() == "8 m ⋅ s"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } @@ -416,7 +416,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 4_q_m * 2_q_s * 2_q_s; os << q; - SECTION("iostream") { CHECK(os.str() == "16 m⋅s²"); } + SECTION("iostream") { CHECK(os.str() == "16 m ⋅ s²"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } @@ -428,7 +428,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 8_q_s / 2_q_m / 2_q_m; os << q; - SECTION("iostream") { CHECK(os.str() == "2 1/m²⋅s"); } + SECTION("iostream") { CHECK(os.str() == "2 1/m² ⋅ s"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } diff --git a/test/unit_test/runtime/fmt_units_test.cpp b/test/unit_test/runtime/fmt_units_test.cpp index 0f611eed..58531174 100644 --- a/test/unit_test/runtime/fmt_units_test.cpp +++ b/test/unit_test/runtime/fmt_units_test.cpp @@ -167,7 +167,7 @@ TEST_CASE("std::format on synthesized unit symbols", "[text][fmt]") SECTION("momentum") { - CHECK(STD_FMT::format("{}", 1_q_kg_m_per_s) == "1 kg⋅m/s"); + CHECK(STD_FMT::format("{}", 1_q_kg_m_per_s) == "1 kg ⋅ m/s"); CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_kg_m_per_s) == "1 kg m/s"); } @@ -244,7 +244,7 @@ TEST_CASE("std::format on synthesized unit symbols", "[text][fmt]") SECTION("dynamic viscosity") { - CHECK(STD_FMT::format("{}", 1_q_Pa_s) == "1 Pa⋅s"); + CHECK(STD_FMT::format("{}", 1_q_Pa_s) == "1 Pa ⋅ s"); CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_Pa_s) == "1 Pa s"); } @@ -252,19 +252,19 @@ TEST_CASE("std::format on synthesized unit symbols", "[text][fmt]") SECTION("specific heat capacity") { - CHECK(STD_FMT::format("{}", 1_q_J_per_kg_K) == "1 J⋅K⁻¹⋅kg⁻¹"); + CHECK(STD_FMT::format("{}", 1_q_J_per_kg_K) == "1 J ⋅ K⁻¹ ⋅ kg⁻¹"); CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_J_per_kg_K) == "1 J K^-1 kg^-1"); } SECTION("molar heath capacity") { - CHECK(STD_FMT::format("{}", 1_q_J_per_mol_K) == "1 J⋅K⁻¹⋅mol⁻¹"); + CHECK(STD_FMT::format("{}", 1_q_J_per_mol_K) == "1 J ⋅ K⁻¹ ⋅ mol⁻¹"); CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_J_per_mol_K) == "1 J K^-1 mol^-1"); } SECTION("thermal conductivity") { - CHECK(STD_FMT::format("{}", 1_q_W_per_m_K) == "1 W⋅m⁻¹⋅K⁻¹"); + CHECK(STD_FMT::format("{}", 1_q_W_per_m_K) == "1 W ⋅ m⁻¹ ⋅ K⁻¹"); CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_W_per_m_K) == "1 W m^-1 K^-1"); } @@ -284,7 +284,7 @@ TEST_CASE("std::format on synthesized unit symbols", "[text][fmt]") SECTION("molar energy") { CHECK(STD_FMT::format("{}", 1_q_J_per_mol) == "1 J/mol"); } - SECTION("torque") { CHECK(STD_FMT::format("{}", 1_q_N_m_per_rad) == "1 N⋅m/rad"); } + SECTION("torque") { CHECK(STD_FMT::format("{}", 1_q_N_m_per_rad) == "1 N ⋅ m/rad"); } SECTION("storage_capacity") { diff --git a/test/unit_test/static/fps_test.cpp b/test/unit_test/static/fps_test.cpp index 774971e7..4304f120 100644 --- a/test/unit_test/static/fps_test.cpp +++ b/test/unit_test/static/fps_test.cpp @@ -88,8 +88,8 @@ static_assert(10_q_pdl * 10_q_ft == 100_q_ft_pdl); static_assert(100_q_ft_pdl / 10_q_ft == 10_q_pdl); static_assert(100_q_ft_pdl / 10_q_pdl == 10_q_ft); -static_assert(detail::unit_text() == basic_symbol_text("ft⋅pdl", "ft pdl")); -static_assert(detail::unit_text() == basic_symbol_text("ft⋅lbf", "ft lbf")); +static_assert(detail::unit_text() == basic_symbol_text("ft ⋅ pdl", "ft pdl")); +static_assert(detail::unit_text() == basic_symbol_text("ft ⋅ lbf", "ft lbf")); /* ************** DERIVED DIMENSIONS IN TERMS OF OTHER UNITS **************** */ @@ -99,8 +99,8 @@ static_assert(10_q_ft_pdl / 10_q_s == 1_q_ft_pdl_per_s); static_assert(1_q_ft_pdl_per_s * 10_q_s == 10_q_ft_pdl); static_assert(10_q_ft_pdl / 1_q_ft_pdl_per_s == 10_q_s); -static_assert(detail::unit_text() == basic_symbol_text("ft⋅pdl/s", "ft pdl/s")); +static_assert(detail::unit_text() == basic_symbol_text("ft ⋅ pdl/s", "ft pdl/s")); static_assert(detail::unit_text() == - basic_symbol_text("ft⋅lbf/s", "ft lbf/s")); + basic_symbol_text("ft ⋅ lbf/s", "ft lbf/s")); } // namespace diff --git a/test/unit_test/static/si_test.cpp b/test/unit_test/static/si_test.cpp index 91baf7c3..bbd81a8e 100644 --- a/test/unit_test/static/si_test.cpp +++ b/test/unit_test/static/si_test.cpp @@ -334,7 +334,7 @@ static_assert(detail::unit_text() == basic_ // dynamic viscosity static_assert(1_q_Pa_s == 1_q_N * 1_q_s / 1_q_m2); -static_assert(detail::unit_text() == basic_symbol_text("Pa⋅s", "Pa s")); +static_assert(detail::unit_text() == basic_symbol_text("Pa ⋅ s", "Pa s")); // [specific|molar] heath capacity @@ -344,13 +344,13 @@ static_assert(1_q_J_per_mol_K == 1_q_J_per_K / 1_q_mol); static_assert(detail::unit_text() == "J/K"); static_assert(detail::unit_text() == - basic_symbol_text("J⋅K⁻¹⋅kg⁻¹", "J K^-1 kg^-1")); + basic_symbol_text("J ⋅ K⁻¹ ⋅ kg⁻¹", "J K^-1 kg^-1")); // thermal conductivity static_assert(20_q_W_per_m_K * 10_q_m * 300_q_K == 60'000_q_W); static_assert(detail::unit_text() == - basic_symbol_text("W⋅m⁻¹⋅K⁻¹", "W m^-1 K^-1")); + basic_symbol_text("W ⋅ m⁻¹ ⋅ K⁻¹", "W m^-1 K^-1")); // electric field strength From a8625d31c6be66f4a9e7e09f2694b32dd9b711ff Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Mon, 6 Jun 2022 19:05:57 +0200 Subject: [PATCH 29/35] docs: P0847 reference removed Resolves #364 --- docs/framework/dimensions.rst | 2 -- docs/framework/units.rst | 2 -- docs/references.rst | 2 -- 3 files changed, 6 deletions(-) diff --git a/docs/framework/dimensions.rst b/docs/framework/dimensions.rst index 534db6b2..524d1b8b 100644 --- a/docs/framework/dimensions.rst +++ b/docs/framework/dimensions.rst @@ -115,8 +115,6 @@ analysis in the library to work as expected. :abbr:`CRTP (Curiously Recurring Template Parameter)` Idiom and is used in many places in this library to provide :ref:`design/downcasting:The Downcasting Facility`. - Hopefully if [P0847]_ will land in C++23 the additional CRTP-related - template parameter will be removed from this definition. Obtaining a Unit of the Dimension diff --git a/docs/framework/units.rst b/docs/framework/units.rst index 8e37e3e2..8f9fd6aa 100644 --- a/docs/framework/units.rst +++ b/docs/framework/units.rst @@ -122,8 +122,6 @@ definitions of prefixed units using ``si::metre`` as a reference (i.e. :abbr:`CRTP (Curiously Recurring Template Parameter)` Idiom and is used in many places in this library to provide :ref:`design/downcasting:The Downcasting Facility`. - Hopefully if [P0847]_ will land in C++23 the additional CRTP-related - template parameter will be removed from this definition. It is important to notice here that :term:`SI` is not the only system used diff --git a/docs/references.rst b/docs/references.rst index 5e7c5842..35181006 100644 --- a/docs/references.rst +++ b/docs/references.rst @@ -2,5 +2,3 @@ References ========== .. [ISO80000] `ISO 80000-1:2009(E) "Quantities and units — Part 1: General" `_, International Organization for Standardization. - -.. [P0847] `"Deducing this" `_, Programming Language C++ proposal. From 9a6d6f9d2a767b9c2f978540e4371090f12b2b3a Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 9 Jun 2022 13:50:01 +0200 Subject: [PATCH 30/35] fix: MSVC conversion issues fixed --- test/unit_test/static/quantity_kind_test.cpp | 2 +- test/unit_test/static/quantity_point_kind_test.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit_test/static/quantity_kind_test.cpp b/test/unit_test/static/quantity_kind_test.cpp index de507037..c421ec12 100644 --- a/test/unit_test/static/quantity_kind_test.cpp +++ b/test/unit_test/static/quantity_kind_test.cpp @@ -219,7 +219,7 @@ static_assert(same(quantity_kind(rate_of_climb(0.01 static_assert(construct_from_only>(1).common() == 1); static_assert(construct_from_only>(1.0).common() == 1); -static_assert(construct_from_only>(1ULL).common().number() == 1); +static_assert(construct_from_only>(1LL).common().number() == 1); static_assert(construct_from_only>(1.0L).common().number() == 1); static_assert(!constructible_or_convertible_from>(1.0)); static_assert(!constructible_or_convertible_from>(1.0)); diff --git a/test/unit_test/static/quantity_point_kind_test.cpp b/test/unit_test/static/quantity_point_kind_test.cpp index f8febd8e..9a234a29 100644 --- a/test/unit_test/static/quantity_point_kind_test.cpp +++ b/test/unit_test/static/quantity_point_kind_test.cpp @@ -264,7 +264,7 @@ static_assert(construct_from_only>(1).relative().common() static_assert(construct_from_only>(short{1}).relative().common() == 1); static_assert(construct_from_only>(1).relative().common() == 1); static_assert(construct_from_only>(1).relative().common() == 1); -static_assert(construct_from_only>(1ULL).relative().common().number() == 1); +static_assert(construct_from_only>(1LL).relative().common().number() == 1); static_assert(construct_from_only>(1).relative().common().number() == 1); static_assert(!constructible_or_convertible_from>(1.0)); static_assert(!constructible_or_convertible_from>(1.0)); From f5e6d052ad0174b6bcbcac4942636aa608964be9 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 9 Jun 2022 13:50:41 +0200 Subject: [PATCH 31/35] fix: MSVC compilation error workaround --- src/core/include/units/chrono.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/core/include/units/chrono.h b/src/core/include/units/chrono.h index 33cd2b5b..91b4ab6b 100644 --- a/src/core/include/units/chrono.h +++ b/src/core/include/units/chrono.h @@ -33,8 +33,11 @@ namespace units { template struct quantity_like_traits> { +private: + static constexpr auto mag = as_magnitude(); +public: using dimension = isq::si::dim_time; - using unit = downcast_unit()>; + using unit = downcast_unit; using rep = Rep; [[nodiscard]] static constexpr rep number(const std::chrono::duration& q) { return q.count(); } }; @@ -44,8 +47,11 @@ struct clock_origin : point_origin {}; template struct quantity_point_like_traits>> { +private: + static constexpr auto mag = as_magnitude(); +public: using origin = clock_origin; - using unit = downcast_unit()>; + using unit = downcast_unit; using rep = Rep; [[nodiscard]] static constexpr auto relative(const std::chrono::time_point>& qp) { From 6d66ac566e13fd14692f3fc00b5c530cccb9ec27 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Wed, 15 Jun 2022 21:11:50 +0000 Subject: [PATCH 32/35] Migrate new `lumen` as well --- src/systems/si/include/units/isq/si/luminous_flux.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systems/si/include/units/isq/si/luminous_flux.h b/src/systems/si/include/units/isq/si/luminous_flux.h index 7d9592e3..f2b30290 100644 --- a/src/systems/si/include/units/isq/si/luminous_flux.h +++ b/src/systems/si/include/units/isq/si/luminous_flux.h @@ -36,7 +36,7 @@ namespace units::isq::si { // TODO Is this correct? Should we account for steradian here? How? -struct lumen : named_scaled_unit {}; +struct lumen : named_scaled_unit(), watt> {}; using dim_luminous_flux = dim_power; From c8ec771d69c1e6533d5d75a7248702ccd51b70c7 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Wed, 15 Jun 2022 21:19:39 +0000 Subject: [PATCH 33/35] Help factoring for eV/c --- src/systems/si-hep/include/units/isq/si/hep/momentum.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/systems/si-hep/include/units/isq/si/hep/momentum.h b/src/systems/si-hep/include/units/isq/si/hep/momentum.h index 21dc44c6..9b53b4c2 100644 --- a/src/systems/si-hep/include/units/isq/si/hep/momentum.h +++ b/src/systems/si-hep/include/units/isq/si/hep/momentum.h @@ -34,6 +34,11 @@ namespace units::isq::si::hep { + +// Necessary to factor `5'344'285'992'678`, which appears in the value for eV/c. +template<> +inline constexpr std::optional units::known_first_factor<296'904'777'371> = 157'667; + struct kilogram_metre_per_second : derived_unit {}; struct eV_per_c : From 3916b29e53f852aa1499aa6170cfc2bb44708281 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Wed, 15 Jun 2022 21:34:45 +0000 Subject: [PATCH 34/35] Move known_first_factor specialization outside namespace --- src/systems/si-hep/include/units/isq/si/hep/momentum.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/systems/si-hep/include/units/isq/si/hep/momentum.h b/src/systems/si-hep/include/units/isq/si/hep/momentum.h index 9b53b4c2..c6791501 100644 --- a/src/systems/si-hep/include/units/isq/si/hep/momentum.h +++ b/src/systems/si-hep/include/units/isq/si/hep/momentum.h @@ -32,13 +32,12 @@ #include #include -namespace units::isq::si::hep { - - // Necessary to factor `5'344'285'992'678`, which appears in the value for eV/c. template<> inline constexpr std::optional units::known_first_factor<296'904'777'371> = 157'667; +namespace units::isq::si::hep { + struct kilogram_metre_per_second : derived_unit {}; struct eV_per_c : From f004854a9ef7c5b141954f5d61db4aa2162eb053 Mon Sep 17 00:00:00 2001 From: Chip Hogg Date: Wed, 15 Jun 2022 21:44:27 +0000 Subject: [PATCH 35/35] Remove useful names to appease MSVC 14 Looks like another compiler bug, where it considers a formal parameter to be "unused" if it isn't used in any `if constexpr` branch. --- src/core/include/units/magnitude.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index 61b67611..ddf18cce 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -577,16 +577,16 @@ constexpr auto common_magnitude(Magnitude auto m, magnitude<>) { return detail:: // Recursive case for the common Magnitude of any two non-identity Magnitudes. template -constexpr auto common_magnitude(magnitude m1, magnitude m2) +constexpr auto common_magnitude(magnitude, magnitude) { using detail::remove_positive_power; if constexpr (H1.get_base() < H2.get_base()) { // When H1 has the smaller base, prepend to result from recursion. - return remove_positive_power(magnitude

{}) * common_magnitude(magnitude{}, m2); + return remove_positive_power(magnitude

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

{}) * common_magnitude(m1, magnitude{}); + 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{});