From 10a2e6dc0d2b59fa3be98f4e7d044593514008e0 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 21 Dec 2023 12:25:09 +0100 Subject: [PATCH 01/19] 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>); ////////////////// From 5f840d707908b7532000b3a2106cfe8b8ff42fdb Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 21 Dec 2023 12:25:09 +0100 Subject: [PATCH 02/19] fix: point origins comparison fixed --- src/core/include/mp-units/quantity_point.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/include/mp-units/quantity_point.h b/src/core/include/mp-units/quantity_point.h index b7600caf..44ef0443 100644 --- a/src/core/include/mp-units/quantity_point.h +++ b/src/core/include/mp-units/quantity_point.h @@ -71,10 +71,10 @@ template 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) && + 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 same_absolute_point_origins(po1, po2) && + return detail::same_absolute_point_origins(po1, po2) && detail::is_eq_zero(PO2::quantity_point.quantity_from(PO2::quantity_point.absolute_point_origin)); } From fe099b5ad1109b86633716a396eca5fc9d5f31d0 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 21 Dec 2023 12:25:09 +0100 Subject: [PATCH 03/19] feat: propagation of point origins for scaled units added --- src/core/include/mp-units/unit.h | 14 +++++++++++++- test/unit_test/static/quantity_point_test.cpp | 10 ++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/core/include/mp-units/unit.h b/src/core/include/mp-units/unit.h index 90ecc0ac..f03257b4 100644 --- a/src/core/include/mp-units/unit.h +++ b/src/core/include/mp-units/unit.h @@ -40,6 +40,18 @@ namespace mp_units { +namespace detail { + +template +struct propagate_point_origin {}; + +template +struct propagate_point_origin { + static constexpr auto point_origin = U::point_origin; +}; + +} // namespace detail + /** * @brief Unit being a scaled version of another unit * @@ -50,7 +62,7 @@ namespace mp_units { * instantiate this type automatically based on the unit arithmetic equation provided by the user. */ template -struct scaled_unit { +struct scaled_unit : detail::propagate_point_origin { static constexpr MP_UNITS_CONSTRAINED_AUTO_WORKAROUND(Magnitude) auto mag = M; static constexpr U reference_unit{}; }; diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index 2e4dfc85..ac0e6b9a 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -106,6 +106,16 @@ static_assert(relative_po + isq::height(42 * m)>.quanti static_assert(relative_po> + isq::height(42 * m)>.quantity_spec == isq::height); static_assert(relative_po + 42 * m>.quantity_spec == isq::height); +inline constexpr struct my_kelvin : named_unit<"my_K", mag<10> * si::kelvin> { +} my_kelvin; + +static_assert(zeroth_point_origin(si::kelvin) == si::absolute_zero); +static_assert(zeroth_point_origin(si::milli) == si::absolute_zero); +static_assert(zeroth_point_origin(mag<10> * si::kelvin) == si::absolute_zero); +static_assert(zeroth_point_origin(my_kelvin) == si::absolute_zero); + +static_assert(zeroth_point_origin(si::degree_Celsius) == si::ice_point); +static_assert(zeroth_point_origin(mag<10> * si::degree_Celsius) == si::ice_point); ///////////////////// // class invariants From cccf1e24b005fd7b3571df37ef8d768e593d7c55 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 21 Dec 2023 12:25:09 +0100 Subject: [PATCH 04/19] refactor: point origin can't be set for a unit that does not have an associated quantity kind --- src/core/include/mp-units/unit.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/core/include/mp-units/unit.h b/src/core/include/mp-units/unit.h index f03257b4..79b2512a 100644 --- a/src/core/include/mp-units/unit.h +++ b/src/core/include/mp-units/unit.h @@ -153,13 +153,6 @@ 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 * From 641163a4c3bd48683358daa273ea0e9f42a548b1 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 21 Dec 2023 12:25:09 +0100 Subject: [PATCH 05/19] test: verify that the implicit point origin is properly set for the rest of the units and derived ones --- test/unit_test/static/quantity_point_test.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index ac0e6b9a..48b379a5 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -117,6 +117,12 @@ static_assert(zeroth_point_origin(my_kelvin) == si::absolute_zero); static_assert(zeroth_point_origin(si::degree_Celsius) == si::ice_point); static_assert(zeroth_point_origin(mag<10> * si::degree_Celsius) == si::ice_point); +static_assert(zeroth_point_origin(si::metre) == implicit_zeroth_point_origin>); +static_assert(zeroth_point_origin(si::kelvin / si::second) == + implicit_zeroth_point_origin>); +static_assert(zeroth_point_origin(si::degree_Celsius / si::second) == + implicit_zeroth_point_origin>); + ///////////////////// // class invariants ///////////////////// From 6a3f9a5ece8b3455eb59ec7a6b1ae75f702a5c2f Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 21 Dec 2023 12:25:09 +0100 Subject: [PATCH 06/19] docs: "The Affine Space" chapter updated to reflect the recent design changes --- docs/getting_started/faq.md | 8 +- .../framework_basics/the_affine_space.md | 475 +++++++++++++----- 2 files changed, 357 insertions(+), 126 deletions(-) diff --git a/docs/getting_started/faq.md b/docs/getting_started/faq.md index f7fc0f54..25e4cb6d 100644 --- a/docs/getting_started/faq.md +++ b/docs/getting_started/faq.md @@ -96,10 +96,10 @@ to form a quantity. !!! note - The same applies to the `quantity_point` construction. To prevent similar issues during - construction, it always needs to get both a `quantity` and - a [`PointOrigin`](../users_guide/framework_basics/concepts.md#PointOrigin) that we use - as a reference point. + The same applies to the construction of `quantity_point` using an explicit point origin. + To prevent similar safety issues during maintenance, the initialization always requires + providing both a `quantity` and a [`PointOrigin`](../users_guide/framework_basics/concepts.md#PointOrigin) + that we use as a reference point. ## Why a dimensionless quantity is not just a fundamental arithmetic type? diff --git a/docs/users_guide/framework_basics/the_affine_space.md b/docs/users_guide/framework_basics/the_affine_space.md index 10fc026f..08f75de3 100644 --- a/docs/users_guide/framework_basics/the_affine_space.md +++ b/docs/users_guide/framework_basics/the_affine_space.md @@ -68,145 +68,245 @@ As we already know, a `quantity` type provides all operations required for a _ve the affine space. -## _Point_ is modeled by `PointOrigin` and `quantity_point` +## _Point_ is modeled by `quantity_point` and `PointOrigin` In the **mp-units** library the _point_ abstraction is modelled by: - [`PointOrigin` concept](concepts.md#PointOrigin) that specifies measurement origin, - `quantity_point` class template that specifies a _point_ relative to a specific predefined origin. -### Absolute _point_ origin - -The **absolute point origin** specifies where the "zero" of our measurement's scale is. User can -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; -``` - -!!! 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: +The `quantity_point` class template specifies an absolute quantity measured from a predefined +origin: ```cpp template auto PO, + PointOriginFor auto PO = zeroth_point_origin(R), RepresentationOf Rep = double> class quantity_point; ``` As we can see above, the `quantity_point` class template exposes one additional parameter compared to `quantity`. The `PO` parameter satisfies a [`PointOriginFor` concept](concepts.md#PointOriginFor) -and specifies the origin of our measurement scale. +and specifies the origin of our measurement scale. By default, it is initialized with a quantity's +zeroth point using the following rules: + +- if the measurement unit of a quantity specifies its point origin in its definition + (e.g., degree Celsius), then this point is being used, +- otherwise, an instantiation of `implicit_zeroth_point_origin` is being used which + provides a zeroth point for a specific quantity type. !!! tip - `quantity_point` definition can be found in the `mp-units/quantity_point.h` header file. + The `quantity_point` definition can be found in the `mp-units/quantity_point.h` header file. -As a _point_ can be represented with a _vector_ from the origin, a `quantity_point` class -template can be created with the following operations: + +### Implicit point origin + +Let's assume that Alice goes for a trip driving a car. She likes taking notes about interesting +places that she visits on the road. For every such item, she writes down: + +- its name, +- a readout from the car's odometer at the location, +- a current timestamp. + +We can implement this in the following way: ```cpp -quantity_point qp1 = mean_sea_level + 42 * m; -quantity_point qp2 = 42 * m + mean_sea_level; -quantity_point qp3 = mean_sea_level - 42 * m; +using std::chrono::system_clock; + +struct trip_log_item { + std::string name; + quantity_point odometer; + quantity_point timestamp; +}; +using trip_log = std::vector; ``` +```cpp +trip_log log; + +quantity_point timestamp_1{quantity{system_clock::now().time_since_epoch()}}; +log.emplace_back("home", quantity_point{1356 * km}, timestamp_1); + +// some time passes + +quantity_point timestamp_2{quantity{system_clock::now().time_since_epoch()}}; +log.emplace_back("castle", quantity_point{1401 * km}, timestamp_2); +``` + +This is an excellent example of where points are helpful. There is no doubt about the correctness +of their usage in this scenario: + +- adding two odometer readouts or two timestamps have no physical sense, and that is why we will + expect a compile-time error when we try to perform such operations accidentally, +- subtracting two odometer readouts or timestamps is perfectly valid and results in a quantity + storing the interval value between the two points. + +Having such a database, we can print the trip log in the following way: + +```cpp +for (const auto& item : log) { + std::cout << "POI: " << item.name << "\n"; + std::cout << "- Distance from home: " << item.odometer - log.front().odometer; + std::cout << "- Trip duration from start: " << (item.timestamp - log.front().timestamp).in(non_si::minute); +} +``` + +Moreover, if Alice had reset the car's trip odometer before leaving home, we could have rewritten +one of the previous lines like that: + +```cpp +std::cout << "Distance from home: " << item.odometer.quantity_from_zero(); +``` + +The above always returns a quantity measured from the "ultimate" zeroth point of a scale used for +this specific quantity type. + +!!! tip + + Storing _points_ is the most efficient representation we can choose in this scenario: + + - to store a value, we read it directly from the instrument, and no additional transformation + is needed, + - to print the absolute value (e.g., odometer), we have the value available right away, + - to get any relative quantity (e.g., distance from the start, distance from the previous point, + etc.), we have to perform a single subtraction operation. + + If we stored _vectors_ in our database instead, we would have to pay at runtime for additional + operations: + + - to store a quantity, we would have to perform the subtraction right away to get the interval + between the current value and some reference point, + - to print the absolute value, we would have to add the quantity to the reference point that + we need to store somewhere in the database as well, + - to get a relative quantity, only the currently stored one is immediate; all other values + will require at least one quantity addition operation. + +Now, let's assume that Bob, a friend of Alice, also keeps a log of his trips but he, of +course, measures distances from his own home with the odometer in his car. Everything is fine as +long as we deal with one trip at a time, but if we start to work with both at once, we may +accidentally subtract points from different trips. The library will not prevent +us from doing so. + +The points from Alice's and Bob's trips should be considered separate, and to enforce it at +compilation time, we need to introduce explicit origins. + + +### Absolute _point_ origin + +The **absolute point origin** specifies the "zero" of our measurement's scale. User can +specify such an origin by deriving from the `absolute_point_origin` class template: + +```cpp +enum class actor { alice, bob }; + +template +struct zeroth_odometer_t : absolute_point_origin, isq::distance> {}; + +template +inline constexpr zeroth_odometer_t zeroth_odometer; +``` + +!!! info + + The `absolute_point_origin` class template uses the 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 + +!!! note + + Unfortunately, due to inconsistencies in C++ language rules: + + - we can't define the above in one line of code, + - provide the same identifier for a class and variable template. + +Odometer is not the only one that can get an explicit point origin in our case. As timestamps are +provided by the `std::chrono::system_clock`, their values are always relative to the epoch of this +clock. + +!!! note + + The **mp-units** library provides means to specify + [interoperability with other units libraries](../use_cases/interoperability_with_other_units_libraries.md). + It also has built-in compatibility with `std::chrono` types, so users do not have to define + interoperability traits for such types by themselves. Those are provided in the + `mp-units/chrono.h` header file. + + +Now, we can refactor our database to benefit from the explicit points: + +```cpp +template +struct trip_log_item { + std::string point_name; + quantity_point, zeroth_odometer> odometer; + quantity_point> timestamp; +}; + +template +using trip_log = std::vector>; +``` + +We also need to update the initialization part in our code. In the case of implicit zeroth origins, +we could construct `quantity_point` directly from the value of a `quantity`. This is no longer +the case. +As a _point_ can be represented with a _vector_ from the origin, to improve the safety of the code +we write, a `quantity_point` class template must be created with one of the following operations: + +```cpp +quantity_point qp1 = zeroth_odometer + 1356 * km; +quantity_point qp2 = 1356 * km + zeroth_odometer; +quantity_point qp3 = zeroth_odometer - 1356 * km; +``` + +Although, the `qp3` above does not have a physical sense in this specific scenario. + !!! note [It is not allowed to subtract a _point_ from a _vector_](#operations-in-the-affine-space) - thus `42 * m - mean_sea_level` is an invalid operation. + thus `1356 * km - zeroth_odometer` is an invalid operation. + +!!! info + + A rationale for this longer construction syntax can be found in the + [Why can't I create a quantity by passing a number to a constructor?](../../getting_started/faq.md#why-cant-i-create-a-quantity-by-passing-a-number-to-a-constructor) + chapter. Similarly to [creation of a quantity](../../getting_started/quick_start.md#creating-a-quantity), if someone does not like the operator-based syntax to create a `quantity_point`, the same results -can be achieved with two-parameter constructor: +can be achieved with a two-parameter constructor: ```cpp -quantity_point qp4{42 * m, mean_sea_level}; -quantity_point qp5{-42 * m, mean_sea_level}; +quantity_point qp4{1356 * km, zeroth_odometer}; ``` -The provided `quantity` representing an offset from the origin is stored inside the `quantity_point` -class template and can be obtained with a `quantity_from(PointOrigin)` member function: +Also, as now our timestamps have a proper point origin provided in a type, we can simplify the +previous code by directly converting `std::chrono::time_point` to our `quantity_point` type. + +With all the above, we can refactor our initialization part to the following: ```cpp -constexpr quantity_point everest_base_camp_alt = mean_sea_level + isq::altitude(5364 * m); -static_assert(everest_base_camp_alt.quantity_from(mean_sea_level) == 5364 * m); +trip_log alice_log; + +alice_log.emplace_back("home", zeroth_odometer + 1356 * km, system_clock::now()); + +// some time passes + +alice_log.emplace_back("castle", zeroth_odometer + 1401 * km, system_clock::now()); ``` -### Relative _point_ origin - -We often do not have only one ultimate "zero" point when we measure things. - -Continuing the Mount Everest trip example above, measuring all daily hikes from the `mean_sea_level` -might not be efficient. Maybe we know that we are not good climbers, so all our climbs can be -represented with an 8-bit integer type allowing us to save memory in our database of climbs? -Why not use `everest_base_camp_alt` as our reference point? - -For this purpose, we can define a `relative_point_origin` in the following way: - -```cpp -constexpr struct everest_base_camp : relative_point_origin {} everest_base_camp; -``` - -The above can be used as an origin for subsequent _points_: - -```cpp -constexpr quantity_point first_climb_alt = everest_base_camp + isq::altitude(std::uint8_t{42} * m); -static_assert(first_climb_alt.quantity_from(everest_base_camp) == 42 * m); -static_assert(first_climb_alt.quantity_from(mean_sea_level) == 5406 * m); -``` - -As we can see above, the `quantity_from()` member function returns a relative distance from the -provided point origin. - - -### Converting between different representations of the same _point_ - -As we might represent the same _point_ with _vectors_ from various origins, the **mp-units** library -provides facilities to convert the _point_ to the `quantity_point` class templates expressed in terms -of different origins. - -For this purpose, we can either use: - -- a converting constructor: - - ```cpp - constexpr quantity_point qp = first_climb_alt; - static_assert(qp.quantity_ref_from(qp.point_origin) == 5406 * m); - ``` - -- a dedicated conversion interface: - - ```cpp - constexpr quantity_point qp = first_climb_alt.point_for(mean_sea_level); - static_assert(qp.quantity_ref_from(qp.point_origin) == 5406 * m); - ``` - -!!! note - - It is only allowed to convert between various origins defined in terms of the same - `absolute_point_origin`. Even if it is possible to express the same _point_ as a _vector_ - from another `absolute_point_origin`, the library will not provide such a conversion. - A custom user-defined conversion function will be needed to add this functionality. - - Said otherwise, in the **mp-units** library, there is no way to spell how two distinct - `absolute_point_origin` types relate to each other. - - ### _Point_ arithmetics -Let's assume we will attend the CppCon conference hosted in Aurora, CO, and we want to estimate -the distance we will travel. We have to take a taxi to a local airport, fly to DEN airport with -a stopover in FRA, and, in the end, get a cab to the Gaylord Rockies Resort & Convention Center: +As another example, let's assume we will attend the CppCon conference hosted in Aurora, CO, +and we want to estimate the distance we will travel. We have to take a taxi to a local airport, +fly to DEN airport with 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; @@ -255,70 +355,201 @@ Taxi distance: 31.2544 km !!! note It is not allowed to subtract two point origins defined in terms of `absolute_point_origin` - (e.g. `mean_sea_level - mean_sea_level`) as those do not contain information about the unit - so we are not able to determine a resulting `quantity` type. + (e.g., `home - home`) as those do not contain information about the unit, so we are not able + to determine a resulting `quantity` type. + + +### Relative _point_ origin + +We often do not have only one ultimate "zero" point when we measure things. + +For example, let's assume that we have the following absolute point origin: + +```cpp +constexpr struct mean_sea_level : absolute_point_origin {} mean_sea_level; +``` + +If we want to model a trip to Mount Everest, measuring all daily hikes from the `mean_sea_level` +might not be efficient. We may know that we are not good climbers, so all our climbs can be +represented with an 8-bit integer type, allowing us to save memory in our database of climbs. + +For this purpose, we can define a `relative_point_origin` in the following way: + +```cpp +constexpr struct everest_base_camp : relative_point_origin {} everest_base_camp; +``` + +The above can be used as an origin for subsequent _points_: + +```cpp +constexpr quantity_point first_climb_alt = everest_base_camp + isq::altitude(std::uint8_t{42} * m); +static_assert(first_climb_alt.quantity_from(everest_base_camp) == 42 * m); +static_assert(first_climb_alt.quantity_from(mean_sea_level) == 5406 * m); +``` + +As we can see above, the `quantity_from()` member function returns a relative distance from the +provided point origin. + + +### Converting between different representations of the same _point_ + +As we might represent the same _point_ with _vectors_ from various origins, the **mp-units** library +provides facilities to convert the _point_ to the `quantity_point` class templates expressed in +terms of different origins. + +For this purpose, we can use: + +- a converting constructor: + + ```cpp + constexpr quantity_point qp = first_climb_alt; + static_assert(qp.quantity_ref_from(qp.point_origin) == 5406 * m); + ``` + +- a dedicated conversion interface: + + ```cpp + constexpr quantity_point qp = first_climb_alt.point_for(mean_sea_level); + static_assert(qp.quantity_ref_from(qp.point_origin) == 5406 * m); + ``` + +!!! note + + It is only allowed to convert between various origins defined in terms of the same + `absolute_point_origin`. Even if it is theoretically possible to express the same _point_ as + a _vector_ from another `absolute_point_origin`, the library will not allow such a conversion. + A custom user-defined conversion function will be needed to add this functionality. + + Said otherwise, in the **mp-units** library, there is no way to spell how two distinct + `absolute_point_origin` types relate to each other. ### Temperature support -Another important example of [relative point origins](#relative-point-origins) is support -of temperature quantity points in units different than kelvin [`K`]. - -The [SI](../../appendix/glossary.md#si) system definition in the **mp-units** library provides -two predefined point origins: +Another important example of [relative point origins](#relative-point-origins) is the support +of temperature quantity points. The **mp-units** library provides a few predefined point origins +for this purpose: ```cpp -namespace mp_units::si { +namespace si { inline constexpr struct absolute_zero : absolute_point_origin {} absolute_zero; -inline constexpr struct ice_point : relative_point_origin {} ice_point; +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; + +} + +namespace usc { + +inline constexpr struct zeroth_degree_Fahrenheit : + relative_point_origin * si::degree_Celsius)> {} zeroth_degree_Fahrenheit; } ``` -With the above, we can be explicit what is the origin of our temperature point. For example, if -we want to implement the degree Celsius scale we can do it as follows: +The above is a great example of how point origins can be stacked on top of each other: -```cpp -using Celsius_point = quantity_point; -``` +- `usc::zeroth_degree_Fahrenheit` is defined relative to `si::zeroth_degree_Celsius` +- `si::zeroth_degree_Celsius` is defined relative to `si::zeroth_kelvin`. !!! note Notice that while stacking point origins, we can use not only different representation types - but also different units for an origin and a _point_. In the above example, the relative - point origin is defined in terms of `si::kelvin`, while the quantity point uses - `si::degree_Celsius`. + but also different units for origins and a _point_. In the above example, the relative + point origin for degree Celsius is defined in terms of `si::kelvin`, while the quantity point + for it will use `si::degree_Celsius` as a unit. -To play a bit with temperatures we can implement a simple room's AC temperature controller in +The temperature point origins defined above are provided explicitly in the respective units' +definitions: + +```cpp +namespace si { + +inline constexpr struct kelvin : + named_unit<"K", kind_of, zeroth_kelvin> {} kelvin; +inline constexpr struct degree_Celsius : + named_unit {} degree_Celsius; + +} + +namespace usc { + +inline constexpr struct degree_Fahrenheit : + named_unit * si::degree_Celsius, + zeroth_degree_Fahrenheit> {} degree_Fahrenheit; + +} +``` + +Now let's see how we can benefit from the above definitions. We have quite a few alternatives to +choose from here. Depending on our needs or taste we can: + +- be explicit about the unit and origin: + + ```cpp + quantity_point q1 = si::zeroth_degree_Celsius + 20.5 * deg_C; + quantity_point q2 = {20.5 * deg_C, si::zeroth_degree_Celsius}; + quantity_point q3{20.5 * deg_C}; + ``` + +- specify a unit and use its zeroth point origin implicitly: + + ```cpp + quantity_point q4 = si::zeroth_degree_Celsius + 20.5 * deg_C; + quantity_point q5 = {20.5 * deg_C, si::zeroth_degree_Celsius}; + quantity_point q6{20.5 * deg_C}; + ``` + +- benefit from CTAD: + + ```cpp + quantity_point q7 = si::zeroth_degree_Celsius + 20.5 * deg_C; + quantity_point q8 = {20.5 * deg_C, si::zeroth_degree_Celsius}; + quantity_point q9{20.5 * deg_C}; + ``` + +In all of the above cases, we end up with the `quantity_point` of the same type and value. + +To play a bit more with temperatures, we can implement a simple room AC temperature controller in the following way: ```cpp -constexpr struct room_reference_temp : relative_point_origin {} room_reference_temp; +constexpr struct room_reference_temp : relative_point_origin {} room_reference_temp; using room_temp = quantity_point; constexpr auto step_delta = isq::Celsius_temperature(0.5 * deg_C); constexpr int number_of_steps = 6; -room_temp room_low = room_reference_temp - number_of_steps * step_delta; -room_temp room_high = room_reference_temp + number_of_steps * step_delta; +room_temp room_ref{}; +room_temp room_low = room_ref - number_of_steps * step_delta; +room_temp room_high = room_ref + number_of_steps * step_delta; -std::println("| {:<14} | {:^18} | {:^18} | {:^18} |", "Temperature", "Room reference", "Ice point", "Absolute zero"); +std::println("Room reference temperature: {} ({}, {})\n", + room_ref.quantity_from_zero(), + room_ref.in(usc::degree_Fahrenheit).quantity_from_zero(), + room_ref.in(si::kelvin).quantity_from_zero()); + +std::println("| {:<14} | {:^18} | {:^18} | {:^18} |", + "Temperature", "Room reference", "Ice point", "Absolute zero"); std::println("|{0:=^16}|{0:=^20}|{0:=^20}|{0:=^20}|", ""); -auto print = [&](std::string_view label, auto v){ - std::println("| {:<14} | {:^18} | {:^18} | {:^18} |", - label, v - room_reference_temp, v - si::ice_point, v - si::absolute_zero); +auto print = [&](std::string_view label, auto v) { + fmt::println("| {:<14} | {:^18} | {:^18} | {:^18} |", label, + v - room_reference_temp, v - si::ice_point, v - si::absolute_zero); }; print("Lowest", room_low); -print("Default", room_reference_temp); +print("Default", room_ref); print("Highest", room_high); ``` The above prints: ```text +Room reference temperature: 21 °C (69.8 °F, 294.15 K) + | Temperature | Room reference | Ice point | Absolute zero | |================|====================|====================|====================| | Lowest | -3 °C | 18 °C | 291.15 °C | From c28a65ffa5ebe1d17bfaa33c2f6e831bde1a8261 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 21 Dec 2023 12:25:09 +0100 Subject: [PATCH 07/19] Update docs/users_guide/framework_basics/the_affine_space.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Johel Ernesto Guerrero Peña --- docs/users_guide/framework_basics/the_affine_space.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/users_guide/framework_basics/the_affine_space.md b/docs/users_guide/framework_basics/the_affine_space.md index 08f75de3..7830c8a6 100644 --- a/docs/users_guide/framework_basics/the_affine_space.md +++ b/docs/users_guide/framework_basics/the_affine_space.md @@ -394,8 +394,8 @@ provided point origin. ### Converting between different representations of the same _point_ As we might represent the same _point_ with _vectors_ from various origins, the **mp-units** library -provides facilities to convert the _point_ to the `quantity_point` class templates expressed in -terms of different origins. +provides facilities to convert the _point_ to `quantity_point` class templates expressed in +terms of origins relative to each other in the type system. For this purpose, we can use: From 6f18ec3142d207f13ee813e53896da6272e07050 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 21 Dec 2023 12:25:10 +0100 Subject: [PATCH 08/19] style: whitespaces trimmed to make pre-commit happy --- docs/users_guide/framework_basics/the_affine_space.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/users_guide/framework_basics/the_affine_space.md b/docs/users_guide/framework_basics/the_affine_space.md index 7830c8a6..8281f8af 100644 --- a/docs/users_guide/framework_basics/the_affine_space.md +++ b/docs/users_guide/framework_basics/the_affine_space.md @@ -168,7 +168,7 @@ this specific quantity type. !!! tip Storing _points_ is the most efficient representation we can choose in this scenario: - + - to store a value, we read it directly from the instrument, and no additional transformation is needed, - to print the absolute value (e.g., odometer), we have the value available right away, From fdacde606cf45e886aba8940f7ffdc320071dd73 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 21 Dec 2023 12:25:10 +0100 Subject: [PATCH 09/19] docs: implicit point origin related erroneous case added to example in the interoperability chapter --- .../use_cases/interoperability_with_other_libraries.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/users_guide/use_cases/interoperability_with_other_libraries.md b/docs/users_guide/use_cases/interoperability_with_other_libraries.md index ec3fbed1..682743e2 100644 --- a/docs/users_guide/use_cases/interoperability_with_other_libraries.md +++ b/docs/users_guide/use_cases/interoperability_with_other_libraries.md @@ -271,9 +271,13 @@ The **mp-units** library comes with built-in interoperability with those types. quantity_point qp4 = my_origin + 1 * s; auto tp4 = to_chrono_time_point(qp4); // Compile-time Error (1) + + quantity_point qp5{1 * s}; + auto tp5 = to_chrono_time_point(qp5); // Compile-time Error (2) ``` 1. `my_origin` is not defined in terms of `chrono_point_origin`. + 2. `implicit_zeroth_point_origin` is not defined in terms of `chrono_point_origin`. Here is an example of how interoperability described in this chapter can be used in practice: From ea7c97dac71b671fdb6ab22969b5d3cb11d5bd5b Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 21 Dec 2023 12:31:24 +0100 Subject: [PATCH 10/19] docs: "Quick Start" chapter updated with implicit origins --- docs/getting_started/quick_start.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/getting_started/quick_start.md b/docs/getting_started/quick_start.md index c220b968..62457e87 100644 --- a/docs/getting_started/quick_start.md +++ b/docs/getting_started/quick_start.md @@ -86,6 +86,8 @@ static_assert(140 * km / (2 * h) == 70 * km / h); ## Quantity points The quantity point specifies an absolute quantity with respect to an origin. +If no origin is provided explicitly, an implicit one will be provided by the library. + Together with quantities, they model [The Affine Space](../users_guide/framework_basics/the_affine_space.md). Quantity points should be used in all places where adding two values is meaningless @@ -106,10 +108,10 @@ int main() using namespace mp_units::si::unit_symbols; using namespace mp_units::usc::unit_symbols; - quantity_point temp = si::zeroth_degree_Celsius + 20. * deg_C; + quantity_point temp{20. * deg_C}; std::cout << "Temperature: " - << temp.quantity_from(si::zeroth_degree_Celsius) << " (" - << temp.in(deg_F).quantity_from(usc::zeroth_degree_Fahrenheit) << ")\n"; + << temp.quantity_from_zero() << " (" + << temp.in(deg_F).quantity_from_zero() << ")\n"; } ``` From b33013ca435e2a7b9ec117bae5eb35df619b51f8 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 21 Dec 2023 12:36:11 +0100 Subject: [PATCH 11/19] style: whitespaces trimmed to make pre-commit happy --- docs/getting_started/quick_start.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting_started/quick_start.md b/docs/getting_started/quick_start.md index 62457e87..3cb39189 100644 --- a/docs/getting_started/quick_start.md +++ b/docs/getting_started/quick_start.md @@ -110,7 +110,7 @@ int main() quantity_point temp{20. * deg_C}; std::cout << "Temperature: " - << temp.quantity_from_zero() << " (" + << temp.quantity_from_zero() << " (" << temp.in(deg_F).quantity_from_zero() << ")\n"; } ``` From 31a5c7667294916123b2497c8573726c40aaab5d Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 22 Dec 2023 15:51:03 +0100 Subject: [PATCH 12/19] feat: `interconvertible(QuantitySpec, QuantitySpec)` added --- src/core/include/mp-units/quantity_spec.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/include/mp-units/quantity_spec.h b/src/core/include/mp-units/quantity_spec.h index 5f70d034..c9b242d4 100644 --- a/src/core/include/mp-units/quantity_spec.h +++ b/src/core/include/mp-units/quantity_spec.h @@ -1401,6 +1401,12 @@ template return detail::convertible_impl(from, to) >= detail::specs_convertible_result::cast; } +template +[[nodiscard]] consteval bool interconvertible(QS1 qs1, QS2 qs2) +{ + return implicitly_convertible(qs1, qs2) && implicitly_convertible(qs2, qs1); +} + namespace detail { template From a962182e13339f7183b7ff6134cc6285dcf9670b Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 22 Dec 2023 16:35:06 +0100 Subject: [PATCH 13/19] feat: implicit point origins compatibility between different quantity specs added --- src/core/include/mp-units/quantity_point.h | 68 +++- test/unit_test/static/quantity_point_test.cpp | 367 +++++++++++++++++- 2 files changed, 410 insertions(+), 25 deletions(-) diff --git a/src/core/include/mp-units/quantity_point.h b/src/core/include/mp-units/quantity_point.h index 44ef0443..4f8c1bcc 100644 --- a/src/core/include/mp-units/quantity_point.h +++ b/src/core/include/mp-units/quantity_point.h @@ -63,27 +63,45 @@ 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)); -} - template struct implicit_zeroth_point_origin_ : absolute_point_origin, QS> {}; template inline constexpr implicit_zeroth_point_origin_ implicit_zeroth_point_origin; +namespace detail { + +template +inline constexpr bool is_specialization_of_implicit_zeroth_point_origin = false; + +template +inline constexpr bool is_specialization_of_implicit_zeroth_point_origin> = true; + +template +[[nodiscard]] consteval bool is_implicit_zeroth_point_origin(PO) +{ + return is_specialization_of_implicit_zeroth_point_origin; +} + +} // namespace detail + +template +[[nodiscard]] consteval bool operator==(PO1 po1, PO2 po2) +{ + if constexpr (detail::AbsolutePointOrigin && detail::AbsolutePointOrigin) + return is_same_v || + (detail::is_implicit_zeroth_point_origin(po1) && detail::is_implicit_zeroth_point_origin(po2) && + interconvertible(po1.quantity_spec, po2.quantity_spec)); + 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_zero()); + else if constexpr (detail::RelativePointOrigin) + return detail::same_absolute_point_origins(po1, po2) && + detail::is_eq_zero(PO2::quantity_point.quantity_from_zero()); +} + template [[nodiscard]] consteval PointOriginFor auto zeroth_point_origin(R) { @@ -151,7 +169,7 @@ public: template requires QuantityOf, get_quantity_spec(R)> && std::constructible_from && - (point_origin == zeroth_point_origin(R)) && (point_origin == zeroth_point_origin(Q::reference)) + (point_origin == zeroth_point_origin(R)) && (implicitly_convertible(Q::quantity_spec, quantity_spec)) constexpr explicit quantity_point(Q&& q) : quantity_from_origin_is_an_implementation_detail_(std::forward(q)) { } @@ -241,11 +259,13 @@ 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))) { + if constexpr (requires { unit.point_origin; }) { + // original quantity point unit can be lost in the below operation + const auto q = quantity_from(unit.point_origin); + if constexpr (requires { q.in(unit); }) + // restore the unit if possible (non-truncating) // 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); }) @@ -378,7 +398,10 @@ template const quantity& q) requires requires { qp.quantity_ref_from(PO1) + q; } { - return quantity_point{qp.quantity_ref_from(PO1) + q, PO1}; + if constexpr (detail::is_implicit_zeroth_point_origin(PO1)) + return quantity_point{qp.quantity_ref_from(PO1) + q}; + else + return quantity_point{qp.quantity_ref_from(PO1) + q, PO1}; } template @@ -412,7 +435,10 @@ template const quantity& q) requires requires { qp.quantity_ref_from(PO1) - q; } { - return quantity_point{qp.quantity_ref_from(PO1) - q, PO1}; + if constexpr (detail::is_implicit_zeroth_point_origin(PO1)) + return quantity_point{qp.quantity_ref_from(PO1) - q}; + else + return quantity_point{qp.quantity_ref_from(PO1) - q, PO1}; } template diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index 48b379a5..082ff3f2 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -33,9 +34,13 @@ namespace { using namespace mp_units; using namespace mp_units::si::unit_symbols; +using namespace mp_units::usc::unit_symbols; using namespace std::chrono_literals; using sys_seconds = std::chrono::time_point; +inline constexpr struct zeroth_length : absolute_point_origin { +} zeroth_length; + inline constexpr struct mean_sea_level : absolute_point_origin { } mean_sea_level; @@ -88,6 +93,7 @@ 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 != zeroth_length); static_assert(mean_sea_level != other_absolute_level); static_assert(my_mean_sea_level != other_absolute_level); static_assert(ground_level != other_ground_level); @@ -123,6 +129,17 @@ static_assert(zeroth_point_origin(si::kelvin / si::second) == static_assert(zeroth_point_origin(si::degree_Celsius / si::second) == implicit_zeroth_point_origin>); +static_assert(implicit_zeroth_point_origin == implicit_zeroth_point_origin); +static_assert(implicit_zeroth_point_origin == + implicit_zeroth_point_origin); +static_assert(implicit_zeroth_point_origin> == implicit_zeroth_point_origin); +static_assert(implicit_zeroth_point_origin> == + implicit_zeroth_point_origin); + +static_assert(implicit_zeroth_point_origin != implicit_zeroth_point_origin); +static_assert(implicit_zeroth_point_origin != implicit_zeroth_point_origin); +static_assert(implicit_zeroth_point_origin != implicit_zeroth_point_origin); + ///////////////////// // class invariants ///////////////////// @@ -142,6 +159,8 @@ concept invalid_types = requires { requires !requires { typename QP; }; requires !requires { typename QP; }; requires !requires { typename QP; }; + requires !requires { typename QP, int>; }; + requires !requires { typename QP, int>; }; // quantity used as Rep requires !requires { typename QP>; }; // quantity point used as Rep @@ -167,6 +186,9 @@ concept valid_types = requires { typename QP; typename QP; typename QP; + typename QP, int>; + typename QP>, int>; + typename QP, int>; }; static_assert(valid_types); @@ -201,6 +223,22 @@ static_assert(std::three_way_comparable::reference == si::metre); +static_assert(quantity_point::quantity_spec == kind_of); +static_assert(quantity_point::dimension == isq::dim_length); +static_assert(quantity_point::unit == si::metre); +static_assert(is_of_type::point_origin, implicit_zeroth_point_origin_>>); +static_assert( + is_of_type::absolute_point_origin, implicit_zeroth_point_origin_>>); + +static_assert(quantity_point::reference == isq::height[m]); +static_assert(quantity_point::quantity_spec == isq::height); +static_assert(quantity_point::dimension == isq::dim_length); +static_assert(quantity_point::unit == si::metre); +static_assert(is_of_type::point_origin, implicit_zeroth_point_origin_>); +static_assert( + is_of_type::absolute_point_origin, implicit_zeroth_point_origin_>); + static_assert(quantity_point::reference == si::metre); static_assert(quantity_point::quantity_spec == kind_of); static_assert(quantity_point::dimension == isq::dim_length); @@ -348,6 +386,80 @@ static_assert(!std::convertible_to, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +// different dimensions +static_assert(!std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +// convertible but different quantity_specs +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +// quantity_specs with common_quantity_spec +static_assert(!std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(!std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +// non-convertible quantity_specs +static_assert(!std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(!std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +// quantity-like +static_assert(!std::constructible_from, std::chrono::seconds>); +static_assert(!std::convertible_to>); + +static_assert(!std::constructible_from, std::chrono::seconds>); +static_assert(!std::convertible_to>); + +static_assert(!std::constructible_from, std::chrono::seconds>); +static_assert(!std::convertible_to>); + + +// ---------------------- +// explicit point origins +// ---------------------- + static_assert(!std::constructible_from, quantity>); static_assert(!std::convertible_to, quantity_point>); @@ -366,6 +478,13 @@ static_assert(!std::convertible_to, quantity_point, quantity>); static_assert(!std::convertible_to, quantity_point>); +// quantity_specs with common_quantity_spec +static_assert(!std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(!std::constructible_from, quantity>); +static_assert(!std::convertible_to, quantity_point>); + // different dimensions static_assert(!std::constructible_from, quantity>); static_assert(!std::convertible_to, quantity_point>); @@ -404,7 +523,60 @@ static_assert( // construction from a quantity point /////////////////////////////////////// -// same origins +// implicit origin +static_assert(std::constructible_from, quantity_point>); +static_assert(std::convertible_to, quantity_point>); +static_assert(std::constructible_from, quantity_point>); +static_assert(std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity_point>); +static_assert(std::convertible_to, quantity_point>); +static_assert(std::constructible_from, quantity_point>); +static_assert(std::convertible_to, quantity_point>); + +static_assert( + std::constructible_from, quantity_point>); +static_assert(std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity_point>); +static_assert(std::convertible_to, quantity_point>); + +static_assert(std::constructible_from, quantity_point>); +static_assert(std::convertible_to, quantity_point>); + +// convertible but different quantity_specs +static_assert(!std::constructible_from, quantity_point>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(!std::constructible_from, quantity_point>); +static_assert(!std::convertible_to, quantity_point>); + +// quantity_specs with common_quantity_spec +static_assert(!std::constructible_from, quantity_point>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(!std::constructible_from, quantity_point>); +static_assert(!std::convertible_to, quantity_point>); + +// non-convertible quantity_specs +static_assert(!std::constructible_from, quantity_point>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(!std::constructible_from, quantity_point>); +static_assert(!std::convertible_to, quantity_point>); + +// mixed origins +static_assert(!std::constructible_from, quantity_point>); +static_assert(!std::convertible_to, quantity_point>); +static_assert(!std::constructible_from, quantity_point>); +static_assert(!std::convertible_to, quantity_point>); + +static_assert(!std::constructible_from, quantity_point>); +static_assert(!std::convertible_to, quantity_point>); +static_assert(!std::constructible_from, quantity_point>); +static_assert(!std::convertible_to, quantity_point>); + +// same explicit origins static_assert( std::constructible_from, quantity_point>); static_assert( @@ -501,6 +673,22 @@ static_assert( static_assert( std::convertible_to, quantity_point>); +static_assert(std::constructible_from, + quantity_point>); +static_assert( + std::convertible_to, quantity_point>); + +// quantity_specs with common_quantity_spec +static_assert(!std::constructible_from, + quantity_point>); +static_assert( + !std::convertible_to, quantity_point>); + +static_assert(!std::constructible_from, + quantity_point>); +static_assert( + !std::convertible_to, quantity_point>); + // different dimensions static_assert( !std::constructible_from, quantity_point>); @@ -605,6 +793,11 @@ static_assert( // obtaining a relative quantity ////////////////////////////////// +static_assert(quantity_point{42 * m}.quantity_from_zero() == 42 * m); +static_assert(quantity_point{isq::height(42 * m)}.quantity_from_zero() == 42 * m); +static_assert(quantity_point{20 * deg_C}.quantity_from_zero() == 20 * deg_C); +static_assert(quantity_point{20. * deg_C}.in(deg_F).quantity_from_zero() == 68 * deg_F); + static_assert((mean_sea_level + 42 * m).quantity_from(mean_sea_level) == 42 * m); static_assert((mean_sea_level + isq::height(42 * m)).quantity_from(mean_sea_level) == 42 * m); @@ -669,17 +862,43 @@ static_assert(invalid_unit_conversion); // CTAD ///////// +static_assert(std::is_same_v); +static_assert(std::is_same_v, + implicit_zeroth_point_origin_>>); +static_assert(std::is_same_v, + implicit_zeroth_point_origin_>>); +static_assert(quantity_point{123 * m}.unit == si::metre); +static_assert(quantity_point{123 * m}.quantity_spec == kind_of); + +static_assert(std::is_same_v); +static_assert(std::is_same_v, + implicit_zeroth_point_origin_>); +static_assert(std::is_same_v, + implicit_zeroth_point_origin_>); +static_assert(quantity_point{isq::height(123 * m)}.unit == si::metre); +static_assert(quantity_point{isq::height(123 * m)}.quantity_spec == isq::height); + +static_assert(std::is_same_v); +static_assert(std::is_same_v, + struct si::zeroth_degree_Celsius>); +static_assert(std::is_same_v, + struct si::zeroth_kelvin>); +static_assert(quantity_point{20 * deg_C}.unit == si::degree_Celsius); +static_assert(quantity_point{20 * deg_C}.quantity_spec == kind_of); + using namespace std::chrono_literals; static_assert(std::is_same_v); static_assert(std::is_same_v, chrono_point_origin_>); +static_assert(std::is_same_v, + chrono_point_origin_>); static_assert(quantity_point{sys_seconds{24h}}.unit == si::second); static_assert(quantity_point{sys_seconds{24h}}.quantity_spec == kind_of); -// //////////// -// // getters -// //////////// +//////////// +// getters +//////////// constexpr quantity_point mean_sea_level_qp = mean_sea_level + 1 * m; constexpr quantity_point my_mean_sea_level_qp = my_mean_sea_level + 1 * m; @@ -808,12 +1027,39 @@ concept invalid_binary_operations = requires { requires !requires { isq::length(1 * m) + QP(1 * m); }; requires !requires { QP(1 * m) + isq::length(1 * m); }; requires !requires { isq::length(1 * m) + QP(1 * m); }; + requires !requires { QP, int>(1 * m) + isq::length(1 * m); }; + requires !requires { isq::length(1 * m) + QP, int>(1 * m); }; + requires !requires { + QP, int>(1 * m) + isq::length(1 * m); + }; + requires !requires { + isq::length(1 * m) + QP, int>(1 * m); + }; requires !requires { Origin + isq::length(1 * m); }; requires !requires { isq::length(1 * m) + Origin; }; // can't subtract more generic quantity (violates point_origin quantity_spec) requires !requires { QP(1 * m) - isq::length(1 * m); }; requires !requires { QP(1 * m) - isq::length(1 * m); }; + requires !requires { QP, int>(1 * m) - isq::length(1 * m); }; + requires !requires { + QP, int>(1 * m) - isq::length(1 * m); + }; + requires !requires { + QP, int>(10 * isq::height[m] / + (2 * isq::time[s])) + + 5 * isq::speed[m / s]; + }; + requires !requires { + 5 * isq::speed[m / s] + + QP, int>( + 10 * isq::height[m] / (2 * isq::time[s])); + }; + requires !requires { + QP, int>(10 * isq::height[m] / + (2 * isq::time[s])) - + 5 * isq::speed[m / s]; + }; requires !requires { Origin - isq::length(1 * m); }; // quantity point can't be subtracted from a quantity @@ -835,6 +1081,28 @@ concept invalid_binary_operations = requires { }; requires !requires { mean_sea_level - QP(1 * m); }; requires !requires { QP(1 * m) - other_absolute_level; }; + requires !requires { QP(1 * m) - quantity_point{1 * m}; }; + requires !requires { quantity_point{1 * m} - QP(1 * m); }; + requires !requires { + QP, int>(1 * m) - quantity_point{isq::height(1 * m)}; + }; + requires !requires { + quantity_point{isq::height(1 * m)} - QP, int>(1 * m); + }; + requires !requires { + QP, int>(1 * m) - quantity_point{isq::length(1 * m)}; + }; + requires !requires { + quantity_point{isq::length(1 * m)} - QP, int>(1 * m); + }; + requires !requires { + quantity_point{10 * isq::height[m] / (2 * isq::time[s])} - + QP, int>(5 * isq::speed[m / s]); + }; + requires !requires { + QP, int>(5 * isq::speed[m / s]) - + quantity_point{10 * isq::height[m] / (2 * isq::time[s])}; + }; // cant'subtract two unrelated points requires !requires { Origin - OtherOrigin; }; @@ -1260,6 +1528,51 @@ 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>>); +static_assert((quantity_point{5 * isq::speed[m / s]} + 10 * isq::length[m] / (2 * isq::time[s])).quantity_from_zero() == + 10 * isq::speed[m / s]); +static_assert((10 * isq::length[m] / (2 * isq::time[s]) + quantity_point{5 * isq::speed[m / s]}).quantity_from_zero() == + 10 * isq::speed[m / s]); +static_assert((quantity_point{5 * isq::speed[m / s]} - 10 * isq::length[m] / (2 * isq::time[s])).quantity_from_zero() == + 0 * isq::speed[m / s]); + +static_assert((quantity_point{10 * isq::length[m] / (2 * isq::time[s])} + 5 * isq::speed[m / s]).quantity_from_zero() == + 10 * isq::speed[m / s]); +static_assert((5 * isq::speed[m / s] + quantity_point{10 * isq::length[m] / (2 * isq::time[s])}).quantity_from_zero() == + 10 * isq::speed[m / s]); +static_assert((quantity_point{10 * isq::length[m] / (2 * isq::time[s])} - 5 * isq::speed[m / s]).quantity_from_zero() == + 0 * isq::speed[m / s]); + +static_assert((quantity_point{5 * isq::speed[m / s]} + 10 * isq::height[m] / (2 * isq::time[s])).quantity_from_zero() == + 10 * isq::speed[m / s]); +static_assert((10 * isq::height[m] / (2 * isq::time[s]) + quantity_point{5 * isq::speed[m / s]}).quantity_from_zero() == + 10 * isq::speed[m / s]); +static_assert((quantity_point{5 * isq::speed[m / s]} - 10 * isq::height[m] / (2 * isq::time[s])).quantity_from_zero() == + 0 * isq::speed[m / s]); + +static_assert(is_of_type, int>>); +static_assert(is_of_type<10 * isq::height[m] / (2 * isq::time[s]) + quantity_point{5 * isq::speed[m / s]}, + quantity_point, int>>); +static_assert(is_of_type, int>>); +static_assert(is_of_type<5 * isq::speed[m / s] + quantity_point{10 * isq::length[m] / (2 * isq::time[s])}, + quantity_point, int>>); +static_assert(is_of_type, int>>); +static_assert(is_of_type, int>>); +static_assert( + is_of_type>); +static_assert( + is_of_type>); + +static_assert( + is_of_type< + quantity_point{10 * isq::height[m] / (2 * isq::time[s])} + (10 * isq::height[m] / (2 * isq::time[s])), + quantity_point<(isq::height / isq::time)[m / s], implicit_zeroth_point_origin, int>>); + inline constexpr struct zero_Hz : absolute_point_origin> { } zero_Hz; @@ -1297,6 +1610,40 @@ static_assert(is_of_type<(zero_Hz + 10 / (2 * isq::period_duration[s])) - (zero_ static_assert(is_of_type<(zero_Hz + 5 * isq::frequency[Hz]) - (zero_Hz + 10 / (2 * isq::period_duration[s])), quantity>); +static_assert((quantity_point{10 / (2 * isq::period_duration[s])} + 5 * isq::frequency[Hz]).quantity_from_zero() == + 10 * isq::frequency[Hz]); +static_assert((10 / (2 * isq::period_duration[s]) + quantity_point{zero_Hz + 5 * isq::frequency[Hz]}) + .quantity_from_zero() == 10 * isq::frequency[Hz]); +static_assert((quantity_point{5 * isq::frequency[Hz]} + 10 / (2 * isq::period_duration[s])).quantity_from_zero() == + 10 * isq::frequency[Hz]); +static_assert((5 * isq::frequency[Hz] + quantity_point{10 / (2 * isq::period_duration[s])}).quantity_from_zero() == + 10 * isq::frequency[Hz]); +static_assert((quantity_point{10 / (2 * isq::period_duration[s])} - 5 * isq::frequency[Hz]).quantity_from_zero() == + 0 * isq::frequency[Hz]); +static_assert((quantity_point{5 * isq::frequency[Hz]} - 10 / (2 * isq::period_duration[s])).quantity_from_zero() == + 0 * isq::frequency[Hz]); +static_assert(quantity_point{10 / (2 * isq::period_duration[s])} - quantity_point{5 * isq::frequency[Hz]} == + 0 * isq::frequency[Hz]); +static_assert(quantity_point{5 * isq::frequency[Hz]} - quantity_point{10 / (2 * isq::period_duration[s])} == + 0 * isq::frequency[Hz]); + +static_assert(is_of_type, int>>); +static_assert(is_of_type<10 / (2 * isq::period_duration[s]) + quantity_point{5 * isq::frequency[Hz]}, + quantity_point, int>>); +static_assert(is_of_type, int>>); +static_assert(is_of_type<5 * isq::frequency[Hz] + quantity_point{10 / (2 * isq::period_duration[s])}, + quantity_point, int>>); +static_assert(is_of_type, int>>); +static_assert(is_of_type, int>>); +static_assert(is_of_type>); +static_assert(is_of_type>); + // Different named dimensions template consteval bool invalid_addition(Ts... ts) @@ -1323,4 +1670,16 @@ 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])); +static_assert(invalid_addition(quantity_point{5 * isq::activity[Bq]}, 5 * isq::frequency[Hz])); +static_assert(invalid_addition(5 * isq::activity[Bq], quantity_point{5 * isq::frequency[Hz]})); +static_assert(invalid_subtraction(quantity_point{5 * isq::activity[Bq]}, 5 * isq::frequency[Hz])); +static_assert(invalid_subtraction(quantity_point{5 * isq::activity[Bq]}, quantity_point{5 * isq::frequency[Hz]})); + +static_assert(invalid_addition(quantity_point{5 * isq::activity[Bq]}, 10 / (2 * isq::time[s]), 5 * isq::frequency[Hz])); +static_assert(invalid_addition(5 * isq::activity[Bq], quantity_point{10 / (2 * isq::time[s])}, 5 * isq::frequency[Hz])); +static_assert(invalid_addition(5 * isq::activity[Bq], 10 / (2 * isq::time[s]), quantity_point{5 * isq::frequency[Hz]})); +static_assert(invalid_subtraction(quantity_point{5 * isq::activity[Bq]}, 10 / (2 * isq::time[s]), + 5 * isq::frequency[Hz])); + + } // namespace From f2abaa28bc4c8b55cee3e63d9ae987ea4fe3b442 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 22 Dec 2023 16:41:14 +0100 Subject: [PATCH 14/19] feat: `quantity_from_zero()` enabled for absolute point origins --- example/include/geographic.h | 8 ++++---- example/unmanned_aerial_vehicle.cpp | 4 ++-- src/core/include/mp-units/quantity_point.h | 12 +++++------- test/unit_test/static/quantity_point_test.cpp | 16 ++++++++++------ 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/example/include/geographic.h b/example/include/geographic.h index 0ccda628..003db203 100644 --- a/example/include/geographic.h +++ b/example/include/geographic.h @@ -178,10 +178,10 @@ distance spherical_distance(position from, position to) using isq::sin, isq::cos, isq::asin, isq::acos; - const quantity from_lat = from.lat.quantity_from(equator); - const quantity from_lon = from.lon.quantity_from(prime_meridian); - const quantity to_lat = to.lat.quantity_from(equator); - const quantity to_lon = to.lon.quantity_from(prime_meridian); + const quantity from_lat = from.lat.quantity_from_zero(); + const quantity from_lon = from.lon.quantity_from_zero(); + const quantity to_lat = to.lat.quantity_from_zero(); + const quantity to_lon = to.lon.quantity_from_zero(); // https://en.wikipedia.org/wiki/Great-circle_distance#Formulae if constexpr (sizeof(T) >= 8) { diff --git a/example/unmanned_aerial_vehicle.cpp b/example/unmanned_aerial_vehicle.cpp index 228e22f0..fe465f68 100644 --- a/example/unmanned_aerial_vehicle.cpp +++ b/example/unmanned_aerial_vehicle.cpp @@ -97,8 +97,8 @@ template hae_altitude to_hae(msl_altitude msl, position pos) { const auto geoid_undulation = - isq::height(GeographicLibWhatsMyOffset(pos.lat.quantity_from(equator).numerical_value_in(si::degree), - pos.lon.quantity_from(prime_meridian).numerical_value_in(si::degree)) * + isq::height(GeographicLibWhatsMyOffset(pos.lat.quantity_from_zero().numerical_value_in(si::degree), + pos.lon.quantity_from_zero().numerical_value_in(si::degree)) * si::metre); return height_above_ellipsoid + (msl - mean_sea_level - geoid_undulation); } diff --git a/src/core/include/mp-units/quantity_point.h b/src/core/include/mp-units/quantity_point.h index 4f8c1bcc..d633d1f4 100644 --- a/src/core/include/mp-units/quantity_point.h +++ b/src/core/include/mp-units/quantity_point.h @@ -266,13 +266,11 @@ public: const auto q = quantity_from(unit.point_origin); if constexpr (requires { q.in(unit); }) // restore the unit if possible (non-truncating) - // 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; + return q.in(unit); + else + return q; + } else + return quantity_from(absolute_point_origin); } // unit conversions diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index 082ff3f2..bb3da04e 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -798,6 +798,10 @@ static_assert(quantity_point{isq::height(42 * m)}.quantity_from_zero() == 42 * m static_assert(quantity_point{20 * deg_C}.quantity_from_zero() == 20 * deg_C); static_assert(quantity_point{20. * deg_C}.in(deg_F).quantity_from_zero() == 68 * deg_F); +static_assert((mean_sea_level + 42 * m).quantity_from_zero() == 42 * m); +static_assert((ground_level + 42 * m).quantity_from_zero() == 84 * m); +static_assert((tower_peak + 42 * m).quantity_from_zero() == 126 * m); + static_assert((mean_sea_level + 42 * m).quantity_from(mean_sea_level) == 42 * m); static_assert((mean_sea_level + isq::height(42 * m)).quantity_from(mean_sea_level) == 42 * m); @@ -979,14 +983,14 @@ static_assert([](auto v) { //////////////////////// // same type -static_assert((mean_sea_level + 1 * m += 1 * m).quantity_from(mean_sea_level).numerical_value_in(m) == 2); -static_assert((mean_sea_level + 2 * m -= 1 * m).quantity_from(mean_sea_level).numerical_value_in(m) == 1); +static_assert((mean_sea_level + 1 * m += 1 * m).quantity_from_zero().numerical_value_in(m) == 2); +static_assert((mean_sea_level + 2 * m -= 1 * m).quantity_from_zero().numerical_value_in(m) == 1); // different types -static_assert((mean_sea_level + 2.5 * m += 3 * m).quantity_from(mean_sea_level).numerical_value_in(m) == 5.5); -static_assert((mean_sea_level + 123 * m += 1 * km).quantity_from(mean_sea_level).numerical_value_in(m) == 1123); -static_assert((mean_sea_level + 5.5 * m -= 3 * m).quantity_from(mean_sea_level).numerical_value_in(m) == 2.5); -static_assert((mean_sea_level + 1123 * m -= 1 * km).quantity_from(mean_sea_level).numerical_value_in(m) == 123); +static_assert((mean_sea_level + 2.5 * m += 3 * m).quantity_from_zero().numerical_value_in(m) == 5.5); +static_assert((mean_sea_level + 123 * m += 1 * km).quantity_from_zero().numerical_value_in(m) == 1123); +static_assert((mean_sea_level + 5.5 * m -= 3 * m).quantity_from_zero().numerical_value_in(m) == 2.5); +static_assert((mean_sea_level + 1123 * m -= 1 * km).quantity_from_zero().numerical_value_in(m) == 123); template typename QP> From bf51bfc96faecad909d1da5bc917ec0b4d693538 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 22 Dec 2023 16:42:03 +0100 Subject: [PATCH 15/19] refactor(example): `quantity_from_zero()` used in `exchange_to` --- example/currency.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/example/currency.cpp b/example/currency.cpp index d0cb63c8..28af832e 100644 --- a/example/currency.cpp +++ b/example/currency.cpp @@ -89,8 +89,7 @@ template auto To, ReferenceOf auto From, auto PO quantity_point exchange_to(quantity_point q) { return quantity_point{ - static_cast(exchange_rate() * (q - q.absolute_point_origin).numerical_value_in(q.unit)) * - To}; + static_cast(exchange_rate() * q.quantity_from_zero().numerical_value_in(q.unit)) * To}; } int main() From be1705bc4a94ddd9b0eba5f7ce495b74256e62c3 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 22 Dec 2023 17:29:02 +0100 Subject: [PATCH 16/19] refactor: `zeroth_point_origin()` renamed to `default_point_origin()` and `implicit_zeroth_point_origin` renamed to `zeroth_point_origin` --- .../framework_basics/the_affine_space.md | 8 +- .../interoperability_with_other_libraries.md | 2 +- src/core/include/mp-units/quantity_point.h | 28 ++-- test/unit_test/static/quantity_point_test.cpp | 138 ++++++++---------- 4 files changed, 83 insertions(+), 93 deletions(-) diff --git a/docs/users_guide/framework_basics/the_affine_space.md b/docs/users_guide/framework_basics/the_affine_space.md index 8281f8af..fdbeb787 100644 --- a/docs/users_guide/framework_basics/the_affine_space.md +++ b/docs/users_guide/framework_basics/the_affine_space.md @@ -83,7 +83,7 @@ origin: ```cpp template auto PO = zeroth_point_origin(R), + PointOriginFor auto PO = default_point_origin(R), RepresentationOf Rep = double> class quantity_point; ``` @@ -95,7 +95,7 @@ zeroth point using the following rules: - if the measurement unit of a quantity specifies its point origin in its definition (e.g., degree Celsius), then this point is being used, -- otherwise, an instantiation of `implicit_zeroth_point_origin` is being used which +- otherwise, an instantiation of `zeroth_point_origin` is being used which provides a zeroth point for a specific quantity type. !!! tip @@ -385,10 +385,12 @@ The above can be used as an origin for subsequent _points_: constexpr quantity_point first_climb_alt = everest_base_camp + isq::altitude(std::uint8_t{42} * m); static_assert(first_climb_alt.quantity_from(everest_base_camp) == 42 * m); static_assert(first_climb_alt.quantity_from(mean_sea_level) == 5406 * m); +static_assert(first_climb_alt.quantity_from_zero() == 5406 * m); ``` As we can see above, the `quantity_from()` member function returns a relative distance from the -provided point origin. +provided point origin while the `quantity_from_zero()` returns the distance from the absolute point +origin. ### Converting between different representations of the same _point_ diff --git a/docs/users_guide/use_cases/interoperability_with_other_libraries.md b/docs/users_guide/use_cases/interoperability_with_other_libraries.md index 682743e2..143a205e 100644 --- a/docs/users_guide/use_cases/interoperability_with_other_libraries.md +++ b/docs/users_guide/use_cases/interoperability_with_other_libraries.md @@ -277,7 +277,7 @@ The **mp-units** library comes with built-in interoperability with those types. ``` 1. `my_origin` is not defined in terms of `chrono_point_origin`. - 2. `implicit_zeroth_point_origin` is not defined in terms of `chrono_point_origin`. + 2. `zeroth_point_origin` is not defined in terms of `chrono_point_origin`. Here is an example of how interoperability described in this chapter can be used in practice: diff --git a/src/core/include/mp-units/quantity_point.h b/src/core/include/mp-units/quantity_point.h index d633d1f4..73d1f66c 100644 --- a/src/core/include/mp-units/quantity_point.h +++ b/src/core/include/mp-units/quantity_point.h @@ -64,23 +64,23 @@ struct relative_point_origin { }; template -struct implicit_zeroth_point_origin_ : absolute_point_origin, QS> {}; +struct zeroth_point_origin_ : absolute_point_origin, QS> {}; template -inline constexpr implicit_zeroth_point_origin_ implicit_zeroth_point_origin; +inline constexpr zeroth_point_origin_ zeroth_point_origin; namespace detail { template -inline constexpr bool is_specialization_of_implicit_zeroth_point_origin = false; +inline constexpr bool is_specialization_of_zeroth_point_origin = false; template -inline constexpr bool is_specialization_of_implicit_zeroth_point_origin> = true; +inline constexpr bool is_specialization_of_zeroth_point_origin> = true; template -[[nodiscard]] consteval bool is_implicit_zeroth_point_origin(PO) +[[nodiscard]] consteval bool is_zeroth_point_origin(PO) { - return is_specialization_of_implicit_zeroth_point_origin; + return is_specialization_of_zeroth_point_origin; } } // namespace detail @@ -90,7 +90,7 @@ template { if constexpr (detail::AbsolutePointOrigin && detail::AbsolutePointOrigin) return is_same_v || - (detail::is_implicit_zeroth_point_origin(po1) && detail::is_implicit_zeroth_point_origin(po2) && + (detail::is_zeroth_point_origin(po1) && detail::is_zeroth_point_origin(po2) && interconvertible(po1.quantity_spec, po2.quantity_spec)); else if constexpr (detail::RelativePointOrigin && detail::RelativePointOrigin) return PO1::quantity_point == PO2::quantity_point; @@ -103,12 +103,12 @@ template } template -[[nodiscard]] consteval PointOriginFor auto zeroth_point_origin(R) +[[nodiscard]] consteval PointOriginFor auto default_point_origin(R) { if constexpr (requires { get_unit(R{}).point_origin; }) return get_unit(R{}).point_origin; else - return implicit_zeroth_point_origin; + return zeroth_point_origin; } namespace detail { @@ -133,7 +133,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 = zeroth_point_origin(R), +template auto PO = default_point_origin(R), RepresentationOf Rep = double> class quantity_point { public: @@ -169,7 +169,7 @@ public: template requires QuantityOf, get_quantity_spec(R)> && std::constructible_from && - (point_origin == zeroth_point_origin(R)) && (implicitly_convertible(Q::quantity_spec, quantity_spec)) + (point_origin == default_point_origin(R)) && (implicitly_convertible(Q::quantity_spec, quantity_spec)) constexpr explicit quantity_point(Q&& q) : quantity_from_origin_is_an_implementation_detail_(std::forward(q)) { } @@ -377,7 +377,7 @@ public: // CTAD template -quantity_point(Q q) -> quantity_point; +quantity_point(Q q) -> quantity_point; template PO> quantity_point(Q q, PO) -> quantity_point; @@ -396,7 +396,7 @@ template const quantity& q) requires requires { qp.quantity_ref_from(PO1) + q; } { - if constexpr (detail::is_implicit_zeroth_point_origin(PO1)) + if constexpr (detail::is_zeroth_point_origin(PO1)) return quantity_point{qp.quantity_ref_from(PO1) + q}; else return quantity_point{qp.quantity_ref_from(PO1) + q, PO1}; @@ -433,7 +433,7 @@ template const quantity& q) requires requires { qp.quantity_ref_from(PO1) - q; } { - if constexpr (detail::is_implicit_zeroth_point_origin(PO1)) + if constexpr (detail::is_zeroth_point_origin(PO1)) return quantity_point{qp.quantity_ref_from(PO1) - q}; else return quantity_point{qp.quantity_ref_from(PO1) - q, PO1}; diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index bb3da04e..4ebfb5ad 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -115,30 +115,28 @@ static_assert(relative_po + 42 * m>.quantity_spec == is inline constexpr struct my_kelvin : named_unit<"my_K", mag<10> * si::kelvin> { } my_kelvin; -static_assert(zeroth_point_origin(si::kelvin) == si::absolute_zero); -static_assert(zeroth_point_origin(si::milli) == si::absolute_zero); -static_assert(zeroth_point_origin(mag<10> * si::kelvin) == si::absolute_zero); -static_assert(zeroth_point_origin(my_kelvin) == si::absolute_zero); +static_assert(default_point_origin(si::kelvin) == si::absolute_zero); +static_assert(default_point_origin(si::milli) == si::absolute_zero); +static_assert(default_point_origin(mag<10> * si::kelvin) == si::absolute_zero); +static_assert(default_point_origin(my_kelvin) == si::absolute_zero); -static_assert(zeroth_point_origin(si::degree_Celsius) == si::ice_point); -static_assert(zeroth_point_origin(mag<10> * si::degree_Celsius) == si::ice_point); +static_assert(default_point_origin(si::degree_Celsius) == si::ice_point); +static_assert(default_point_origin(mag<10> * si::degree_Celsius) == si::ice_point); -static_assert(zeroth_point_origin(si::metre) == implicit_zeroth_point_origin>); -static_assert(zeroth_point_origin(si::kelvin / si::second) == - implicit_zeroth_point_origin>); -static_assert(zeroth_point_origin(si::degree_Celsius / si::second) == - implicit_zeroth_point_origin>); +static_assert(default_point_origin(si::metre) == zeroth_point_origin>); +static_assert(default_point_origin(si::kelvin / si::second) == + zeroth_point_origin>); +static_assert(default_point_origin(si::degree_Celsius / si::second) == + zeroth_point_origin>); -static_assert(implicit_zeroth_point_origin == implicit_zeroth_point_origin); -static_assert(implicit_zeroth_point_origin == - implicit_zeroth_point_origin); -static_assert(implicit_zeroth_point_origin> == implicit_zeroth_point_origin); -static_assert(implicit_zeroth_point_origin> == - implicit_zeroth_point_origin); +static_assert(zeroth_point_origin == zeroth_point_origin); +static_assert(zeroth_point_origin == zeroth_point_origin); +static_assert(zeroth_point_origin> == zeroth_point_origin); +static_assert(zeroth_point_origin> == zeroth_point_origin); -static_assert(implicit_zeroth_point_origin != implicit_zeroth_point_origin); -static_assert(implicit_zeroth_point_origin != implicit_zeroth_point_origin); -static_assert(implicit_zeroth_point_origin != implicit_zeroth_point_origin); +static_assert(zeroth_point_origin != zeroth_point_origin); +static_assert(zeroth_point_origin != zeroth_point_origin); +static_assert(zeroth_point_origin != zeroth_point_origin); ///////////////////// // class invariants @@ -159,8 +157,8 @@ concept invalid_types = requires { requires !requires { typename QP; }; requires !requires { typename QP; }; requires !requires { typename QP; }; - requires !requires { typename QP, int>; }; - requires !requires { typename QP, int>; }; + requires !requires { typename QP, int>; }; + requires !requires { typename QP, int>; }; // quantity used as Rep requires !requires { typename QP>; }; // quantity point used as Rep @@ -186,9 +184,9 @@ concept valid_types = requires { typename QP; typename QP; typename QP; - typename QP, int>; - typename QP>, int>; - typename QP, int>; + typename QP, int>; + typename QP>, int>; + typename QP, int>; }; static_assert(valid_types); @@ -227,17 +225,15 @@ static_assert(quantity_point::reference == si::metre); static_assert(quantity_point::quantity_spec == kind_of); static_assert(quantity_point::dimension == isq::dim_length); static_assert(quantity_point::unit == si::metre); -static_assert(is_of_type::point_origin, implicit_zeroth_point_origin_>>); -static_assert( - is_of_type::absolute_point_origin, implicit_zeroth_point_origin_>>); +static_assert(is_of_type::point_origin, zeroth_point_origin_>>); +static_assert(is_of_type::absolute_point_origin, zeroth_point_origin_>>); static_assert(quantity_point::reference == isq::height[m]); static_assert(quantity_point::quantity_spec == isq::height); static_assert(quantity_point::dimension == isq::dim_length); static_assert(quantity_point::unit == si::metre); -static_assert(is_of_type::point_origin, implicit_zeroth_point_origin_>); -static_assert( - is_of_type::absolute_point_origin, implicit_zeroth_point_origin_>); +static_assert(is_of_type::point_origin, zeroth_point_origin_>); +static_assert(is_of_type::absolute_point_origin, zeroth_point_origin_>); static_assert(quantity_point::reference == si::metre); static_assert(quantity_point::quantity_spec == kind_of); @@ -868,17 +864,17 @@ static_assert(invalid_unit_conversion); static_assert(std::is_same_v); static_assert(std::is_same_v, - implicit_zeroth_point_origin_>>); + zeroth_point_origin_>>); static_assert(std::is_same_v, - implicit_zeroth_point_origin_>>); + zeroth_point_origin_>>); static_assert(quantity_point{123 * m}.unit == si::metre); static_assert(quantity_point{123 * m}.quantity_spec == kind_of); static_assert(std::is_same_v); static_assert(std::is_same_v, - implicit_zeroth_point_origin_>); + zeroth_point_origin_>); static_assert(std::is_same_v, - implicit_zeroth_point_origin_>); + zeroth_point_origin_>); static_assert(quantity_point{isq::height(123 * m)}.unit == si::metre); static_assert(quantity_point{isq::height(123 * m)}.quantity_spec == isq::height); @@ -1031,37 +1027,30 @@ concept invalid_binary_operations = requires { requires !requires { isq::length(1 * m) + QP(1 * m); }; requires !requires { QP(1 * m) + isq::length(1 * m); }; requires !requires { isq::length(1 * m) + QP(1 * m); }; - requires !requires { QP, int>(1 * m) + isq::length(1 * m); }; - requires !requires { isq::length(1 * m) + QP, int>(1 * m); }; - requires !requires { - QP, int>(1 * m) + isq::length(1 * m); - }; - requires !requires { - isq::length(1 * m) + QP, int>(1 * m); - }; + requires !requires { QP, int>(1 * m) + isq::length(1 * m); }; + requires !requires { isq::length(1 * m) + QP, int>(1 * m); }; + requires !requires { QP, int>(1 * m) + isq::length(1 * m); }; + requires !requires { isq::length(1 * m) + QP, int>(1 * m); }; requires !requires { Origin + isq::length(1 * m); }; requires !requires { isq::length(1 * m) + Origin; }; // can't subtract more generic quantity (violates point_origin quantity_spec) requires !requires { QP(1 * m) - isq::length(1 * m); }; requires !requires { QP(1 * m) - isq::length(1 * m); }; - requires !requires { QP, int>(1 * m) - isq::length(1 * m); }; + requires !requires { QP, int>(1 * m) - isq::length(1 * m); }; + requires !requires { QP, int>(1 * m) - isq::length(1 * m); }; requires !requires { - QP, int>(1 * m) - isq::length(1 * m); - }; - requires !requires { - QP, int>(10 * isq::height[m] / - (2 * isq::time[s])) + + QP, int>(10 * isq::height[m] / + (2 * isq::time[s])) + 5 * isq::speed[m / s]; }; requires !requires { - 5 * isq::speed[m / s] + - QP, int>( - 10 * isq::height[m] / (2 * isq::time[s])); + 5 * isq::speed[m / s] + QP, int>( + 10 * isq::height[m] / (2 * isq::time[s])); }; requires !requires { - QP, int>(10 * isq::height[m] / - (2 * isq::time[s])) - + QP, int>(10 * isq::height[m] / + (2 * isq::time[s])) - 5 * isq::speed[m / s]; }; requires !requires { Origin - isq::length(1 * m); }; @@ -1088,23 +1077,23 @@ concept invalid_binary_operations = requires { requires !requires { QP(1 * m) - quantity_point{1 * m}; }; requires !requires { quantity_point{1 * m} - QP(1 * m); }; requires !requires { - QP, int>(1 * m) - quantity_point{isq::height(1 * m)}; + QP, int>(1 * m) - quantity_point{isq::height(1 * m)}; }; requires !requires { - quantity_point{isq::height(1 * m)} - QP, int>(1 * m); + quantity_point{isq::height(1 * m)} - QP, int>(1 * m); }; requires !requires { - QP, int>(1 * m) - quantity_point{isq::length(1 * m)}; + QP, int>(1 * m) - quantity_point{isq::length(1 * m)}; }; requires !requires { - quantity_point{isq::length(1 * m)} - QP, int>(1 * m); + quantity_point{isq::length(1 * m)} - QP, int>(1 * m); }; requires !requires { quantity_point{10 * isq::height[m] / (2 * isq::time[s])} - - QP, int>(5 * isq::speed[m / s]); + QP, int>(5 * isq::speed[m / s]); }; requires !requires { - QP, int>(5 * isq::speed[m / s]) - + QP, int>(5 * isq::speed[m / s]) - quantity_point{10 * isq::height[m] / (2 * isq::time[s])}; }; @@ -1554,17 +1543,17 @@ static_assert((quantity_point{5 * isq::speed[m / s]} - 10 * isq::height[m] / (2 0 * isq::speed[m / s]); static_assert(is_of_type, int>>); + quantity_point, int>>); static_assert(is_of_type<10 * isq::height[m] / (2 * isq::time[s]) + quantity_point{5 * isq::speed[m / s]}, - quantity_point, int>>); + quantity_point, int>>); static_assert(is_of_type, int>>); + quantity_point, int>>); static_assert(is_of_type<5 * isq::speed[m / s] + quantity_point{10 * isq::length[m] / (2 * isq::time[s])}, - quantity_point, int>>); + quantity_point, int>>); static_assert(is_of_type, int>>); + quantity_point, int>>); static_assert(is_of_type, int>>); + quantity_point, int>>); static_assert( is_of_type>); @@ -1573,9 +1562,8 @@ static_assert( quantity>); static_assert( - is_of_type< - quantity_point{10 * isq::height[m] / (2 * isq::time[s])} + (10 * isq::height[m] / (2 * isq::time[s])), - quantity_point<(isq::height / isq::time)[m / s], implicit_zeroth_point_origin, int>>); + is_of_type, int>>); inline constexpr struct zero_Hz : absolute_point_origin> { } zero_Hz; @@ -1632,17 +1620,17 @@ static_assert(quantity_point{5 * isq::frequency[Hz]} - quantity_point{10 / (2 * 0 * isq::frequency[Hz]); static_assert(is_of_type, int>>); + quantity_point, int>>); static_assert(is_of_type<10 / (2 * isq::period_duration[s]) + quantity_point{5 * isq::frequency[Hz]}, - quantity_point, int>>); + quantity_point, int>>); static_assert(is_of_type, int>>); + quantity_point, int>>); static_assert(is_of_type<5 * isq::frequency[Hz] + quantity_point{10 / (2 * isq::period_duration[s])}, - quantity_point, int>>); + quantity_point, int>>); static_assert(is_of_type, int>>); + quantity_point, int>>); static_assert(is_of_type, int>>); + quantity_point, int>>); static_assert(is_of_type>); static_assert(is_of_type Date: Fri, 22 Dec 2023 18:23:44 +0100 Subject: [PATCH 17/19] build: gcc-11 support removed because of ICEs --- .github/workflows/ci-conan.yml | 12 ------------ .github/workflows/ci-test-package-cmake.yml | 11 ----------- docs/getting_started/installation_and_usage.md | 4 +--- docs/index.md | 4 +--- 4 files changed, 2 insertions(+), 29 deletions(-) diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml index 8825a9d1..2a41e36f 100644 --- a/.github/workflows/ci-conan.yml +++ b/.github/workflows/ci-conan.yml @@ -54,18 +54,6 @@ jobs: # compiler: { type: MSVC, version: 193, cc: "", cxx: "" }, # conan-config: "", # } - - { - name: "GCC-11", - os: ubuntu-22.04, - compiler: - { - type: GCC, - version: 11, - cc: "gcc-11", - cxx: "g++-11", - }, - conan-config: "", - } - { name: "GCC-12", os: ubuntu-22.04, diff --git a/.github/workflows/ci-test-package-cmake.yml b/.github/workflows/ci-test-package-cmake.yml index a621125a..add6fdf1 100644 --- a/.github/workflows/ci-test-package-cmake.yml +++ b/.github/workflows/ci-test-package-cmake.yml @@ -53,17 +53,6 @@ jobs: # os: windows-2022, # compiler: { type: MSVC, version: 193, cc: "", cxx: "" }, # } - - { - name: "GCC-11", - os: ubuntu-22.04, - compiler: - { - type: GCC, - version: 11, - cc: "gcc-11", - cxx: "g++-11", - }, - } - { name: "GCC-12", os: ubuntu-22.04, diff --git a/docs/getting_started/installation_and_usage.md b/docs/getting_started/installation_and_usage.md index 164a652d..4a4a5fa2 100644 --- a/docs/getting_started/installation_and_usage.md +++ b/docs/getting_started/installation_and_usage.md @@ -14,9 +14,7 @@ As of today, the library compiles fine on the following compilers (or newer): - - gcc-11 - - please note that we observed some ICEs on gcc-11 - - no problems with gcc-12.2+ + - gcc-12 - clang-16 - apple-clang-15 diff --git a/docs/index.md b/docs/index.md index 1f50e5ee..694e9987 100644 --- a/docs/index.md +++ b/docs/index.md @@ -27,9 +27,7 @@ The library source code is hosted on [GitHub](https://github.com/mpusz/mp-units) As of today, the library compiles fine on the following compilers (or newer): - - gcc-11 - - please note that we observed some ICEs on gcc-11 - - no problems with gcc-12.2+ + - gcc-12 - clang-16 - apple-clang-15 From e12fc5e2079c6fd364397c9979fa042fc62d4ecc Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 22 Dec 2023 18:40:44 +0100 Subject: [PATCH 18/19] ci: CodeQL GitHub Action bumped to gcc-12 --- .github/workflows/codeql.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 03408170..9cf16a5f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -37,11 +37,11 @@ jobs: contents: read security-events: write env: - CC: gcc-11 - CXX: g++-11 + CC: gcc-12 + CXX: g++-12 BUILD_TYPE: Debug COMPILER_TYPE: GCC - COMPILER_VERSION: 11 + COMPILER_VERSION: 12 STDLIB: libstdc++11 strategy: From 46bc8e72db4775234a27678d6919f571d13af68b Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 22 Dec 2023 18:42:29 +0100 Subject: [PATCH 19/19] build: gcc-12 set as mininum in conanfile --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 83098f97..1fd11af6 100644 --- a/conanfile.py +++ b/conanfile.py @@ -73,7 +73,7 @@ class MPUnitsConan(ConanFile): @property def _minimum_compilers_version(self): return { - "gcc": "11", + "gcc": "12", "clang": "16", "apple-clang": "15" # , "msvc": "192"