From da8721b997e5aa037b3e830e9c625e692795a40c Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sun, 3 Dec 2023 16:15:38 +0100 Subject: [PATCH] feat: users are now allowed to inherit their ow types from absolute point origins --- .../framework_basics/the_affine_space.md | 13 ++- example/currency.cpp | 2 +- example/include/geographic.h | 6 +- example/unmanned_aerial_vehicle.cpp | 4 +- .../mp-units/bits/quantity_point_concepts.h | 30 ++++--- src/core/include/mp-units/quantity_point.h | 42 ++++++++-- .../mp-units/systems/si/point_origins.h | 5 +- .../usc/include/mp-units/systems/usc/usc.h | 2 +- src/utility/include/mp-units/chrono.h | 2 +- test/unit_test/static/concepts_test.cpp | 6 +- test/unit_test/static/quantity_point_test.cpp | 82 +++++++++++++------ 11 files changed, 133 insertions(+), 61 deletions(-) diff --git a/docs/users_guide/framework_basics/the_affine_space.md b/docs/users_guide/framework_basics/the_affine_space.md index 20456d74..608246f2 100644 --- a/docs/users_guide/framework_basics/the_affine_space.md +++ b/docs/users_guide/framework_basics/the_affine_space.md @@ -64,9 +64,16 @@ The **absolute point origin** specifies where the "zero" of our measurement's sc specify such an origin by deriving from the `absolute_point_origin` class template: ```cpp -constexpr struct mean_sea_level : absolute_point_origin {} mean_sea_level; +constexpr struct mean_sea_level : absolute_point_origin {} mean_sea_level; ``` +!!! info + + The `absolute_point_origin` class template uses CRTP idiom to enforce the uniqueness of such a type. + You should pass the type of a derived class as the first argument of the template instantiation. + +*[CRTP]: Curiously Recurring Template Parameter + ### `quantity_point` The `quantity_point` class template specifies an absolute quantity with respect to an origin: @@ -185,7 +192,7 @@ the distance we will travel. We have to take a taxi to a local airport, fly to D a stopover in FRA, and, in the end, get a cab to the Gaylord Rockies Resort & Convention Center: ```cpp -constexpr struct home : absolute_point_origin {} home; +constexpr struct home : absolute_point_origin {} home; quantity_point home_airport = home + 15 * km; quantity_point fra_airport = home_airport + 829 * km; @@ -246,7 +253,7 @@ two predefined point origins: ```cpp namespace mp_units::si { -inline constexpr struct absolute_zero : absolute_point_origin {} absolute_zero; +inline constexpr struct absolute_zero : absolute_point_origin {} absolute_zero; inline constexpr struct ice_point : relative_point_origin {} ice_point; } diff --git a/example/currency.cpp b/example/currency.cpp index fb1ff9fa..b1bcd871 100644 --- a/example/currency.cpp +++ b/example/currency.cpp @@ -33,7 +33,7 @@ inline constexpr struct dim_currency : base_dimension<"$"> {} dim_currency; QUANTITY_SPEC(currency, dim_currency); -constexpr struct zero : absolute_point_origin {} zero; +constexpr struct zero : absolute_point_origin {} zero; inline constexpr struct euro : named_unit<"EUR", kind_of> {} euro; inline constexpr struct us_dollar : named_unit<"USD", kind_of> {} us_dollar; diff --git a/example/include/geographic.h b/example/include/geographic.h index d6830896..a647459a 100644 --- a/example/include/geographic.h +++ b/example/include/geographic.h @@ -38,7 +38,7 @@ namespace geographic { -inline constexpr struct mean_sea_level : mp_units::absolute_point_origin { +inline constexpr struct mean_sea_level : mp_units::absolute_point_origin { } mean_sea_level; using msl_altitude = mp_units::quantity_point; @@ -64,9 +64,9 @@ struct MP_UNITS_STD_FMT::formatter : formatter { +inline constexpr struct equator : mp_units::absolute_point_origin { } equator; -inline constexpr struct prime_meridian : mp_units::absolute_point_origin { +inline constexpr struct prime_meridian : mp_units::absolute_point_origin { } prime_meridian; diff --git a/example/unmanned_aerial_vehicle.cpp b/example/unmanned_aerial_vehicle.cpp index f880ba56..228e22f0 100644 --- a/example/unmanned_aerial_vehicle.cpp +++ b/example/unmanned_aerial_vehicle.cpp @@ -37,7 +37,7 @@ using namespace geographic; enum class earth_gravity_model { egm84_15, egm95_5, egm2008_1 }; template -struct height_above_ellipsoid_t : absolute_point_origin { +struct height_above_ellipsoid_t : absolute_point_origin, isq::altitude> { static constexpr earth_gravity_model egm = M; }; template @@ -107,7 +107,7 @@ hae_altitude to_hae(msl_altitude msl, position pos) // **** HAL **** // clang-format off -inline constexpr struct height_above_launch : absolute_point_origin {} height_above_launch; +inline constexpr struct height_above_launch : absolute_point_origin {} height_above_launch; // clang-format on using hal_altitude = quantity_point; diff --git a/src/core/include/mp-units/bits/quantity_point_concepts.h b/src/core/include/mp-units/bits/quantity_point_concepts.h index 7d2fa735..6488a096 100644 --- a/src/core/include/mp-units/bits/quantity_point_concepts.h +++ b/src/core/include/mp-units/bits/quantity_point_concepts.h @@ -30,7 +30,7 @@ namespace mp_units { -template +template struct absolute_point_origin; namespace detail { @@ -41,11 +41,11 @@ inline constexpr bool is_quantity_point = false; template inline constexpr bool is_specialization_of_absolute_point_origin = false; -template -inline constexpr bool is_specialization_of_absolute_point_origin> = true; +template +inline constexpr bool is_specialization_of_absolute_point_origin> = true; -template -void to_base_specialization_of_absolute_point_origin(const volatile absolute_point_origin*); +template +void to_base_specialization_of_absolute_point_origin(const volatile absolute_point_origin*); template inline constexpr bool is_derived_from_specialization_of_absolute_point_origin = @@ -123,18 +123,16 @@ template inline constexpr bool is_quantity_point = true; template -[[nodiscard]] consteval bool same_absolute_point_origins(PO1, PO2) +[[nodiscard]] consteval bool same_absolute_point_origins(PO1 po1, PO2 po2) { - if constexpr (is_same_v) - return true; - else if constexpr (is_derived_from_specialization_of_relative_point_origin && - is_derived_from_specialization_of_relative_point_origin) - return is_same_v, - std::remove_const_t>; - else if constexpr (is_derived_from_specialization_of_relative_point_origin) - return is_same_v, PO2>; - else if constexpr (is_derived_from_specialization_of_relative_point_origin) - return is_same_v>; + if constexpr (AbsolutePointOrigin && AbsolutePointOrigin) + return po1 == po2; + else if constexpr (RelativePointOrigin && RelativePointOrigin) + return po1.absolute_point_origin == po2.absolute_point_origin; + else if constexpr (RelativePointOrigin) + return po1.absolute_point_origin == po2; + else if constexpr (RelativePointOrigin) + return po1 == po2.absolute_point_origin; else return false; } diff --git a/src/core/include/mp-units/quantity_point.h b/src/core/include/mp-units/quantity_point.h index a69a29ac..3920437c 100644 --- a/src/core/include/mp-units/quantity_point.h +++ b/src/core/include/mp-units/quantity_point.h @@ -29,9 +29,25 @@ namespace mp_units { -template +namespace detail { + +template + requires requires { + { + T::zero() + } -> std::equality_comparable_with; + } +[[nodiscard]] constexpr bool is_eq_zero(T v) +{ + return v == T::zero(); +} + +} // namespace detail + +template struct absolute_point_origin { static constexpr QuantitySpec auto quantity_spec = QS; + using _type_ = absolute_point_origin; }; template @@ -47,14 +63,30 @@ struct relative_point_origin { static constexpr PointOrigin auto absolute_point_origin = QP.absolute_point_origin; }; +template +[[nodiscard]] consteval bool operator==(PO1 po1, PO2 po2) +{ + if constexpr (detail::AbsolutePointOrigin && detail::AbsolutePointOrigin) + return is_same_v; + else if constexpr (detail::RelativePointOrigin && detail::RelativePointOrigin) + return PO1::quantity_point == PO2::quantity_point; + else if constexpr (detail::RelativePointOrigin) + return detail::same_absolute_point_origins(po1, po2) && + detail::is_eq_zero(PO1::quantity_point.quantity_from(PO1::quantity_point.absolute_point_origin)); + else if constexpr (detail::RelativePointOrigin) + return detail::same_absolute_point_origins(po1, po2) && + detail::is_eq_zero(PO2::quantity_point.quantity_from(PO2::quantity_point.absolute_point_origin)); +} + namespace detail { -[[nodiscard]] consteval PointOrigin auto get_absolute_point_origin(PointOrigin auto po) +template +[[nodiscard]] consteval PointOrigin auto get_absolute_point_origin(PO po) { - if constexpr (requires { po.quantity_point.absolute_point_origin; }) - return po.quantity_point.absolute_point_origin; - else + if constexpr (AbsolutePointOrigin) return po; + else + return po.quantity_point.absolute_point_origin; } } // namespace detail diff --git a/src/systems/si/include/mp-units/systems/si/point_origins.h b/src/systems/si/include/mp-units/systems/si/point_origins.h index d395478e..784b2d12 100644 --- a/src/systems/si/include/mp-units/systems/si/point_origins.h +++ b/src/systems/si/include/mp-units/systems/si/point_origins.h @@ -28,8 +28,11 @@ namespace mp_units::si { // clang-format off -inline constexpr struct absolute_zero : absolute_point_origin {} absolute_zero; +inline constexpr struct absolute_zero : absolute_point_origin {} absolute_zero; +inline constexpr struct zeroth_kelvin : decltype(absolute_zero) {} zeroth_kelvin; + inline constexpr struct ice_point : relative_point_origin {} ice_point; +inline constexpr struct zeroth_degree_Celsius : decltype(ice_point) {} zeroth_degree_Celsius; // clang-format on } // namespace mp_units::si diff --git a/src/systems/usc/include/mp-units/systems/usc/usc.h b/src/systems/usc/include/mp-units/systems/usc/usc.h index bdfc0365..eb0ac882 100644 --- a/src/systems/usc/include/mp-units/systems/usc/usc.h +++ b/src/systems/usc/include/mp-units/systems/usc/usc.h @@ -112,7 +112,7 @@ inline constexpr struct inch_of_mercury : named_unit<"inHg", mag * si::degree_Celsius> {} degree_Fahrenheit; -inline constexpr struct zero_Fahrenheit : relative_point_origin {} zero_Fahrenheit; +inline constexpr struct zero_Fahrenheit : relative_point_origin {} zero_Fahrenheit; // clang-format on namespace unit_symbols { diff --git a/src/utility/include/mp-units/chrono.h b/src/utility/include/mp-units/chrono.h index d71372e3..5784dabb 100644 --- a/src/utility/include/mp-units/chrono.h +++ b/src/utility/include/mp-units/chrono.h @@ -79,7 +79,7 @@ struct quantity_like_traits> { }; template -struct chrono_point_origin_ : absolute_point_origin { +struct chrono_point_origin_ : absolute_point_origin, isq::time> { using clock = C; }; template diff --git a/test/unit_test/static/concepts_test.cpp b/test/unit_test/static/concepts_test.cpp index 978e1356..4312e638 100644 --- a/test/unit_test/static/concepts_test.cpp +++ b/test/unit_test/static/concepts_test.cpp @@ -38,7 +38,7 @@ namespace { using namespace mp_units; -inline constexpr struct my_origin : absolute_point_origin { +inline constexpr struct my_origin : absolute_point_origin { } my_origin; inline constexpr struct my_relative_origin : relative_point_origin { } my_relative_origin; @@ -358,7 +358,7 @@ static_assert(QuantityPoint>); static_assert(QuantityPoint>); static_assert(!QuantityPoint>); -static_assert(!QuantityPoint>); +static_assert(!QuantityPoint>); static_assert(!QuantityPoint); static_assert(!QuantityPoint); static_assert(!QuantityPoint); @@ -390,7 +390,7 @@ static_assert(QuantityPointOf); static_assert(PointOrigin); -static_assert(!PointOrigin>); +static_assert(!PointOrigin>); static_assert(!PointOrigin>); static_assert(!PointOrigin>); static_assert(!PointOrigin>); diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index 54094970..9d4c4754 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -36,26 +36,77 @@ using namespace mp_units::si::unit_symbols; using namespace std::chrono_literals; using sys_seconds = std::chrono::time_point; -inline constexpr struct mean_sea_level : absolute_point_origin { +inline constexpr struct mean_sea_level : absolute_point_origin { } mean_sea_level; +inline constexpr struct my_mean_sea_level : decltype(mean_sea_level) { +} my_mean_sea_level; + +inline constexpr struct same_mean_sea_level : relative_point_origin { +} same_mean_sea_level; + inline constexpr struct ground_level : relative_point_origin { } ground_level; +inline constexpr struct my_ground_level : decltype(ground_level) { +} my_ground_level; + +inline constexpr struct same_ground_level1 : relative_point_origin { +} same_ground_level1; + +inline constexpr struct same_ground_level2 : relative_point_origin { +} same_ground_level2; + inline constexpr struct tower_peak : relative_point_origin { } tower_peak; inline constexpr struct other_ground_level : relative_point_origin { } other_ground_level; -inline constexpr struct other_absolute_level : absolute_point_origin { +inline constexpr struct other_absolute_level : absolute_point_origin { } other_absolute_level; -inline constexpr struct zero : absolute_point_origin { +inline constexpr struct zero : absolute_point_origin { } zero; QUANTITY_SPEC(special_height, isq::height); + +////////////////// +// point origins +////////////////// + +static_assert(si::absolute_zero == si::zeroth_kelvin); +static_assert(si::ice_point == si::zeroth_degree_Celsius); +static_assert(si::absolute_zero != si::ice_point); +static_assert(si::zeroth_kelvin != si::zeroth_degree_Celsius); + +static_assert(my_mean_sea_level == mean_sea_level); +static_assert(my_mean_sea_level == same_mean_sea_level); + +static_assert(my_ground_level == ground_level); +static_assert(same_ground_level1 == ground_level); +static_assert(same_ground_level2 == my_ground_level); + +static_assert(mean_sea_level != other_absolute_level); +static_assert(my_mean_sea_level != other_absolute_level); +static_assert(ground_level != other_ground_level); + +template +struct absolute_po_ : absolute_point_origin, QS> {}; +template +inline constexpr absolute_po_ absolute_po; + +template +struct relative_po_ : relative_point_origin {}; +template +inline constexpr relative_po_ relative_po; + +static_assert(relative_po + isq::height(42 * m)>.quantity_spec == isq::height); +static_assert(relative_po> + isq::height(42 * m)>.quantity_spec == isq::height); +static_assert(relative_po + 42 * m>.quantity_spec == isq::height); + + ///////////////////// // class invariants ///////////////////// @@ -1115,7 +1166,7 @@ static_assert(ground_level - other_ground_level == -81 * m); static_assert(other_ground_level - tower_peak == 39 * m); static_assert(tower_peak - other_ground_level == -39 * m); -inline constexpr struct zero_m_per_s : absolute_point_origin> { +inline constexpr struct zero_m_per_s : absolute_point_origin> { } zero_m_per_s; // commutativity and associativity @@ -1159,7 +1210,7 @@ static_assert( is_of_type<(zero_m_per_s + 10 * isq::height[m] / (2 * isq::time[s])) + (10 * isq::height[m] / (2 * isq::time[s])), quantity_point<(isq::height / isq::time)[m / s], zero_m_per_s, int>>); -inline constexpr struct zero_Hz : absolute_point_origin> { +inline constexpr struct zero_Hz : absolute_point_origin> { } zero_Hz; static_assert(((zero_Hz + 10 / (2 * isq::period_duration[s])) + 5 * isq::frequency[Hz]).quantity_from(zero_Hz) == @@ -1209,7 +1260,7 @@ consteval bool invalid_subtraction(Ts... ts) return !requires { (... - ts); }; } -inline constexpr struct zero_Bq : absolute_point_origin> { +inline constexpr struct zero_Bq : absolute_point_origin> { } zero_Bq; static_assert(invalid_addition(zero_Bq + 5 * isq::activity[Bq], 5 * isq::frequency[Hz])); @@ -1222,23 +1273,4 @@ static_assert(invalid_addition(5 * isq::activity[Bq], zero_Hz + 10 / (2 * isq::t static_assert(invalid_addition(5 * isq::activity[Bq], 10 / (2 * isq::time[s]), zero_Hz + 5 * isq::frequency[Hz])); static_assert(invalid_subtraction(zero_Bq + 5 * isq::activity[Bq], 10 / (2 * isq::time[s]), 5 * isq::frequency[Hz])); - -///////////////////////// -// relative point origin -///////////////////////// - -template -struct absolute_po_ : absolute_point_origin {}; -template -inline constexpr absolute_po_ absolute_po; - -template -struct relative_po_ : relative_point_origin {}; -template -inline constexpr relative_po_ relative_po; - -static_assert(relative_po + isq::height(42 * m)>.quantity_spec == isq::height); -static_assert(relative_po> + isq::height(42 * m)>.quantity_spec == isq::height); -static_assert(relative_po + 42 * m>.quantity_spec == isq::height); - } // namespace