diff --git a/example/currency.cpp b/example/currency.cpp index fb1ff9fa63..d0cb63c88d 100644 --- a/example/currency.cpp +++ b/example/currency.cpp @@ -33,8 +33,6 @@ inline constexpr struct dim_currency : base_dimension<"$"> {} dim_currency; QUANTITY_SPEC(currency, dim_currency); -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; inline constexpr struct great_british_pound : named_unit<"GBP", kind_of> {} great_british_pound; @@ -90,18 +88,19 @@ quantity exchange_to(quantity q) template auto To, ReferenceOf auto From, auto PO, typename Rep> quantity_point exchange_to(quantity_point q) { - return quantity_point{zero + static_cast(exchange_rate() * - (q - q.absolute_point_origin).numerical_value_in(q.unit)) * - To}; + return quantity_point{ + static_cast(exchange_rate() * (q - q.absolute_point_origin).numerical_value_in(q.unit)) * + To}; } int main() { using namespace unit_symbols; - quantity_point price_usd = zero + 100 * USD; + quantity_point price_usd{100 * USD}; quantity_point price_euro = exchange_to(price_usd); - std::cout << price_usd.quantity_from(zero) << " -> " << price_euro.quantity_from(zero) << "\n"; - // std::cout << price_usd.quantity_from(zero) + price_euro.quantity_from(zero) << "\n"; // does not compile + std::cout << price_usd.quantity_from_zero() << " -> " << price_euro.quantity_from_zero() << "\n"; + // std::cout << price_usd.quantity_from_zero() + price_euro.quantity_from_zero() << "\n"; // does + // not compile } diff --git a/example/include/geographic.h b/example/include/geographic.h index d683089630..0ccda628d6 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,10 @@ 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 f880ba567a..228e22f04b 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 7d2fa7355e..2ae6c4e30a 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 = diff --git a/src/core/include/mp-units/quantity_point.h b/src/core/include/mp-units/quantity_point.h index a69a29ac1e..21d5bda973 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,45 @@ struct relative_point_origin { static constexpr PointOrigin auto absolute_point_origin = QP.absolute_point_origin; }; -namespace detail { +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 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 same_absolute_point_origins(po1, po2) && + detail::is_eq_zero(PO2::quantity_point.quantity_from(PO2::quantity_point.absolute_point_origin)); +} + +template +struct implicit_zeroth_point_origin_ : absolute_point_origin, QS> {}; + +template +inline constexpr implicit_zeroth_point_origin_ implicit_zeroth_point_origin; -[[nodiscard]] consteval PointOrigin auto get_absolute_point_origin(PointOrigin auto po) +template +[[nodiscard]] consteval PointOriginFor auto zeroth_point_origin(R) { - if constexpr (requires { po.quantity_point.absolute_point_origin; }) - return po.quantity_point.absolute_point_origin; + if constexpr (requires { get_unit(R{}).point_origin; }) + return get_unit(R{}).point_origin; else + return implicit_zeroth_point_origin; +} + +namespace detail { + +template +[[nodiscard]] consteval PointOrigin auto get_absolute_point_origin(PO po) +{ + if constexpr (AbsolutePointOrigin) return po; + else + return po.quantity_point.absolute_point_origin; } } // namespace detail @@ -68,7 +115,7 @@ namespace detail { * @tparam PO a type that represents the origin point from which the quantity point is measured from * @tparam Rep a type to be used to represent values of a quantity point */ -template auto PO, +template auto PO = zeroth_point_origin(R), RepresentationOf Rep = double> class quantity_point { public: @@ -103,7 +150,14 @@ class quantity_point { quantity_point(quantity_point&&) = default; template - requires std::same_as, quantity_type> + requires QuantityOf, get_quantity_spec(R)> && std::constructible_from && + (point_origin == zeroth_point_origin(R)) && (point_origin == zeroth_point_origin(Q::reference)) + constexpr explicit quantity_point(Q&& q) : quantity_from_origin_is_an_implementation_detail_(std::forward(q)) + { + } + + template + requires QuantityOf, get_quantity_spec(R)> && std::constructible_from constexpr quantity_point(Q&& q, std::remove_const_t) : quantity_from_origin_is_an_implementation_detail_(std::forward(q)) { @@ -186,6 +240,20 @@ class quantity_point { return *this - PO2{}; } + // returns always a value relative to the unit's zero + // available only if point is defined in terms of a unit's zero point origin + [[nodiscard]] constexpr Quantity auto quantity_from_zero() const + requires(detail::same_absolute_point_origins(absolute_point_origin, zeroth_point_origin(R))) + { + // original quantity point unit can be lost in the below operation + const auto q = quantity_from(zeroth_point_origin(R)); + if constexpr (requires { q.in(unit); }) + // restore it if possible (non-truncating) + return q.in(unit); + else + return q; + } + // unit conversions template U> requires detail::QuantityConvertibleTo> @@ -291,6 +359,9 @@ class quantity_point { }; // CTAD +template +quantity_point(Q q) -> quantity_point; + template PO> quantity_point(Q q, PO) -> quantity_point; diff --git a/src/core/include/mp-units/unit.h b/src/core/include/mp-units/unit.h index 0fb778333d..90ecc0ac93 100644 --- a/src/core/include/mp-units/unit.h +++ b/src/core/include/mp-units/unit.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -116,6 +117,14 @@ struct named_unit { static constexpr auto quantity_spec = QS; }; +template + requires(!Symbol.empty()) && detail::BaseDimension> +struct named_unit { + static constexpr auto symbol = Symbol; ///< Unique base unit identifier + static constexpr auto quantity_spec = QS; + static constexpr auto point_origin = PO; +}; + /** * @brief Specialization for a unit that can be reused by several base quantities * @@ -132,6 +141,13 @@ struct named_unit { static constexpr auto symbol = Symbol; ///< Unique base unit identifier }; +template + requires(!Symbol.empty()) +struct named_unit { + static constexpr auto symbol = Symbol; ///< Unique base unit identifier + static constexpr auto point_origin = PO; +}; + /** * @brief Specialization for a unit with special name * @@ -146,6 +162,13 @@ struct named_unit : std::remove_const_t { static constexpr auto symbol = Symbol; ///< Unique unit identifier }; +template + requires(!Symbol.empty()) +struct named_unit : std::remove_const_t { + static constexpr auto symbol = Symbol; ///< Unique unit identifier + static constexpr auto point_origin = PO; +}; + /** * @brief Specialization for a unit with special name valid only for a specific quantity * @@ -162,6 +185,14 @@ struct named_unit : std::remove_const_t { static constexpr auto quantity_spec = QS; }; +template + requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension) +struct named_unit : std::remove_const_t { + static constexpr auto symbol = Symbol; ///< Unique unit identifier + static constexpr auto quantity_spec = QS; + static constexpr auto point_origin = PO; +}; + /** * @brief A prefixed unit * @@ -286,14 +317,14 @@ canonical_unit(M, U) -> canonical_unit; #endif -template -[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit&); +template +[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit&); -template -[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit&); +template +[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit&); -template -[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit&); +template +[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit&); template [[nodiscard]] consteval auto get_canonical_unit_impl(T, const power&); @@ -308,20 +339,20 @@ template return canonical_unit{M * base.mag, base.reference_unit}; } -template -[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit&) +template +[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit&) { return canonical_unit{mag<1>, t}; } -template -[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit&) +template +[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit&) { return canonical_unit{mag<1>, t}; } -template -[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit&) +template +[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit&) { return get_canonical_unit_impl(U, U); } 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 deleted file mode 100644 index d395478e5a..0000000000 --- a/src/systems/si/include/mp-units/systems/si/point_origins.h +++ /dev/null @@ -1,35 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include -#include - -namespace mp_units::si { - -// clang-format off -inline constexpr struct absolute_zero : absolute_point_origin {} absolute_zero; -inline constexpr struct ice_point : relative_point_origin {} ice_point; -// clang-format on - -} // namespace mp_units::si diff --git a/src/systems/si/include/mp-units/systems/si/si.h b/src/systems/si/include/mp-units/systems/si/si.h index 4fbf1e469d..9efad6f32c 100644 --- a/src/systems/si/include/mp-units/systems/si/si.h +++ b/src/systems/si/include/mp-units/systems/si/si.h @@ -23,7 +23,6 @@ #pragma once #include -#include #include #include #include diff --git a/src/systems/si/include/mp-units/systems/si/units.h b/src/systems/si/include/mp-units/systems/si/units.h index c9f517f039..8936df8180 100644 --- a/src/systems/si/include/mp-units/systems/si/units.h +++ b/src/systems/si/include/mp-units/systems/si/units.h @@ -22,6 +22,7 @@ #pragma once +#include #include #include #include @@ -39,7 +40,11 @@ inline constexpr struct metre : named_unit<"m", kind_of> {} metre; inline constexpr struct gram : named_unit<"g", kind_of> {} gram; inline constexpr struct kilogram : decltype(kilo) {} kilogram; inline constexpr struct ampere : named_unit<"A", kind_of> {} ampere; -inline constexpr struct kelvin : named_unit<"K", kind_of> {} kelvin; + +inline constexpr struct absolute_zero : absolute_point_origin {} absolute_zero; +inline constexpr struct zeroth_kelvin : decltype(absolute_zero) {} zeroth_kelvin; +inline constexpr struct kelvin : named_unit<"K", kind_of, zeroth_kelvin> {} kelvin; + inline constexpr struct mole : named_unit<"mol", kind_of> {} mole; inline constexpr struct candela : named_unit<"cd", kind_of> {} candela; @@ -68,7 +73,11 @@ inline constexpr struct siemens : named_unit<"S", one / ohm> {} siemens; inline constexpr struct weber : named_unit<"Wb", volt * second> {} weber; inline constexpr struct tesla : named_unit<"T", weber / square(metre)> {} tesla; inline constexpr struct henry : named_unit<"H", weber / ampere> {} henry; -inline constexpr struct degree_Celsius : named_unit {} degree_Celsius; + +inline constexpr struct ice_point : relative_point_origin {} ice_point; +inline constexpr struct zeroth_degree_Celsius : decltype(ice_point) {} zeroth_degree_Celsius; +inline constexpr struct degree_Celsius : named_unit {} degree_Celsius; + inline constexpr struct lumen : named_unit<"lm", candela * steradian> {} lumen; inline constexpr struct lux : named_unit<"lx", lumen / square(metre)> {} lux; inline constexpr struct becquerel : named_unit<"Bq", one / second, kind_of> {} becquerel; 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 bdfc0365a2..b126c98379 100644 --- a/src/systems/usc/include/mp-units/systems/usc/usc.h +++ b/src/systems/usc/include/mp-units/systems/usc/usc.h @@ -110,9 +110,9 @@ inline constexpr struct troy_pound : named_unit<"lb t", mag<12> * troy_once> {} inline constexpr struct inch_of_mercury : named_unit<"inHg", mag * si::pascal> {} inch_of_mercury; // https://en.wikipedia.org/wiki/United_States_customary_units#Temperature -inline constexpr struct degree_Fahrenheit : named_unit * si::degree_Celsius> {} degree_Fahrenheit; +inline constexpr struct zeroth_degree_Fahrenheit : relative_point_origin * si::degree_Celsius)> {} zeroth_degree_Fahrenheit; +inline constexpr struct degree_Fahrenheit : named_unit * si::degree_Celsius, zeroth_degree_Fahrenheit> {} degree_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 d71372e3b9..5784dabb22 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 978e135601..4312e638aa 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 54094970ab..1f09068af3 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -36,26 +36,61 @@ 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 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); + +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 ///////////////////// @@ -202,7 +237,7 @@ static_assert(quantity_point::dimension == is static_assert(quantity_point::unit == si::degree_Celsius); static_assert(is_of_type::point_origin, struct si::ice_point>); static_assert( - is_of_type::absolute_point_origin, struct si::absolute_zero>); + is_of_type::absolute_point_origin, struct si::zeroth_kelvin>); static_assert(quantity_point::reference == isq::Celsius_temperature[si::degree_Celsius]); @@ -215,7 +250,7 @@ static_assert(is_of_type); static_assert( is_of_type::absolute_point_origin, - struct si::absolute_zero>); + struct si::zeroth_kelvin>); ////////////////// @@ -1115,7 +1150,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 +1194,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 +1244,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])); @@ -1223,22 +1258,4 @@ static_assert(invalid_addition(5 * isq::activity[Bq], 10 / (2 * isq::time[s]), z 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 +} \ No newline at end of file