From 10a2e6dc0d2b59fa3be98f4e7d044593514008e0 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 21 Dec 2023 12:25:09 +0100 Subject: [PATCH] feat: :boom: initial implementation of implicit point origins --- example/currency.cpp | 15 +++-- src/core/include/mp-units/quantity_point.h | 47 ++++++++++++++-- src/core/include/mp-units/unit.h | 55 +++++++++++++++---- .../mp-units/systems/si/point_origins.h | 38 ------------- .../si/include/mp-units/systems/si/si.h | 1 - .../si/include/mp-units/systems/si/units.h | 13 ++++- .../usc/include/mp-units/systems/usc/usc.h | 4 +- test/unit_test/static/quantity_point_test.cpp | 4 +- 8 files changed, 108 insertions(+), 69 deletions(-) delete mode 100644 src/systems/si/include/mp-units/systems/si/point_origins.h diff --git a/example/currency.cpp b/example/currency.cpp index b1bcd871..d0cb63c8 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/src/core/include/mp-units/quantity_point.h b/src/core/include/mp-units/quantity_point.h index 840c502d..b7600caf 100644 --- a/src/core/include/mp-units/quantity_point.h +++ b/src/core/include/mp-units/quantity_point.h @@ -71,13 +71,28 @@ template 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) && + 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 detail::same_absolute_point_origins(po1, po2) && + 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; + +template +[[nodiscard]] consteval PointOriginFor auto zeroth_point_origin(R) +{ + if constexpr (requires { get_unit(R{}).point_origin; }) + return get_unit(R{}).point_origin; + else + return implicit_zeroth_point_origin; +} + namespace detail { template @@ -100,7 +115,7 @@ template * @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: @@ -135,7 +150,14 @@ public: 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)) { @@ -219,6 +241,20 @@ public: 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> @@ -322,6 +358,9 @@ public: }; // 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 0fb77833..90ecc0ac 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 784b2d12..00000000 --- a/src/systems/si/include/mp-units/systems/si/point_origins.h +++ /dev/null @@ -1,38 +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 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/si/include/mp-units/systems/si/si.h b/src/systems/si/include/mp-units/systems/si/si.h index 4fbf1e46..9efad6f3 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 c9f517f0..8936df81 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 d5d40edd..b126c983 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 zeroth_degree_Fahrenheit : relative_point_origin {} zeroth_degree_Fahrenheit; // clang-format on namespace unit_symbols { diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index 1bca06a4..2e4dfc85 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -253,7 +253,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]); @@ -266,7 +266,7 @@ static_assert(is_of_type); static_assert( is_of_type::absolute_point_origin, - struct si::absolute_zero>); + struct si::zeroth_kelvin>); //////////////////