From 0670fbdd9f1e82b985ab06a468ea5e6936b55ee1 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 13 Sep 2024 21:38:59 +0200 Subject: [PATCH 001/213] feat: `value_cast()` complementary conversion function added --- .../framework_basics/value_conversions.md | 16 ++++++++-------- .../include/mp-units/framework/value_cast.h | 18 ++++++++++++++++++ test/static/quantity_point_test.cpp | 1 + test/static/quantity_test.cpp | 1 + 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/docs/users_guide/framework_basics/value_conversions.md b/docs/users_guide/framework_basics/value_conversions.md index 1ec7e340a3..96181fd21f 100644 --- a/docs/users_guide/framework_basics/value_conversions.md +++ b/docs/users_guide/framework_basics/value_conversions.md @@ -182,11 +182,11 @@ which may well be outside the range of one or both quantity types. The table below provides all the value conversions functions that may be run on `x` being the instance of either `quantity` or `quantity_point`: -| Forcing | Representation | Unit | Member function | Conversion function | -|:-------:|:--------------:|:----:|--------------------|-----------------------| -| No | Same | `u` | `x.in(u)` | | -| No | `T` | Same | `x.in()` | | -| No | `T` | `u` | `x.in(u)` | | -| Yes | Same | `u` | `x.force_in(u)` | `value_cast(x)` | -| Yes | `T` | Same | `x.force_in()` | `value_cast(x)` | -| Yes | `T` | `u` | `x.force_in(u)` | `value_cast(x)` | +| Forcing | Representation | Unit | Member function | Non-member function | +|:-------:|:--------------:|:----:|--------------------|------------------------------------------------| +| No | Same | `u` | `x.in(u)` | | +| No | `T` | Same | `x.in()` | | +| No | `T` | `u` | `x.in(u)` | | +| Yes | Same | `u` | `x.force_in(u)` | `value_cast(x)` | +| Yes | `T` | Same | `x.force_in()` | `value_cast(x)` | +| Yes | `T` | `u` | `x.force_in(u)` | `value_cast(x)` or `value_cast(x)` | diff --git a/src/core/include/mp-units/framework/value_cast.h b/src/core/include/mp-units/framework/value_cast.h index d5ebf69e9a..5fe6437352 100644 --- a/src/core/include/mp-units/framework/value_cast.h +++ b/src/core/include/mp-units/framework/value_cast.h @@ -88,6 +88,14 @@ template>(std::forward(q)); } +template> + requires(convertible(Q::reference, ToU)) && RepresentationOf && + std::constructible_from +[[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q) +{ + return detail::sudo_cast>(std::forward(q)); +} + /** * @brief Explicit cast of a quantity's representation @@ -168,6 +176,16 @@ template> + requires(convertible(QP::reference, ToU)) && RepresentationOf && + std::constructible_from +[[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp) +{ + return quantity_point{ + value_cast(std::forward(qp).quantity_from_origin_is_an_implementation_detail_), + QP::point_origin}; +} + /** * @brief Explicit cast of a quantity point's representation * diff --git a/test/static/quantity_point_test.cpp b/test/static/quantity_point_test.cpp index f04e9c1ad0..2a86878a5f 100644 --- a/test/static/quantity_point_test.cpp +++ b/test/static/quantity_point_test.cpp @@ -1725,6 +1725,7 @@ constexpr quantity_point lvalue_qp{2 * km}; static_assert(value_cast(lvalue_qp).quantity_from_zero().numerical_value_in(m) == 2000); static_assert(value_cast(lvalue_qp).quantity_from_zero().numerical_value_in(km) == 2.f); static_assert(value_cast(lvalue_qp).quantity_from_zero().numerical_value_in(m) == 2000.f); +static_assert(value_cast(lvalue_qp).quantity_from_zero().numerical_value_in(m) == 2000.f); } // namespace lvalue_tests static_assert(value_cast>(quantity_point{2000 * m}).quantity_from_zero().numerical_value_in(km) == 2); diff --git a/test/static/quantity_test.cpp b/test/static/quantity_test.cpp index 89f169bbf1..6df1602a84 100644 --- a/test/static/quantity_test.cpp +++ b/test/static/quantity_test.cpp @@ -1014,6 +1014,7 @@ static_assert(value_cast(2000.0 * m / (3600.0 * s)).numerical_value_in(k static_assert(value_cast(1.23 * m).numerical_value_in(m) == 1); static_assert(value_cast(1.23 * m).numerical_value_in(km) == 0); +static_assert(value_cast(1.23 * m).numerical_value_in(km) == 0); static_assert((2 * km).force_in(m).numerical_value_in(m) == 2000); static_assert((2000 * m).force_in(km).numerical_value_in(km) == 2); From 2e96871176cd21923f96b7caf4d5ebb8ed5a8d36 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sat, 14 Sep 2024 19:41:43 -0600 Subject: [PATCH 002/213] refactor: `is_power_of_quantity_spec` and `is_power_of_dim` variable templates converted to concepts --- .../include/mp-units/framework/dimension_concepts.h | 10 ++++------ .../mp-units/framework/quantity_spec_concepts.h | 11 +++++------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/core/include/mp-units/framework/dimension_concepts.h b/src/core/include/mp-units/framework/dimension_concepts.h index 5f726c2ba1..1d289867bb 100644 --- a/src/core/include/mp-units/framework/dimension_concepts.h +++ b/src/core/include/mp-units/framework/dimension_concepts.h @@ -68,21 +68,19 @@ template struct is_dimension_one : std::false_type {}; template -constexpr bool is_power_of_dim = requires { - requires is_specialization_of_power && - (BaseDimension || is_dimension_one::value); -}; +concept IsPowerOfDim = + is_specialization_of_power && (BaseDimension || is_dimension_one::value); template constexpr bool is_per_of_dims = false; template constexpr bool is_per_of_dims> = - (... && (BaseDimension || is_dimension_one::value || is_power_of_dim)); + (... && (BaseDimension || is_dimension_one::value || IsPowerOfDim)); template concept DerivedDimensionExpr = - BaseDimension || is_dimension_one::value || is_power_of_dim || is_per_of_dims; + BaseDimension || is_dimension_one::value || IsPowerOfDim || is_per_of_dims; template concept SameDimension = diff --git a/src/core/include/mp-units/framework/quantity_spec_concepts.h b/src/core/include/mp-units/framework/quantity_spec_concepts.h index d780558e4c..d726349495 100644 --- a/src/core/include/mp-units/framework/quantity_spec_concepts.h +++ b/src/core/include/mp-units/framework/quantity_spec_concepts.h @@ -86,21 +86,20 @@ template struct is_dimensionless : std::false_type {}; template -constexpr bool is_power_of_quantity_spec = requires { - requires is_specialization_of_power && - (NamedQuantitySpec || is_dimensionless::value); -}; +concept IsPowerOfQuantitySpec = is_specialization_of_power && (NamedQuantitySpec || + is_dimensionless::value); + template constexpr bool is_per_of_quantity_specs = false; template constexpr bool is_per_of_quantity_specs> = - (... && (NamedQuantitySpec || is_dimensionless::value || is_power_of_quantity_spec)); + (... && (NamedQuantitySpec || is_dimensionless::value || IsPowerOfQuantitySpec)); template concept DerivedQuantitySpecExpr = detail::NamedQuantitySpec || detail::is_dimensionless::value || - detail::is_power_of_quantity_spec || detail::is_per_of_quantity_specs; + detail::IsPowerOfQuantitySpec || detail::is_per_of_quantity_specs; } // namespace detail From bd4a61d518ee518286a73a301a7f6df7714a25b8 Mon Sep 17 00:00:00 2001 From: Yves Delley Date: Sun, 15 Sep 2024 17:36:14 +0200 Subject: [PATCH 003/213] added test for upstream clang on macos-14, as an example for an arm64 platform --- .github/workflows/ci-test-package-cmake.yml | 25 ++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-test-package-cmake.yml b/.github/workflows/ci-test-package-cmake.yml index df1cae9f20..979ed1be91 100644 --- a/.github/workflows/ci-test-package-cmake.yml +++ b/.github/workflows/ci-test-package-cmake.yml @@ -136,6 +136,20 @@ jobs: cxx_modules: "False", std_format_support: "True" } + - { + name: "Clang-18 on Apple M1 (arm64)", + os: macos-14, + compiler: + { + type: CLANG, + version: 18, + cc: "/opt/homebrew/opt/llvm@18/bin/clang-18", + cxx: "/opt/homebrew/opt/llvm@18/bin/clang++", + }, + lib: "libc++", + cxx_modules: "False", + std_format_support: "True" + } - { name: "Apple Clang 15", os: macos-14, @@ -188,8 +202,8 @@ jobs: shell: bash run: | sudo apt install -y g++-${{ matrix.config.compiler.version }} - - name: Install Clang - if: matrix.config.compiler.type == 'CLANG' + - name: Install Clang with apt + if: matrix.config.compiler.type == 'CLANG' && matrix.config.os != 'macos-14' shell: bash working-directory: ${{ env.HOME }} run: | @@ -197,8 +211,13 @@ jobs: chmod +x llvm.sh sudo ./llvm.sh ${{ matrix.config.compiler.version }} sudo apt install -y clang-tools-${{ matrix.config.compiler.version }} + - name: Install Clang using homebrew + if: matrix.config.compiler.type == 'CLANG' && matrix.config.os == 'macos-14' + shell: bash + run: | + brew install llvm@18 - name: Install Libc++ - if: matrix.config.compiler.type == 'CLANG' && matrix.config.lib == 'libc++' + if: matrix.config.compiler.type == 'CLANG' && matrix.config.lib == 'libc++' && matrix.config.os != 'macos-14' shell: bash run: | sudo apt install -y libc++-${{ matrix.config.compiler.version }}-dev libc++abi-${{ matrix.config.compiler.version }}-dev libunwind-${{ matrix.config.compiler.version }}-dev From 25534c6998a0b79b6f356b7a32c4576d1094db05 Mon Sep 17 00:00:00 2001 From: Yves Delley Date: Sun, 15 Sep 2024 18:34:28 +0200 Subject: [PATCH 004/213] also run conan tests on macOS 14 (again, an example for arm64) --- .github/workflows/ci-conan.yml | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml index 58df088083..380bbae523 100644 --- a/.github/workflows/ci-conan.yml +++ b/.github/workflows/ci-conan.yml @@ -143,6 +143,20 @@ jobs: std_format_support: "True", conan-config: "", } + - { + name: "Clang-18 on Apple M1 (arm64)", + os: macos-14, + compiler: + { + type: CLANG, + version: 18, + cc: "/opt/homebrew/opt/llvm@18/bin/clang-18", + cxx: "/opt/homebrew/opt/llvm@18/bin/clang++", + }, + lib: "libc++", + cxx_modules: "False", + std_format_support: "True" + } - { name: "Apple Clang 15", os: macos-13, @@ -196,8 +210,8 @@ jobs: shell: bash run: | sudo apt install -y g++-${{ matrix.config.compiler.version }} - - name: Install Clang - if: matrix.config.compiler.type == 'CLANG' + - name: Install Clang with apt + if: matrix.config.compiler.type == 'CLANG' && matrix.config.os != 'macos-14' shell: bash working-directory: ${{ env.HOME }} run: | @@ -205,8 +219,13 @@ jobs: chmod +x llvm.sh sudo ./llvm.sh ${{ matrix.config.compiler.version }} sudo apt install -y clang-tools-${{ matrix.config.compiler.version }} + - name: Install Clang using homebrew + if: matrix.config.compiler.type == 'CLANG' && matrix.config.os == 'macos-14' + shell: bash + run: | + brew install llvm@18 - name: Install Libc++ - if: matrix.config.compiler.type == 'CLANG' && matrix.config.lib == 'libc++' + if: matrix.config.compiler.type == 'CLANG' && matrix.config.lib == 'libc++' && matrix.config.os != 'macos-14' shell: bash run: | sudo apt install -y libc++-${{ matrix.config.compiler.version }}-dev libc++abi-${{ matrix.config.compiler.version }}-dev libunwind-${{ matrix.config.compiler.version }}-dev From 618b097c42372fbf927e1ce567ed1d9f01fbedef Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sun, 15 Sep 2024 19:29:40 -0600 Subject: [PATCH 005/213] refactor: `value_cast` now reuses `value_cast` --- src/core/include/mp-units/framework/value_cast.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/include/mp-units/framework/value_cast.h b/src/core/include/mp-units/framework/value_cast.h index 5fe6437352..020424dcd5 100644 --- a/src/core/include/mp-units/framework/value_cast.h +++ b/src/core/include/mp-units/framework/value_cast.h @@ -93,7 +93,7 @@ template [[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q) { - return detail::sudo_cast>(std::forward(q)); + return value_cast(std::forward(q)); } @@ -181,9 +181,7 @@ template [[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp) { - return quantity_point{ - value_cast(std::forward(qp).quantity_from_origin_is_an_implementation_detail_), - QP::point_origin}; + return value_cast(std::forward(qp)); } /** From 78efb77b967457298633e0a25a9c8454d1d4f68e Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sun, 15 Sep 2024 19:30:13 -0600 Subject: [PATCH 006/213] style: empty leading line removed from `quantity.h` --- src/core/include/mp-units/framework/quantity.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index 18490e0729..3d1b319f8d 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -1,4 +1,3 @@ - // The MIT License (MIT) // // Copyright (c) 2018 Mateusz Pusz From 3e502fb795a93c2f56bf78034c7ff92cc116aeb6 Mon Sep 17 00:00:00 2001 From: Yves Delley Date: Mon, 16 Sep 2024 20:34:12 +0200 Subject: [PATCH 007/213] increase tolerance for certain math tests to two epsilon --- test/runtime/almost_equals.h | 9 +++++---- test/runtime/math_test.cpp | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/test/runtime/almost_equals.h b/test/runtime/almost_equals.h index 0cc03e8eb7..4c2153a0bd 100644 --- a/test/runtime/almost_equals.h +++ b/test/runtime/almost_equals.h @@ -35,7 +35,7 @@ namespace mp_units { template struct AlmostEqualsMatcher : Catch::Matchers::MatcherGenericBase { - explicit AlmostEqualsMatcher(const T& target) : target_{target} {} + explicit AlmostEqualsMatcher(const T& target, int n_eps) : target_{target}, n_eps_{n_eps} {} template U> requires std::same_as @@ -48,7 +48,7 @@ struct AlmostEqualsMatcher : Catch::Matchers::MatcherGenericBase { const auto y = common(other).numerical_value_in(common::unit); if constexpr (treat_as_floating_point) { const auto maxXYOne = std::max({rep{1}, abs(x), abs(y)}); - return abs(x - y) <= std::numeric_limits::epsilon() * maxXYOne; + return abs(x - y) <= (n_eps_ * std::numeric_limits::epsilon()) * maxXYOne; } else { if (x >= 0) { return x - 1 <= y && y - 1 <= x; @@ -71,12 +71,13 @@ struct AlmostEqualsMatcher : Catch::Matchers::MatcherGenericBase { private: const T& target_; + int n_eps_; }; template -AlmostEqualsMatcher AlmostEquals(const T& target) +AlmostEqualsMatcher AlmostEquals(const T& target, int n_eps = 1) { - return AlmostEqualsMatcher{target}; + return AlmostEqualsMatcher{target, n_eps}; } diff --git a/test/runtime/math_test.cpp b/test/runtime/math_test.cpp index 2a59135b84..79018bff87 100644 --- a/test/runtime/math_test.cpp +++ b/test/runtime/math_test.cpp @@ -448,7 +448,7 @@ TEST_CASE("Angle trigonometric functions", "[trig][angle]") REQUIRE_THAT(sin(0 * angle[grad]), AlmostEquals(0. * one)); REQUIRE_THAT(sin(100 * angle[grad]), AlmostEquals(1. * one)); - REQUIRE_THAT(sin(200 * angle[grad]), AlmostEquals(0. * one)); + REQUIRE_THAT(sin(200 * angle[grad]), AlmostEquals(0. * one, 2)); REQUIRE_THAT(sin(300 * angle[grad]), AlmostEquals(-1. * one)); } @@ -475,7 +475,7 @@ TEST_CASE("Angle trigonometric functions", "[trig][angle]") REQUIRE_THAT(tan(0 * angle[grad]), AlmostEquals(0. * one)); REQUIRE_THAT(tan(50 * angle[grad]), AlmostEquals(1. * one)); REQUIRE_THAT(tan(150 * angle[grad]), AlmostEquals(-1. * one)); - REQUIRE_THAT(tan(200 * angle[grad]), AlmostEquals(0. * one)); + REQUIRE_THAT(tan(200 * angle[grad]), AlmostEquals(0. * one, 2)); } } From 7ce8d695b180fc76bceb9a04b9e3b4c08d8b2e79 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 17 Sep 2024 12:48:13 -0600 Subject: [PATCH 008/213] test: `std::complex`-based quantities tests added --- test/static/quantity_test.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/static/quantity_test.cpp b/test/static/quantity_test.cpp index 6df1602a84..23853b9271 100644 --- a/test/static/quantity_test.cpp +++ b/test/static/quantity_test.cpp @@ -37,9 +37,15 @@ import std; #include #if MP_UNITS_HOSTED #include +#include #endif #endif +#if MP_UNITS_HOSTED +template +constexpr bool mp_units::is_scalar> = true; +#endif + template<> constexpr bool mp_units::is_vector = true; @@ -267,6 +273,12 @@ static_assert(quantity(2000 * m).force_in(km).numerical_val static_assert((15. * m).in(nm).numerical_value_in(m) == 15.); static_assert((15'000. * nm).in(m).numerical_value_in(nm) == 15'000.); +#if MP_UNITS_HOSTED +using namespace std::complex_literals; +static_assert(((2. + 1i) * V).in(mV).numerical_value_in(mV) == 2000. + 1000i); +static_assert(((2. + 1i) * V).in(mV).numerical_value_in(V) == 2. + 1i); +#endif + template typename Q> concept invalid_unit_conversion = requires { requires !requires { Q(2000 * m).in(km); }; // truncating conversion From 80880f1014bb596220290c1782e532d205371bcb Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 17 Sep 2024 16:11:19 -0600 Subject: [PATCH 009/213] docs: examples line numbers fixed --- docs/users_guide/examples/avg_speed.md | 32 +++++++-------- docs/users_guide/examples/hello_units.md | 47 ++++++++++++----------- docs/users_guide/examples/si_constants.md | 21 ++++++---- 3 files changed, 54 insertions(+), 46 deletions(-) diff --git a/docs/users_guide/examples/avg_speed.md b/docs/users_guide/examples/avg_speed.md index 397cafc1c4..313725df61 100644 --- a/docs/users_guide/examples/avg_speed.md +++ b/docs/users_guide/examples/avg_speed.md @@ -7,7 +7,7 @@ tags: # `avg_speed` -!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/TnqGa4sdn)" +!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/v9c5T6bc4)" Let's continue the previous example. This time, our purpose will not be to showcase as many library features as possible, but we will scope on different interfaces one can provide @@ -18,28 +18,28 @@ First, we either import a module or include all the necessary header files and i the identifiers from the `mp_units` namespace: ```cpp title="avg_speed.cpp" linenums="1" ---8<-- "example/avg_speed.cpp:28:42" +--8<-- "example/avg_speed.cpp:28:46" ``` Next, we define two functions calculating average speed based on quantities of fixed units and integral and floating-point representation types, respectively, and a third function that we introduced in the [previous example](hello_units.md): -```cpp title="avg_speed.cpp" linenums="16" ---8<-- "example/avg_speed.cpp:44:58" +```cpp title="avg_speed.cpp" linenums="20" +--8<-- "example/avg_speed.cpp:48:62" ``` We also added a simple utility to print our results: -```cpp title="avg_speed.cpp" linenums="31" ---8<-- "example/avg_speed.cpp:60:66" +```cpp title="avg_speed.cpp" linenums="35" +--8<-- "example/avg_speed.cpp:64:70" ``` Now, let's analyze how those three utility functions behave with different sets of arguments. First, we are going to use quantities of SI units and integral representation: -```cpp title="avg_speed.cpp" linenums="38" ---8<-- "example/avg_speed.cpp:68:82" +```cpp title="avg_speed.cpp" linenums="42" +--8<-- "example/avg_speed.cpp:72:86" ``` The above provides the following output: @@ -61,8 +61,8 @@ representation types (the resulting speed is `108 km/h`). The second scenario is really similar to the previous one, but this time, function arguments have floating-point representation types: -```cpp title="avg_speed.cpp" linenums="53" ---8<-- "example/avg_speed.cpp:84:95" +```cpp title="avg_speed.cpp" linenums="57" +--8<-- "example/avg_speed.cpp:88:99" ``` Conversion from floating-point to integral representation types is @@ -82,8 +82,8 @@ Average speed of a car that makes 220 km in 2 h is 110 km/h. Next, let's do the same for integral and floating-point representations, but this time using international mile: -```cpp title="avg_speed.cpp" linenums="65" ---8<-- "example/avg_speed.cpp:97:129" +```cpp title="avg_speed.cpp" linenums="69" +--8<-- "example/avg_speed.cpp:101:132" ``` One important difference here is the fact that as it is not possible to make a lossless conversion @@ -108,8 +108,8 @@ Please note how the first and third results get truncated using integral represe In the end, we repeat the scenario for CGS units: -```cpp title="avg_speed.cpp" linenums="97" ---8<-- "example/avg_speed.cpp:131:161" +```cpp title="avg_speed.cpp" linenums="101" +--8<-- "example/avg_speed.cpp:134:165" ``` Again, we observe `value_cast` being used in the same places and consistent truncation errors @@ -129,6 +129,6 @@ Average speed of a car that makes 2.2e+07 cm in 7200 s is 110 km/h. The example file ends with a simple `main()` function: -```cpp title="avg_speed.cpp" linenums="128" ---8<-- "example/avg_speed.cpp:163:" +```cpp title="avg_speed.cpp" linenums="133" +--8<-- "example/avg_speed.cpp:167:" ``` diff --git a/docs/users_guide/examples/hello_units.md b/docs/users_guide/examples/hello_units.md index dd69fcf478..a434e08621 100644 --- a/docs/users_guide/examples/hello_units.md +++ b/docs/users_guide/examples/hello_units.md @@ -6,32 +6,32 @@ tags: # `hello_units` -!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/bT4GGPbef)" +!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/MYn5qjPzh)" This is a really simple example showcasing the features of the **mp-units** library. First, we either import the `mp_units` module or include the headers for: -- an International System of Quantities (ISQ) -- an International System of units (SI) -- units derived from the International Yard and Pound -- text formatting and stream output support +- an International System of Quantities (ISQ), +- an International System of units (SI), +- units derived from the International Yard and Pound, +- text formatting and stream output support. ```cpp title="hello_units.cpp" linenums="1" ---8<-- "example/hello_units.cpp:28:41" +--8<-- "example/hello_units.cpp:28:45" ``` Also, to shorten the definitions, we "import" all the symbols from the `mp_units` namespace. -```cpp title="hello_units.cpp" linenums="14" ---8<-- "example/hello_units.cpp:42:43" +```cpp title="hello_units.cpp" linenums="18" +--8<-- "example/hello_units.cpp:46:47" ``` Next, we define a simple function that calculates the average speed based on the provided arguments of length and time: -```cpp title="hello_units.cpp" linenums="15" ---8<-- "example/hello_units.cpp:44:47" +```cpp title="hello_units.cpp" linenums="19" +--8<-- "example/hello_units.cpp:48:51" ``` The above function template takes any quantities implicitly convertible to `isq::length` @@ -45,37 +45,37 @@ that its quantity type is implicitly convertible to `isq::speed`. type is beneficial for users of such a function as it provides more information of what to expect from a function than just using `auto`. -```cpp title="hello_units.cpp" linenums="19" ---8<-- "example/hello_units.cpp:49:52" +```cpp title="hello_units.cpp" linenums="23" +--8<-- "example/hello_units.cpp:53:56" ``` The above lines explicitly opt into using unit symbols from two systems of units. As this introduces a lot of short identifiers into the current scope, it is not done implicitly while including a header file. -```cpp title="hello_units.cpp" linenums="23" ---8<-- "example/hello_units.cpp:54:60" +```cpp title="hello_units.cpp" linenums="27" +--8<-- "example/hello_units.cpp:58:64" ``` -- Lines `23` & `24` create a quantity of kind `isq::length / isq::time` with the numbers +- Lines `27` & `28` create a quantity of kind `isq::length / isq::time` with the numbers and units provided. Such quantities can be converted or assigned to any other quantity with a matching kind. -- Line `25` calls our function template with quantities of kind `isq::length` and +- Line `29` calls our function template with quantities of kind `isq::length` and `isq::time` and number and units provided. -- Line `26` explicitly provides quantity types of the quantities passed to a function template. +- Line `30` explicitly provides quantity types of the quantities passed to a function template. This time, those will not be quantity kinds anymore and will have [more restrictive conversion rules](../framework_basics/simple_and_typed_quantities.md#quantity_cast-to-force-unsafe-conversions). -- Line `27` changes the unit of a quantity `v3` to `m / s` in a +- Line `31` changes the unit of a quantity `v3` to `m / s` in a [value-preserving way](../framework_basics/value_conversions.md#value-preserving-conversions) (floating-point representations are considered to be value-preserving). -- Line `28` does a similar operation, but this time, it would also succeed for +- Line `32` does a similar operation, but this time, it would also succeed for [value-truncating cases](../framework_basics/value_conversions.md#value-truncating-conversions) (if that was the case). -- Line `29` does a [value-truncating conversion](../framework_basics/value_conversions.md#value-truncating-conversions) +- Line `33` does a [value-truncating conversion](../framework_basics/value_conversions.md#value-truncating-conversions) of changing the underlying representation type from `double` to `int`. -```cpp title="hello_units.cpp" linenums="30" ---8<-- "example/hello_units.cpp:62" +```cpp title="hello_units.cpp" linenums="34" +--8<-- "example/hello_units.cpp:66" ``` The above presents [various ways to print a quantity](../framework_basics/text_output.md). @@ -86,3 +86,6 @@ Both stream insertion operations and `std::format` facilities are supported. `MP_UNITS_STD_FMT` is used for compatibility reasons. If a specific compiler does not support `std::format` or a user prefers to use the `{fmt}` library, this macro will resolve to `fmt` namespace. Otherwise, the `std` namespace will be used. + + More about it can be found in the [Wide Compatibility](../use_cases/wide_compatibility.md#mp_units_std_fmt) + chapter. diff --git a/docs/users_guide/examples/si_constants.md b/docs/users_guide/examples/si_constants.md index 05dbfbc245..f5feb15353 100644 --- a/docs/users_guide/examples/si_constants.md +++ b/docs/users_guide/examples/si_constants.md @@ -6,7 +6,7 @@ tags: # `si_constants` -!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/MevcK8vYT)" +!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/eGqbW5d8K)" The next example presents all the seven defining constants of the SI system. We can observe how [Faster-than-lightspeed Constants](../framework_basics/faster_than_lightspeed_constants.md) @@ -22,15 +22,20 @@ the simplicity of this example, we to be able to express vector quantities with simple scalar types. ```cpp title="si_constants.cpp" linenums="14" ---8<-- "example/si_constants.cpp:42:" +--8<-- "example/si_constants.cpp:42:44" ``` -The main part of the example prints all of the SI-defining constants. While analyzing the output of -this program (provided below), we can easily notice that a direct printing of the quantity provides -just a value `1` with a proper constant symbol. This is the main power of the -[Faster-than-lightspeed Constants](../framework_basics/faster_than_lightspeed_constants.md) feature. -Only after we explicitly convert the unit of a quantity to proper SI units we get an actual numeric -value of the constant. +The main part of the example prints all of the SI-defining constants: + +```cpp title="si_constants.cpp" linenums="17" +--8<-- "example/si_constants.cpp:45:" +``` + +While analyzing the output of this program (provided below), we can easily notice that a direct +printing of the quantity provides just a value `1` with a proper constant symbol. This is the main +power of the [Faster-than-lightspeed Constants](../framework_basics/faster_than_lightspeed_constants.md) +feature. Only after we explicitly convert the unit of a quantity to proper SI units we get an +actual numeric value of the constant. ```text The seven defining constants of the SI and the seven corresponding units they define: From 3ead7c2d520d1fdfd2b46ffa5f998676f9be7bdf Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 17 Sep 2024 16:12:03 -0600 Subject: [PATCH 010/213] feat: hw_voltage example added --- docs/users_guide/examples/hw_voltage.md | 72 ++++++++++++++++++ example/CMakeLists.txt | 1 + example/hw_voltage.cpp | 97 +++++++++++++++++++++++++ mkdocs.yml | 1 + 4 files changed, 171 insertions(+) create mode 100644 docs/users_guide/examples/hw_voltage.md create mode 100644 example/hw_voltage.cpp diff --git a/docs/users_guide/examples/hw_voltage.md b/docs/users_guide/examples/hw_voltage.md new file mode 100644 index 0000000000..92925732ee --- /dev/null +++ b/docs/users_guide/examples/hw_voltage.md @@ -0,0 +1,72 @@ +--- +tags: + - Affine Space + - Embedded + - Text Formatting +--- + +# `hw_voltage` + +!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/jjod7hvsd)" + +As it was stated in [The Affine Space](../framework_basics/the_affine_space.md) chapter, +every measurement can (and probably should) be modelled as a `quantity_point`. This is +a perfect example of such a use case. + +This example implements a simplified scenario of measuring voltage read from hardware through +a mapped 16-bits register. The actual voltage range of [-10 V, 10 V] is mapped to [-32767, 32767] +on hardware. Translation of the value requires not only scaling of the value but also applying +of an offset. + +First we include all the dependencies: + +```cpp title="hw_voltage.cpp" linenums="1" +--8<-- "example/hw_voltage.cpp:28:43" +``` + +Next, we specify the real measurement voltage range to be in the range of [-10, 10]: + +```cpp title="hw_voltage.cpp" linenums="17" +--8<-- "example/hw_voltage.cpp:45:48" +``` + +and provide a storage type and special values for the hardware representation: + +```cpp title="hw_voltage.cpp" linenums="21" +--8<-- "example/hw_voltage.cpp:50:56" +``` + +Finally, we define a quantity point origin, an offset unit that scales the value and uses this +origin to offset the zero of the sale, and a dedicated quantity point alias using those: + +```cpp title="hw_voltage.cpp" linenums="28" +--8<-- "example/hw_voltage.cpp:60:66" +``` + +Now, when everything is ready, we can simulate mapping of our hardware register, and provide +a helper function that will read the value and construct a quantity point from the obtained copy: + +```cpp title="hw_voltage.cpp" linenums="35" +--8<-- "example/hw_voltage.cpp:69:77" +``` + +We also provide a simple print helper for our quantity points: + +```cpp title="hw_voltage.cpp" linenums="44" +--8<-- "example/hw_voltage.cpp:79:82" +``` + +In the main function we simulate setting of 3 values by our hardware. Each of them is read +and printed in the voltage unit used on the hardware as well as in the standard SI unit: + +```cpp title="hw_voltage.cpp" linenums="48" +--8<-- "example/hw_voltage.cpp:84:" +``` + +The above program results with the following text output: + +```text + 0 hwV (-10 V) + 32767 hwV ( 0 V) + 65534 hwV ( 10 V) +``` diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index de84b9a8f6..7c329d4eec 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -57,6 +57,7 @@ add_example(currency) add_example(foot_pound_second) add_example(glide_computer glide_computer_lib) add_example(hello_units) +add_example(hw_voltage) add_example(measurement) add_example(si_constants) add_example(spectroscopy_units) diff --git a/example/hw_voltage.cpp b/example/hw_voltage.cpp new file mode 100644 index 0000000000..8eef10858a --- /dev/null +++ b/example/hw_voltage.cpp @@ -0,0 +1,97 @@ +// 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. + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// !!! Before you commit any changes to this file please make sure to check if it !!! +// !!! renders correctly in the documentation "Examples" section. !!! +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +#include +#ifdef MP_UNITS_IMPORT_STD +import std; +#else +#include +#include +#endif +#ifdef MP_UNITS_MODULES +import mp_units; +#else +#include +#include +#include +#endif + +using namespace mp_units; + +// real voltage range +inline constexpr int min_voltage = -10; +inline constexpr int max_voltage = 10; +inline constexpr int voltage_range = max_voltage - min_voltage; + +// hardware encoding of voltage +using voltage_hw_t = std::uint16_t; +inline constexpr voltage_hw_t voltage_hw_error = std::numeric_limits::max(); +inline constexpr voltage_hw_t voltage_hw_min = 0; +inline constexpr voltage_hw_t voltage_hw_max = voltage_hw_error - 1; +inline constexpr voltage_hw_t voltage_hw_range = voltage_hw_max - voltage_hw_min; +inline constexpr voltage_hw_t voltage_hw_zero = voltage_hw_range / 2; + + +// clang-format off +inline constexpr struct hw_voltage_origin final : + relative_point_origin(min_voltage)> {} hw_voltage_origin; + +inline constexpr struct hw_voltage_unit final : + named_unit<"hwV", mag_ratio * si::volt, hw_voltage_origin> {} hw_voltage_unit; + +using hw_voltage_quantity_point = quantity_point; +// clang-format on + +// mapped HW register +volatile voltage_hw_t hw_voltage_value; + +std::optional read_hw_voltage() +{ + voltage_hw_t local_copy = hw_voltage_value; + if (local_copy == voltage_hw_error) return std::nullopt; + return absolute(local_copy); +} + +void print(QuantityPoint auto qp) +{ + std::println("{:10} ({:5})", qp.quantity_from_zero(), value_cast(qp).quantity_from_zero()); +} + +int main() +{ + // simulate reading of 3 values from the hardware + hw_voltage_value = voltage_hw_min; + quantity_point qp1 = read_hw_voltage().value(); + hw_voltage_value = voltage_hw_zero; + quantity_point qp2 = read_hw_voltage().value(); + hw_voltage_value = voltage_hw_max; + quantity_point qp3 = read_hw_voltage().value(); + + print(qp1); + print(qp2); + print(qp3); +} diff --git a/mkdocs.yml b/mkdocs.yml index da83c3c84b..d54a807efb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -175,6 +175,7 @@ nav: - hello_units: users_guide/examples/hello_units.md - avg_speed: users_guide/examples/avg_speed.md - si_constants: users_guide/examples/si_constants.md + - hw_voltage: users_guide/examples/hw_voltage.md - Appendix: - Glossary: appendix/glossary.md - References: appendix/references.md From 7e777f228ab98c58adec79f8280fb52a67596076 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 17 Sep 2024 17:10:53 -0600 Subject: [PATCH 011/213] fix: hw_voltage compilation fixed on C++20 --- docs/users_guide/examples/hw_voltage.md | 26 ++++++++++++------------- example/hw_voltage.cpp | 6 ++++-- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/docs/users_guide/examples/hw_voltage.md b/docs/users_guide/examples/hw_voltage.md index 92925732ee..42ac306000 100644 --- a/docs/users_guide/examples/hw_voltage.md +++ b/docs/users_guide/examples/hw_voltage.md @@ -21,46 +21,46 @@ of an offset. First we include all the dependencies: ```cpp title="hw_voltage.cpp" linenums="1" ---8<-- "example/hw_voltage.cpp:28:43" +--8<-- "example/hw_voltage.cpp:28:44" ``` Next, we specify the real measurement voltage range to be in the range of [-10, 10]: -```cpp title="hw_voltage.cpp" linenums="17" ---8<-- "example/hw_voltage.cpp:45:48" +```cpp title="hw_voltage.cpp" linenums="18" +--8<-- "example/hw_voltage.cpp:46:49" ``` and provide a storage type and special values for the hardware representation: -```cpp title="hw_voltage.cpp" linenums="21" ---8<-- "example/hw_voltage.cpp:50:56" +```cpp title="hw_voltage.cpp" linenums="22" +--8<-- "example/hw_voltage.cpp:51:57" ``` Finally, we define a quantity point origin, an offset unit that scales the value and uses this origin to offset the zero of the sale, and a dedicated quantity point alias using those: -```cpp title="hw_voltage.cpp" linenums="28" ---8<-- "example/hw_voltage.cpp:60:66" +```cpp title="hw_voltage.cpp" linenums="29" +--8<-- "example/hw_voltage.cpp:61:67" ``` Now, when everything is ready, we can simulate mapping of our hardware register, and provide a helper function that will read the value and construct a quantity point from the obtained copy: -```cpp title="hw_voltage.cpp" linenums="35" ---8<-- "example/hw_voltage.cpp:69:77" +```cpp title="hw_voltage.cpp" linenums="36" +--8<-- "example/hw_voltage.cpp:70:78" ``` We also provide a simple print helper for our quantity points: -```cpp title="hw_voltage.cpp" linenums="44" ---8<-- "example/hw_voltage.cpp:79:82" +```cpp title="hw_voltage.cpp" linenums="45" +--8<-- "example/hw_voltage.cpp:80:84" ``` In the main function we simulate setting of 3 values by our hardware. Each of them is read and printed in the voltage unit used on the hardware as well as in the standard SI unit: -```cpp title="hw_voltage.cpp" linenums="48" ---8<-- "example/hw_voltage.cpp:84:" +```cpp title="hw_voltage.cpp" linenums="50" +--8<-- "example/hw_voltage.cpp:86:" ``` The above program results with the following text output: diff --git a/example/hw_voltage.cpp b/example/hw_voltage.cpp index 8eef10858a..f04c6489fd 100644 --- a/example/hw_voltage.cpp +++ b/example/hw_voltage.cpp @@ -26,11 +26,12 @@ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #include +#include #ifdef MP_UNITS_IMPORT_STD import std; #else +#include #include -#include #endif #ifdef MP_UNITS_MODULES import mp_units; @@ -78,7 +79,8 @@ std::optional read_hw_voltage() void print(QuantityPoint auto qp) { - std::println("{:10} ({:5})", qp.quantity_from_zero(), value_cast(qp).quantity_from_zero()); + std::cout << MP_UNITS_STD_FMT::format("{:10} ({:5})", qp.quantity_from_zero(), + value_cast(qp).quantity_from_zero()); } int main() From 5305de7a2852c0cda542e0baa959dc21dadd303d Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 17 Sep 2024 17:12:14 -0600 Subject: [PATCH 012/213] docs: "Text Formatting" tag removed from avg_speed example --- docs/users_guide/examples/avg_speed.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/users_guide/examples/avg_speed.md b/docs/users_guide/examples/avg_speed.md index 313725df61..9a0073a90d 100644 --- a/docs/users_guide/examples/avg_speed.md +++ b/docs/users_guide/examples/avg_speed.md @@ -2,7 +2,6 @@ tags: - CGS System - International System - - Text Formatting --- # `avg_speed` From 831c017a2298354230d0659ae4b33065e1e9a5a8 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 17 Sep 2024 20:14:57 -0600 Subject: [PATCH 013/213] build: `std_format` enabled for MSVC --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 5a876a110c..f4519c588e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -103,7 +103,7 @@ def _feature_compatibility(self): "gcc": "13", "clang": "17", "apple-clang": "", - "msvc": "", + "msvc": "194", }, }, "cxx_modules": { From 089ce3f9d94e090367cb7eb72c30fb6d4229ee97 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 17 Sep 2024 20:27:26 -0600 Subject: [PATCH 014/213] style: pre-commit updated to clang-format-18.1.8 --- .clang-format | 12 +++- .pre-commit-config.yaml | 2 +- src/core/include/mp-units/framework/compare.h | 24 ++------ .../mp-units/framework/customization_points.h | 8 +-- .../mp-units/framework/dimension_concepts.h | 3 +- .../include/mp-units/framework/quantity.h | 60 +++++-------------- .../mp-units/framework/quantity_concepts.h | 8 +-- .../framework/quantity_point_concepts.h | 4 +- .../mp-units/framework/quantity_spec.h | 2 +- .../framework/quantity_spec_concepts.h | 8 +-- .../include/mp-units/framework/reference.h | 16 ++--- .../framework/representation_concepts.h | 2 +- .../mp-units/framework/unit_concepts.h | 2 +- 13 files changed, 50 insertions(+), 101 deletions(-) diff --git a/.clang-format b/.clang-format index bbffcae153..e60e2541c9 100644 --- a/.clang-format +++ b/.clang-format @@ -10,24 +10,28 @@ AccessModifierOffset: -2 # AcrossEmptyLines: false # AcrossComments: false # AlignCompound: false +# AlignFunctionPointers: false # PadOperators: true # AlignConsecutiveBitFields: # Enabled: false # AcrossEmptyLines: false # AcrossComments: false # AlignCompound: false +# AlignFunctionPointers: false # PadOperators: false # AlignConsecutiveDeclarations: # Enabled: false # AcrossEmptyLines: false # AcrossComments: false # AlignCompound: false +# AlignFunctionPointers: false # PadOperators: false # AlignConsecutiveMacros: # Enabled: false # AcrossEmptyLines: false # AcrossComments: false # AlignCompound: false +# AlignFunctionPointers: false # PadOperators: false # AlignConsecutiveShortCaseStatements: # Enabled: false @@ -41,8 +45,10 @@ AccessModifierOffset: -2 # OverEmptyLines: 0 # AllowAllArgumentsOnNextLine: true # AllowAllParametersOfDeclarationOnNextLine: true +# AllowBreakBeforeNoexceptSpecifier: Never # AllowShortBlocksOnASingleLine: Never # AllowShortCaseLabelsOnASingleLine: false +# AllowShortCompoundRequirementOnASingleLine: true # AllowShortEnumsOnASingleLine: true # AllowShortFunctionsOnASingleLine: All # AllowShortIfStatementsOnASingleLine: WithoutElse @@ -76,7 +82,8 @@ BraceWrapping: # SplitEmptyFunction: true SplitEmptyRecord: false # SplitEmptyNamespace: true -# BreakAfterAttributes: Never +# BreakAdjacentStringLiterals: true +# BreakAfterAttributes: Leave # BreakAfterJavaFieldAnnotations: false # BreakArrays: true # BreakBeforeBinaryOperators: None @@ -160,6 +167,7 @@ MaxEmptyLinesToKeep: 2 # PenaltyBreakComment: 300 # PenaltyBreakFirstLessLess: 120 # PenaltyBreakOpenParenthesis: 0 +# PenaltyBreakScopeResolution: 500 # PenaltyBreakString: 1000 # PenaltyBreakTemplateDeclaration: 10 # PenaltyExcessCharacter: 1000000 @@ -207,6 +215,7 @@ QualifierAlignment: Left # RequiresExpressionIndentation: OuterScope # SeparateDefinitionBlocks: Leave # ShortNamespaceLines: 1 +# SkipMacroDefinitionBody: false # SortIncludes: CaseSensitive # SortJavaStaticImport: Before SortUsingDeclarations: false @@ -228,6 +237,7 @@ SpaceBeforeParensOptions: # AfterFunctionDeclarationName: false # AfterIfMacros: true # AfterOverloadedOperator: false + # AfterPlacementOperator: true AfterRequiresInClause: true # AfterRequiresInExpression: false # BeforeNonEmptyParentheses: false diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 38ce493234..43956a87d7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: - id: trailing-whitespace - id: end-of-file-fixer - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v17.0.5 + rev: v18.1.8 hooks: - id: clang-format - repo: https://github.com/cheshirekow/cmake-format-precommit diff --git a/src/core/include/mp-units/framework/compare.h b/src/core/include/mp-units/framework/compare.h index aef69ae8fe..c044a12936 100644 --- a/src/core/include/mp-units/framework/compare.h +++ b/src/core/include/mp-units/framework/compare.h @@ -38,9 +38,7 @@ namespace mp_units { template requires requires { - { - T::zero() - } -> std::equality_comparable_with; + { T::zero() } -> std::equality_comparable_with; } [[nodiscard]] constexpr bool is_eq_zero(T v) { @@ -49,9 +47,7 @@ template template requires requires { - { - T::zero() - } -> std::equality_comparable_with; + { T::zero() } -> std::equality_comparable_with; } [[nodiscard]] constexpr bool is_neq_zero(T v) { @@ -60,9 +56,7 @@ template template requires requires { - { - T::zero() - } -> std::three_way_comparable_with; + { T::zero() } -> std::three_way_comparable_with; } [[nodiscard]] constexpr bool is_lt_zero(T v) { @@ -71,9 +65,7 @@ template template requires requires { - { - T::zero() - } -> std::three_way_comparable_with; + { T::zero() } -> std::three_way_comparable_with; } [[nodiscard]] constexpr bool is_gt_zero(T v) { @@ -82,9 +74,7 @@ template template requires requires { - { - T::zero() - } -> std::three_way_comparable_with; + { T::zero() } -> std::three_way_comparable_with; } [[nodiscard]] constexpr bool is_lteq_zero(T v) { @@ -93,9 +83,7 @@ template template requires requires { - { - T::zero() - } -> std::three_way_comparable_with; + { T::zero() } -> std::three_way_comparable_with; } [[nodiscard]] constexpr bool is_gteq_zero(T v) { diff --git a/src/core/include/mp-units/framework/customization_points.h b/src/core/include/mp-units/framework/customization_points.h index 8d7add3f8c..16a33a2b84 100644 --- a/src/core/include/mp-units/framework/customization_points.h +++ b/src/core/include/mp-units/framework/customization_points.h @@ -118,9 +118,7 @@ struct quantity_values { static constexpr Rep min() noexcept requires requires { - { - std::numeric_limits::lowest() - } -> std::same_as; + { std::numeric_limits::lowest() } -> std::same_as; } { return std::numeric_limits::lowest(); @@ -128,9 +126,7 @@ struct quantity_values { static constexpr Rep max() noexcept requires requires { - { - std::numeric_limits::max() - } -> std::same_as; + { std::numeric_limits::max() } -> std::same_as; } { return std::numeric_limits::max(); diff --git a/src/core/include/mp-units/framework/dimension_concepts.h b/src/core/include/mp-units/framework/dimension_concepts.h index 1d289867bb..90330eb29e 100644 --- a/src/core/include/mp-units/framework/dimension_concepts.h +++ b/src/core/include/mp-units/framework/dimension_concepts.h @@ -79,8 +79,7 @@ constexpr bool is_per_of_dims> = (... && (BaseDimension || is_dimension_one::value || IsPowerOfDim)); template -concept DerivedDimensionExpr = - BaseDimension || is_dimension_one::value || IsPowerOfDim || is_per_of_dims; +concept DerivedDimensionExpr = BaseDimension || is_dimension_one::value || IsPowerOfDim || is_per_of_dims; template concept SameDimension = diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index 3d1b319f8d..03088ba88e 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -64,9 +64,7 @@ concept IsFloatingPoint = treat_as_floating_point; template concept ValuePreservingTo = requires(FromRep&& from, ToRep to) { - { - to = std::forward(from) - } -> std::same_as; + { to = std::forward(from) } -> std::same_as; } && (IsFloatingPoint || (!IsFloatingPoint && (integral_conversion_factor(FromUnit, ToUnit)))); template @@ -325,9 +323,7 @@ class quantity { // member unary operators [[nodiscard]] constexpr QuantityOf auto operator+() const requires requires(rep v) { - { - +v - } -> std::common_with; + { +v } -> std::common_with; } { return ::mp_units::quantity{+numerical_value_is_an_implementation_detail_, reference}; @@ -335,9 +331,7 @@ class quantity { [[nodiscard]] constexpr QuantityOf auto operator-() const requires requires(rep v) { - { - -v - } -> std::common_with; + { -v } -> std::common_with; } { return ::mp_units::quantity{-numerical_value_is_an_implementation_detail_, reference}; @@ -346,9 +340,7 @@ class quantity { template Q = std::remove_cvref_t> friend constexpr decltype(auto) operator++(FwdQ&& q) requires requires(rep v) { - { - ++v - } -> std::same_as; + { ++v } -> std::same_as; } { ++q.numerical_value_is_an_implementation_detail_; @@ -357,9 +349,7 @@ class quantity { [[nodiscard]] constexpr QuantityOf auto operator++(int) requires requires(rep v) { - { - v++ - } -> std::common_with; + { v++ } -> std::common_with; } { return ::mp_units::quantity{numerical_value_is_an_implementation_detail_++, reference}; @@ -368,9 +358,7 @@ class quantity { template Q = std::remove_cvref_t> friend constexpr decltype(auto) operator--(FwdQ&& q) requires requires(rep v) { - { - --v - } -> std::same_as; + { --v } -> std::same_as; } { --q.numerical_value_is_an_implementation_detail_; @@ -379,9 +367,7 @@ class quantity { [[nodiscard]] constexpr QuantityOf auto operator--(int) requires requires(rep v) { - { - v-- - } -> std::common_with; + { v-- } -> std::common_with; } { return ::mp_units::quantity{numerical_value_is_an_implementation_detail_--, reference}; @@ -390,9 +376,7 @@ class quantity { // compound assignment operators template Q = std::remove_cvref_t> requires requires(rep a, rep b) { - { - a += b - } -> std::same_as; + { a += b } -> std::same_as; } friend constexpr decltype(auto) operator+=(FwdQ&& lhs, const quantity& rhs) { @@ -402,9 +386,7 @@ class quantity { template Q = std::remove_cvref_t> requires requires(rep a, rep b) { - { - a -= b - } -> std::same_as; + { a -= b } -> std::same_as; } friend constexpr decltype(auto) operator-=(FwdQ&& lhs, const quantity& rhs) { @@ -414,9 +396,7 @@ class quantity { template Q = std::remove_cvref_t> requires(!treat_as_floating_point) && requires(rep a, rep b) { - { - a %= b - } -> std::same_as; + { a %= b } -> std::same_as; } friend constexpr decltype(auto) operator%=(FwdQ&& lhs, const quantity& rhs) @@ -428,9 +408,7 @@ class quantity { template Q = std::remove_cvref_t> requires(!Quantity) && requires(rep a, const Value b) { - { - a *= b - } -> std::same_as; + { a *= b } -> std::same_as; } friend constexpr decltype(auto) operator*=(FwdQ&& lhs, const Value& v) { @@ -442,9 +420,7 @@ class quantity { template Q2, std::derived_from Q1 = std::remove_cvref_t> requires(Q2::unit == ::mp_units::one) && requires(rep a, const typename Q2::rep b) { - { - a *= b - } -> std::same_as; + { a *= b } -> std::same_as; } friend constexpr decltype(auto) operator*=(FwdQ1&& lhs, const Q2& rhs) { @@ -457,9 +433,7 @@ class quantity { template Q = std::remove_cvref_t> requires(!Quantity) && requires(rep a, const Value b) { - { - a /= b - } -> std::same_as; + { a /= b } -> std::same_as; } friend constexpr decltype(auto) operator/=(FwdQ&& lhs, const Value& v) { @@ -472,9 +446,7 @@ class quantity { template Q2, std::derived_from Q1 = std::remove_cvref_t> requires(Q2::unit == ::mp_units::one) && requires(rep a, const typename Q2::rep b) { - { - a /= b - } -> std::same_as; + { a /= b } -> std::same_as; } friend constexpr decltype(auto) operator/=(FwdQ1&& lhs, const Q2& rhs) { @@ -673,9 +645,7 @@ MP_UNITS_EXPORT_END template requires requires { - { - mp_units::common_reference(Q1::reference, Q2::reference) - } -> mp_units::Reference; + { mp_units::common_reference(Q1::reference, Q2::reference) } -> mp_units::Reference; typename std::common_type_t; } struct std::common_type { diff --git a/src/core/include/mp-units/framework/quantity_concepts.h b/src/core/include/mp-units/framework/quantity_concepts.h index cfdb705a28..2cdf9eb76d 100644 --- a/src/core/include/mp-units/framework/quantity_concepts.h +++ b/src/core/include/mp-units/framework/quantity_concepts.h @@ -78,12 +78,8 @@ concept QuantityLike = requires { requires RepresentationOf::rep, get_quantity_spec(quantity_like_traits::reference).character>; } && requires(T q, typename quantity_like_traits::rep v) { - { - quantity_like_traits::to_numerical_value(q) - } -> detail::ConversionSpecOf::rep>; - { - quantity_like_traits::from_numerical_value(v) - } -> detail::ConversionSpecOf; + { quantity_like_traits::to_numerical_value(q) } -> detail::ConversionSpecOf::rep>; + { quantity_like_traits::from_numerical_value(v) } -> detail::ConversionSpecOf; }; MP_UNITS_EXPORT_END diff --git a/src/core/include/mp-units/framework/quantity_point_concepts.h b/src/core/include/mp-units/framework/quantity_point_concepts.h index 2269f7ec35..89f49dbf6e 100644 --- a/src/core/include/mp-units/framework/quantity_point_concepts.h +++ b/src/core/include/mp-units/framework/quantity_point_concepts.h @@ -161,9 +161,7 @@ concept QuantityPointLike = requires { { quantity_point_like_traits::to_numerical_value(qp) } -> detail::ConversionSpecOf::rep>; - { - quantity_point_like_traits::from_numerical_value(v) - } -> detail::ConversionSpecOf; + { quantity_point_like_traits::from_numerical_value(v) } -> detail::ConversionSpecOf; }; } // namespace mp_units diff --git a/src/core/include/mp-units/framework/quantity_spec.h b/src/core/include/mp-units/framework/quantity_spec.h index ef21e47ec5..7672ba0291 100644 --- a/src/core/include/mp-units/framework/quantity_spec.h +++ b/src/core/include/mp-units/framework/quantity_spec.h @@ -684,7 +684,7 @@ struct ingredients_less : std::bool_constant<(lhs_compl > rhs_compl) || (lhs_compl == rhs_compl && ingredients_dimension_less(Lhs::dimension, Rhs::dimension)) || (lhs_compl == rhs_compl && Lhs::dimension == Rhs::dimension && - detail::type_name() < detail::type_name())> {}; + detail::type_name() < detail::type_name())>{}; template using type_list_of_ingredients_less = expr_less; diff --git a/src/core/include/mp-units/framework/quantity_spec_concepts.h b/src/core/include/mp-units/framework/quantity_spec_concepts.h index d726349495..545b1875c7 100644 --- a/src/core/include/mp-units/framework/quantity_spec_concepts.h +++ b/src/core/include/mp-units/framework/quantity_spec_concepts.h @@ -86,8 +86,8 @@ template struct is_dimensionless : std::false_type {}; template -concept IsPowerOfQuantitySpec = is_specialization_of_power && (NamedQuantitySpec || - is_dimensionless::value); +concept IsPowerOfQuantitySpec = is_specialization_of_power && + (NamedQuantitySpec || is_dimensionless::value); template @@ -142,8 +142,8 @@ concept ChildQuantitySpecOf = (is_child_of(Child, Parent)); template concept NestedQuantityKindSpecOf = - QuantitySpec && QuantitySpec && - (!SameQuantitySpec)&&ChildQuantitySpecOf; + QuantitySpec && QuantitySpec && (!SameQuantitySpec) && + ChildQuantitySpecOf; template concept QuantitySpecConvertibleTo = diff --git a/src/core/include/mp-units/framework/reference.h b/src/core/include/mp-units/framework/reference.h index ea19739df5..e926a1e22d 100644 --- a/src/core/include/mp-units/framework/reference.h +++ b/src/core/include/mp-units/framework/reference.h @@ -271,12 +271,8 @@ constexpr auto operator/(R, Q&& q) = delete; [[nodiscard]] consteval AssociatedUnit auto common_reference(AssociatedUnit auto u1, AssociatedUnit auto u2, AssociatedUnit auto... rest) requires requires { - { - common_quantity_spec(get_quantity_spec(u1), get_quantity_spec(u2), get_quantity_spec(rest)...) - } -> QuantitySpec; - { - common_unit(u1, u2, rest...) - } -> AssociatedUnit; + { common_quantity_spec(get_quantity_spec(u1), get_quantity_spec(u2), get_quantity_spec(rest)...) } -> QuantitySpec; + { common_unit(u1, u2, rest...) } -> AssociatedUnit; } { return common_unit(u1, u2, rest...); @@ -285,12 +281,8 @@ constexpr auto operator/(R, Q&& q) = delete; template [[nodiscard]] consteval Reference auto common_reference(R1 r1, R2 r2, Rest... rest) requires(!(AssociatedUnit && AssociatedUnit && (... && AssociatedUnit))) && requires { - { - common_quantity_spec(get_quantity_spec(r1), get_quantity_spec(r2), get_quantity_spec(rest)...) - } -> QuantitySpec; - { - common_unit(get_unit(r1), get_unit(r2), get_unit(rest)...) - } -> Unit; + { common_quantity_spec(get_quantity_spec(r1), get_quantity_spec(r2), get_quantity_spec(rest)...) } -> QuantitySpec; + { common_unit(get_unit(r1), get_unit(r2), get_unit(rest)...) } -> Unit; } { return detail::reference_t && std::equality_comparable; MP_UNITS_EXPORT template concept Representation = - (is_scalar || is_vector || is_tensor)&&detail::WeaklyRegular && detail::Scalable; + (is_scalar || is_vector || is_tensor) && detail::WeaklyRegular && detail::Scalable; MP_UNITS_EXPORT template concept RepresentationOf = Representation && ((Ch == quantity_character::scalar && is_scalar) || diff --git a/src/core/include/mp-units/framework/unit_concepts.h b/src/core/include/mp-units/framework/unit_concepts.h index e924454271..92a2199bd6 100644 --- a/src/core/include/mp-units/framework/unit_concepts.h +++ b/src/core/include/mp-units/framework/unit_concepts.h @@ -161,7 +161,7 @@ concept UnitConvertibleTo = MP_UNITS_EXPORT template concept UnitCompatibleWith = Unit && Unit && QuantitySpec && - (!AssociatedUnit || UnitOf)&&detail::UnitConvertibleTo; + (!AssociatedUnit || UnitOf) && detail::UnitConvertibleTo; template concept OffsetUnit = Unit && requires { T::point_origin; }; From 8e188030830512e6246af55ecd0a52f3f67517a5 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 18 Sep 2024 19:41:02 -0600 Subject: [PATCH 015/213] build: gitpod Dockerfile fixed --- .gitpod/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitpod/Dockerfile b/.gitpod/Dockerfile index c26d34d2ab..38d32f8da7 100644 --- a/.gitpod/Dockerfile +++ b/.gitpod/Dockerfile @@ -30,5 +30,5 @@ RUN npm install split mathjax-full mathjax-node-sre RUN cabal update # Install MathJax-Node-CLI -git clone https://github.com/mathjax/mathjax-node-cli --depth=1 -echo "export PATH=\"$PWD/mathjax-node-cli/bin:\$PATH\"" >> ~/.bashrc +RUN git clone https://github.com/mathjax/mathjax-node-cli --depth=1 +RUN echo "export PATH=\"$PWD/mathjax-node-cli/bin:\$PATH\"" >> ~/.bashrc From f43310585a1d09a4ccf95d5d5baf5620796e0ec6 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 19 Sep 2024 14:46:43 -0600 Subject: [PATCH 016/213] build: `generate()` in `test_package` now correctly propagates project's options --- test_package/conanfile.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/test_package/conanfile.py b/test_package/conanfile.py index 5e3586b192..37654d773b 100644 --- a/test_package/conanfile.py +++ b/test_package/conanfile.py @@ -39,9 +39,22 @@ def layout(self): def generate(self): tc = CMakeToolchain(self) - tc.variables["MP_UNITS_API_STD_FORMAT"] = bool( - self.dependencies["mp-units"].options.std_format - ) + opt = self.dependencies["mp-units"].options + if opt.cxx_modules: + tc.cache_variables["CMAKE_CXX_SCAN_FOR_MODULES"] = True + tc.cache_variables["MP_UNITS_BUILD_CXX_MODULES"] = True + if opt.import_std: + tc.cache_variables["CMAKE_CXX_MODULE_STD"] = True + tc.cache_variables["MP_UNITS_BUILD_IMPORT_STD"] = True + # Current experimental support according to `Help/dev/experimental.rst` + tc.cache_variables["CMAKE_EXPERIMENTAL_CXX_IMPORT_STD"] = ( + "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" + ) + if opt.freestanding: + tc.cache_variables["MP_UNITS_API_FREESTANDING"] = True + else: + tc.cache_variables["MP_UNITS_API_STD_FORMAT"] = opt.std_format + tc.cache_variables["MP_UNITS_API_CONTRACTS"] = str(opt.contracts).upper() tc.generate() def build(self): From e9284652abea1e3ff7174eb76cd4f01b0e0f062e Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 19 Sep 2024 14:47:36 -0600 Subject: [PATCH 017/213] build: `target_include_directories` is not needed anymore --- src/cmake/AddMPUnitsModule.cmake | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/cmake/AddMPUnitsModule.cmake b/src/cmake/AddMPUnitsModule.cmake index 6be0e27861..df551f09e2 100644 --- a/src/cmake/AddMPUnitsModule.cmake +++ b/src/cmake/AddMPUnitsModule.cmake @@ -72,10 +72,6 @@ function(add_mp_units_module name target_name) target_sources( ${target_name} PUBLIC FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include FILES ${ARG_HEADERS} ) - target_include_directories( - ${target_name} ${${projectPrefix}_AS_SYSTEM} ${${projectPrefix}TARGET_SCOPE} - $ $ - ) endif() if(${projectPrefix}BUILD_CXX_MODULES) From 64056ee3212a037bc9b71b243fd3436fbfb0b459 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 19 Sep 2024 14:50:32 -0600 Subject: [PATCH 018/213] build: `target_compile_features` now uses `CMAKE_CXX_STANDARD` --- src/cmake/AddMPUnitsModule.cmake | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cmake/AddMPUnitsModule.cmake b/src/cmake/AddMPUnitsModule.cmake index df551f09e2..279899b1d9 100644 --- a/src/cmake/AddMPUnitsModule.cmake +++ b/src/cmake/AddMPUnitsModule.cmake @@ -64,7 +64,13 @@ function(add_mp_units_module name target_name) # define the target for a module add_library(${target_name} ${SCOPE}) - target_compile_features(${target_name} ${${projectPrefix}TARGET_SCOPE} cxx_std_20) + + if(DEFINED CMAKE_CXX_STANDARD AND CMAKE_CXX_STANDARD GREATER 20) + # TODO revise when a fixed version of CMake is released + target_compile_features(${target_name} ${${projectPrefix}TARGET_SCOPE} cxx_std_${CMAKE_CXX_STANDARD}) + else() + target_compile_features(${target_name} ${${projectPrefix}TARGET_SCOPE} cxx_std_20) + endif() target_link_libraries(${target_name} ${${projectPrefix}TARGET_SCOPE} ${ARG_DEPENDENCIES}) set_target_properties(${target_name} PROPERTIES EXPORT_NAME ${name}) From adbae41175089b5167fde84dc49b803d87d05154 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 19 Sep 2024 15:26:33 -0600 Subject: [PATCH 019/213] feat: `MP_UNITS_IMPORT_STD` and `MP_UNITS_MODULES` handled properly in `test_package.cpp` --- test_package/test_package.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test_package/test_package.cpp b/test_package/test_package.cpp index e676162f2d..c6229ecfab 100644 --- a/test_package/test_package.cpp +++ b/test_package/test_package.cpp @@ -20,7 +20,16 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include +#include +#include +#ifdef MP_UNITS_IMPORT_STD +import std; +#else +#include +#endif +#ifdef MP_UNITS_MODULES +import mp_units; +#else #include #include #include From 3a9f09345ee304f42cdb2d188d777b9c469b968c Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 19 Sep 2024 15:28:09 -0600 Subject: [PATCH 020/213] style: reformat `test_package/conanfile.py` --- test_package/conanfile.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test_package/conanfile.py b/test_package/conanfile.py index 37654d773b..73f188e18c 100644 --- a/test_package/conanfile.py +++ b/test_package/conanfile.py @@ -47,9 +47,9 @@ def generate(self): tc.cache_variables["CMAKE_CXX_MODULE_STD"] = True tc.cache_variables["MP_UNITS_BUILD_IMPORT_STD"] = True # Current experimental support according to `Help/dev/experimental.rst` - tc.cache_variables["CMAKE_EXPERIMENTAL_CXX_IMPORT_STD"] = ( - "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" - ) + tc.cache_variables[ + "CMAKE_EXPERIMENTAL_CXX_IMPORT_STD" + ] = "0e5b6991-d74f-4b3d-a41c-cf096e0b2508" if opt.freestanding: tc.cache_variables["MP_UNITS_API_FREESTANDING"] = True else: From 7cbbcb5c174b28bf1aa348bbca368177752c19ce Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 19 Sep 2024 15:31:59 -0600 Subject: [PATCH 021/213] fix: header conflict resulting from a merge fixed --- test_package/test_package.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_package/test_package.cpp b/test_package/test_package.cpp index c6229ecfab..15e159e00b 100644 --- a/test_package/test_package.cpp +++ b/test_package/test_package.cpp @@ -34,7 +34,7 @@ import mp_units; #include #include #include -#include +#endif using namespace mp_units; From bfa221857eacf8c5d9546ffdf3289aa8620eab13 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 19 Sep 2024 15:54:48 -0600 Subject: [PATCH 022/213] docs: enabled generation of PDF for API Reference --- docs/api_reference/src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api_reference/src/CMakeLists.txt b/docs/api_reference/src/CMakeLists.txt index 74fc94fbf7..28e29d64ec 100644 --- a/docs/api_reference/src/CMakeLists.txt +++ b/docs/api_reference/src/CMakeLists.txt @@ -14,7 +14,7 @@ jegp_add_standardese_sources( LIBRARIES intro quantities EXTENSIONS macros_extensions CHECKED TRUE - # PDF PATH "mp-units.pdf" #[[EXCLUDE_FROM_MAIN]] + PDF PATH "mp-units.pdf" #[[EXCLUDE_FROM_MAIN]] HTML PATH "mp-units.html" #[[EXCLUDE_FROM_MAIN]] SECTION_FILE_STYLE "WithExtension" LATEX_REGEX_REPLACE From bbdb7503624635486bcc7378584a685c23aa70c6 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 19 Sep 2024 17:02:30 -0600 Subject: [PATCH 023/213] build: `package_type` is dynamically set in conanfile.py depending if we build modules or not --- conanfile.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index f4519c588e..266a8accc7 100644 --- a/conanfile.py +++ b/conanfile.py @@ -82,7 +82,6 @@ class MPUnitsConan(ConanFile): "example/*", "CMakeLists.txt", ) - package_type = "header-library" no_copy_source = True @property @@ -204,6 +203,10 @@ def config_options(self): self._set_default_option(key) def configure(self): + if self.options.cxx_modules: + self.package_type = "static-library" + else: + self.package_type = "header-library" if self.options.freestanding: self.options.rm_safe("std_format") From 61a730d0a4879d67f4eed0d5eeafc5df6538edad Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 19 Sep 2024 17:09:53 -0600 Subject: [PATCH 024/213] build: latex dependencies installation --- .github/workflows/documentation.yml | 1 + .gitpod/Dockerfile | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 9c1d49fc44..a215d06b8b 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -53,6 +53,7 @@ jobs: git fetch origin gh-pages --depth=1 - name: Installing API reference dependencies run: | + sudo apt install latexmk texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended lmodern sudo apt install haskell-stack graphviz nodejs npm ghc cabal-install npm install split mathjax-full mathjax-node-sre cabal update diff --git a/.gitpod/Dockerfile b/.gitpod/Dockerfile index 38d32f8da7..5c487083fb 100644 --- a/.gitpod/Dockerfile +++ b/.gitpod/Dockerfile @@ -19,6 +19,12 @@ RUN sudo install-packages \ libz-dev # Install API reference dependencies +RUN sudo install-packages \ + latexmk \ + texlive-latex-recommended \ + texlive-latex-extra \ + texlive-fonts-recommended \ + lmodern RUN sudo install-packages \ haskell-stack \ graphviz \ From 6e6e0ce2d8e2954411ac2a07e07d1c8027d28a66 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 19 Sep 2024 21:12:16 -0600 Subject: [PATCH 025/213] docs: line breaks in mermaid graphs fixed --- .../framework_basics/dimensionless_quantities.md | 8 ++++---- .../framework_basics/systems_of_quantities.md | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/users_guide/framework_basics/dimensionless_quantities.md b/docs/users_guide/framework_basics/dimensionless_quantities.md index 3cb3d089f4..9547aec1fe 100644 --- a/docs/users_guide/framework_basics/dimensionless_quantities.md +++ b/docs/users_guide/framework_basics/dimensionless_quantities.md @@ -253,15 +253,15 @@ Those cases make dimensionless quantities an exceptional tree in the library. Th ```mermaid flowchart TD - dimensionless["dimensionless\n[one]"] + dimensionless["dimensionless
[one]"] dimensionless --- rotation dimensionless --- efficiency - dimensionless --- angular_measure["angular_measure\n[rad]"] + dimensionless --- angular_measure["angular_measure
[rad]"] angular_measure --- rotational_displacement angular_measure --- phase_angle - dimensionless --- solid_angular_measure["solid_angular_measure\n[sr]"] + dimensionless --- solid_angular_measure["solid_angular_measure
[sr]"] dimensionless --- drag_factor - dimensionless --- storage_capacity["storage_capacity\n[bit]"] --- equivalent_binary_storage_capacity + dimensionless --- storage_capacity["storage_capacity
[bit]"] --- equivalent_binary_storage_capacity dimensionless --- ... ``` diff --git a/docs/users_guide/framework_basics/systems_of_quantities.md b/docs/users_guide/framework_basics/systems_of_quantities.md index 438de21380..6f0b1ec3cb 100644 --- a/docs/users_guide/framework_basics/systems_of_quantities.md +++ b/docs/users_guide/framework_basics/systems_of_quantities.md @@ -108,8 +108,8 @@ flowchart TD path_length --- distance distance --- radial_distance length --- wavelength - length --- position_vector["position_vector\n{vector}"] - length --- displacement["displacement\n{vector}"] + length --- position_vector["position_vector
{vector}"] + length --- displacement["displacement
{vector}"] radius --- radius_of_curvature ``` @@ -299,12 +299,12 @@ The below presents some arbitrary hierarchy of derived quantities of kind energy ```mermaid flowchart TD - energy["energy\n(mass * length2 / time2)"] + energy["energy
(mass * length2 / time2)"] energy --- mechanical_energy mechanical_energy --- potential_energy - potential_energy --- gravitational_potential_energy["gravitational_potential_energy\n(mass * acceleration_of_free_fall * height)"] - potential_energy --- elastic_potential_energy["elastic_potential_energy\n(spring_constant * amount_of_compression2)"] - mechanical_energy --- kinetic_energy["kinetic_energy\n(mass * speed2)"] + potential_energy --- gravitational_potential_energy["gravitational_potential_energy
(mass * acceleration_of_free_fall * height)"] + potential_energy --- elastic_potential_energy["elastic_potential_energy
(spring_constant * amount_of_compression2)"] + mechanical_energy --- kinetic_energy["kinetic_energy
(mass * speed2)"] energy --- enthalpy enthalpy --- internal_energy[internal_energy, thermodynamic_energy] internal_energy --- Helmholtz_energy[Helmholtz_energy, Helmholtz_function] From 696f789e77f25839d972989b499678d7abe175b6 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 19 Sep 2024 21:43:19 -0600 Subject: [PATCH 026/213] fix: `complex_power` & co fixed --- .../include/mp-units/systems/isq/electromagnetism.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/systems/include/mp-units/systems/isq/electromagnetism.h b/src/systems/include/mp-units/systems/isq/electromagnetism.h index bdb5bc8b2f..96adc09fae 100644 --- a/src/systems/include/mp-units/systems/isq/electromagnetism.h +++ b/src/systems/include/mp-units/systems/isq/electromagnetism.h @@ -137,12 +137,12 @@ QUANTITY_SPEC(modulus_of_admittance, admittance); QUANTITY_SPEC(quality_factor, dimensionless, reactance / resistance); QUANTITY_SPEC(loss_factor, dimensionless, inverse(quality_factor)); QUANTITY_SPEC(loss_angle, angular_measure); -QUANTITY_SPEC(active_power, inverse(period) * (instantaneous_power * time)); -QUANTITY_SPEC(apparent_power, voltage* electric_current); +QUANTITY_SPEC(active_power, isq::power, inverse(period) * (instantaneous_power * time)); +QUANTITY_SPEC(complex_power, voltage_phasor* electric_current_phasor); // separate kind +QUANTITY_SPEC(apparent_power, complex_power); QUANTITY_SPEC(power_factor, dimensionless, active_power / apparent_power); -QUANTITY_SPEC(complex_power, voltage_phasor* electric_current_phasor); -QUANTITY_SPEC(reactive_power, complex_power); -QUANTITY_SPEC(non_active_power, pow<1, 2>(pow<2>(apparent_power))); +QUANTITY_SPEC(reactive_power, isq::mass* pow<2>(isq::length) / pow<3>(isq::time)); // separate kind +QUANTITY_SPEC(non_active_power, pow<1, 2>(pow<2>(apparent_power))); // separate kind QUANTITY_SPEC(active_energy, instantaneous_power* time); } // namespace mp_units::isq From 7f0dea9d1881ce919370d02b8e8accd7b9f2ee3a Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 19 Sep 2024 21:57:03 -0600 Subject: [PATCH 027/213] feat: `complex` quantity character added --- .../mp-units/framework/customization_points.h | 8 ++++++++ .../mp-units/framework/representation_concepts.h | 13 ++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/core/include/mp-units/framework/customization_points.h b/src/core/include/mp-units/framework/customization_points.h index 16a33a2b84..6b40e83338 100644 --- a/src/core/include/mp-units/framework/customization_points.h +++ b/src/core/include/mp-units/framework/customization_points.h @@ -64,6 +64,14 @@ constexpr bool treat_as_floating_point = treat_as_floating_point constexpr bool is_scalar = std::is_floating_point_v || (std::is_integral_v && !is_same_v); +/** + * @brief Specifies a type to have a complex character + * + * A complex is a physical quantity that has a complex representation type. + */ +template +constexpr bool is_complex = false; + /** * @brief Specifies a type to have a vector character * diff --git a/src/core/include/mp-units/framework/representation_concepts.h b/src/core/include/mp-units/framework/representation_concepts.h index 764edbc4b0..3a19398edf 100644 --- a/src/core/include/mp-units/framework/representation_concepts.h +++ b/src/core/include/mp-units/framework/representation_concepts.h @@ -50,6 +50,8 @@ namespace mp_units { * * A scalar is a physical quantity that has magnitude but no direction. * + * A complex is a physical quantity that is represented with a complex number. + * * Vectors are physical quantities that possess both magnitude and direction * and whose operations obey the axioms of a vector space. * @@ -57,7 +59,7 @@ namespace mp_units { * For example, the Cauchy stress tensor possess magnitude, direction, * and orientation qualities. */ -MP_UNITS_EXPORT enum class quantity_character : std::int8_t { scalar, vector, tensor }; +MP_UNITS_EXPORT enum class quantity_character : std::int8_t { scalar, complex, vector, tensor }; namespace detail { @@ -86,11 +88,12 @@ concept WeaklyRegular = std::copyable && std::equality_comparable; MP_UNITS_EXPORT template concept Representation = - (is_scalar || is_vector || is_tensor) && detail::WeaklyRegular && detail::Scalable; + (is_scalar || is_complex || is_vector || is_tensor) && detail::WeaklyRegular && detail::Scalable; MP_UNITS_EXPORT template -concept RepresentationOf = Representation && ((Ch == quantity_character::scalar && is_scalar) || - (Ch == quantity_character::vector && is_vector) || - (Ch == quantity_character::tensor && is_tensor)); +concept RepresentationOf = + Representation && + ((Ch == quantity_character::scalar && is_scalar) || (Ch == quantity_character::complex && is_complex) || + (Ch == quantity_character::vector && is_vector) || (Ch == quantity_character::tensor && is_tensor)); } // namespace mp_units From e6c65d6c890a89e14e1ec18b7dbc778e6c2e0f68 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 20 Sep 2024 09:34:38 -0600 Subject: [PATCH 028/213] build: `MP_UNITS_BUILD_IMPORT_STD` CMake option removed --- conanfile.py | 1 - docs/getting_started/cpp_compiler_support.md | 2 +- docs/getting_started/installation_and_usage.md | 8 -------- src/CMakeLists.txt | 4 +--- src/core/CMakeLists.txt | 2 +- test_package/conanfile.py | 1 - 6 files changed, 3 insertions(+), 15 deletions(-) diff --git a/conanfile.py b/conanfile.py index 266a8accc7..f7f3e97b5e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -266,7 +266,6 @@ def generate(self): tc.cache_variables["MP_UNITS_BUILD_CXX_MODULES"] = True if self.options.import_std: tc.cache_variables["CMAKE_CXX_MODULE_STD"] = True - tc.cache_variables["MP_UNITS_BUILD_IMPORT_STD"] = True # Current experimental support according to `Help/dev/experimental.rst` tc.cache_variables[ "CMAKE_EXPERIMENTAL_CXX_IMPORT_STD" diff --git a/docs/getting_started/cpp_compiler_support.md b/docs/getting_started/cpp_compiler_support.md index c62e039396..d5eb5a3480 100644 --- a/docs/getting_started/cpp_compiler_support.md +++ b/docs/getting_started/cpp_compiler_support.md @@ -84,7 +84,7 @@ C++ feature: `import std;` instead of the "old-style" header includes. - Related build options: - Conan: [import_std](installation_and_usage.md#import_std) - - CMake: [MP_UNITS_BUILD_IMPORT_STD](installation_and_usage.md#MP_UNITS_BUILD_IMPORT_STD) + - CMake: [CMAKE_CXX_MODULE_STD](https://cmake.org/cmake/help/latest/variable/CMAKE_CXX_MODULE_STD.html) ## Static `constexpr` variables in `constexpr` functions diff --git a/docs/getting_started/installation_and_usage.md b/docs/getting_started/installation_and_usage.md index 3a59deac01..d9fcbcb61f 100644 --- a/docs/getting_started/installation_and_usage.md +++ b/docs/getting_started/installation_and_usage.md @@ -186,14 +186,6 @@ dependencies by other means, some modifications to the library's CMake files mig [cmake build cxx modules support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 - [`MP_UNITS_BUILD_IMPORT_STD`](#MP_UNITS_BUILD_IMPORT_STD){ #MP_UNITS_BUILD_IMPORT_STD } - - : [:octicons-tag-24: 2.3.0][cmake import std support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`) - - Enables `import std;` usage. - - [cmake import std support]: https://github.com/mpusz/mp-units/releases/tag/v2.3.0 - [`MP_UNITS_API_STD_FORMAT`](#MP_UNITS_API_STD_FORMAT){ #MP_UNITS_API_STD_FORMAT } : [:octicons-tag-24: 2.2.0][cmake std::format support] · :octicons-milestone-24: `ON`/`OFF` (Default: automatically determined) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c9c1fbb82a..7449d678d4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -38,11 +38,9 @@ check_libcxx_in_use(${projectPrefix}LIBCXX) # project build options option(${projectPrefix}BUILD_AS_SYSTEM_HEADERS "Export library as system headers" OFF) option(${projectPrefix}BUILD_CXX_MODULES "Add C++ modules to the list of default targets" OFF) -option(${projectPrefix}BUILD_IMPORT_STD "Enable `import std;` usage" OFF) message(STATUS "${projectPrefix}BUILD_AS_SYSTEM_HEADERS: ${${projectPrefix}BUILD_AS_SYSTEM_HEADERS}") message(STATUS "${projectPrefix}BUILD_CXX_MODULES: ${${projectPrefix}BUILD_CXX_MODULES}") -message(STATUS "${projectPrefix}BUILD_IMPORT_STD: ${${projectPrefix}BUILD_IMPORT_STD}") if(${projectPrefix}BUILD_AS_SYSTEM_HEADERS) set(${projectPrefix}_AS_SYSTEM SYSTEM) @@ -126,7 +124,7 @@ else() set(${projectPrefix}TARGET_SCOPE "INTERFACE") endif() -if(${projectPrefix}BUILD_IMPORT_STD) +if(CMAKE_CXX_MODULE_STD) if(CMAKE_VERSION VERSION_LESS "3.30") message(FATAL_ERROR "CMake versions before 3.30 do not support `import std;` properly") endif() diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ce9db06be9..1adba1e9b6 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -125,7 +125,7 @@ if(${projectPrefix}BUILD_CXX_MODULES) endif() endif() -if(${projectPrefix}BUILD_IMPORT_STD) +if(CMAKE_CXX_MODULE_STD) target_compile_definitions(mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}IMPORT_STD) # https://github.com/llvm/llvm-project/issues/75057 if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19) diff --git a/test_package/conanfile.py b/test_package/conanfile.py index 73f188e18c..d0ea23692d 100644 --- a/test_package/conanfile.py +++ b/test_package/conanfile.py @@ -45,7 +45,6 @@ def generate(self): tc.cache_variables["MP_UNITS_BUILD_CXX_MODULES"] = True if opt.import_std: tc.cache_variables["CMAKE_CXX_MODULE_STD"] = True - tc.cache_variables["MP_UNITS_BUILD_IMPORT_STD"] = True # Current experimental support according to `Help/dev/experimental.rst` tc.cache_variables[ "CMAKE_EXPERIMENTAL_CXX_IMPORT_STD" From e92178abefc638261f0918c9acb3ffd51e23f55b Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 20 Sep 2024 09:52:24 -0600 Subject: [PATCH 029/213] refactor: :boom: `iec80000` system renamed to `iec` --- docs/users_guide/framework_basics/design_overview.md | 2 +- src/systems/CMakeLists.txt | 10 +++++----- .../include/mp-units/systems/{iec80000.h => iec.h} | 8 ++++---- .../systems/{iec80000 => iec}/binary_prefixes.h | 4 ++-- .../mp-units/systems/{iec80000 => iec}/quantities.h | 4 ++-- .../mp-units/systems/{iec80000 => iec}/unit_symbols.h | 8 ++++---- .../include/mp-units/systems/{iec80000 => iec}/units.h | 6 +++--- src/systems/mp-units-systems.cpp | 2 +- test/static/CMakeLists.txt | 2 +- test/static/{iec80000_test.cpp => iec_test.cpp} | 6 +++--- test/static/unit_symbol_test.cpp | 4 ++-- 11 files changed, 28 insertions(+), 28 deletions(-) rename src/systems/include/mp-units/systems/{iec80000.h => iec.h} (86%) rename src/systems/include/mp-units/systems/{iec80000 => iec}/binary_prefixes.h (97%) rename src/systems/include/mp-units/systems/{iec80000 => iec}/quantities.h (98%) rename src/systems/include/mp-units/systems/{iec80000 => iec}/unit_symbols.h (95%) rename src/systems/include/mp-units/systems/{iec80000 => iec}/units.h (94%) rename test/static/{iec80000_test.cpp => iec_test.cpp} (97%) diff --git a/docs/users_guide/framework_basics/design_overview.md b/docs/users_guide/framework_basics/design_overview.md index a63a6846d4..7446bea7fd 100644 --- a/docs/users_guide/framework_basics/design_overview.md +++ b/docs/users_guide/framework_basics/design_overview.md @@ -53,7 +53,7 @@ For example: _luminous intensity_ ($\mathsf{J}$) are the base dimensions of the [ISQ](../../appendix/glossary.md#isq). - A derived dimension of _force_ in the [ISQ](../../appendix/glossary.md#isq) is denoted by $\textsf{dim }F = \mathsf{LMT}^{–2}$. -- The implementation of IEC 80000 in this library provides `iec80000::dim_traffic_intensity` +- The implementation of IEC 80000 in this library provides `iec::dim_traffic_intensity` base dimension to extend ISQ with strong information technology quantities. [Base dimensions](../../appendix/glossary.md#base-dimension) can be defined by the user in diff --git a/src/systems/CMakeLists.txt b/src/systems/CMakeLists.txt index 1d7681bcbd..81fd2abd86 100644 --- a/src/systems/CMakeLists.txt +++ b/src/systems/CMakeLists.txt @@ -24,10 +24,10 @@ add_mp_units_module( systems mp-units-systems DEPENDENCIES mp-units::core HEADERS include/mp-units/systems/angular/units.h - include/mp-units/systems/iec80000/binary_prefixes.h - include/mp-units/systems/iec80000/quantities.h - include/mp-units/systems/iec80000/unit_symbols.h - include/mp-units/systems/iec80000/units.h + include/mp-units/systems/iec/binary_prefixes.h + include/mp-units/systems/iec/quantities.h + include/mp-units/systems/iec/unit_symbols.h + include/mp-units/systems/iec/units.h include/mp-units/systems/isq/atomic_and_nuclear_physics.h include/mp-units/systems/isq/base_quantities.h include/mp-units/systems/isq/electromagnetism.h @@ -44,7 +44,7 @@ add_mp_units_module( include/mp-units/systems/cgs.h include/mp-units/systems/hep.h include/mp-units/systems/iau.h - include/mp-units/systems/iec80000.h + include/mp-units/systems/iec.h include/mp-units/systems/imperial.h include/mp-units/systems/international.h include/mp-units/systems/isq.h diff --git a/src/systems/include/mp-units/systems/iec80000.h b/src/systems/include/mp-units/systems/iec.h similarity index 86% rename from src/systems/include/mp-units/systems/iec80000.h rename to src/systems/include/mp-units/systems/iec.h index 0b19046b2c..d46b9125b2 100644 --- a/src/systems/include/mp-units/systems/iec80000.h +++ b/src/systems/include/mp-units/systems/iec.h @@ -23,10 +23,10 @@ #pragma once // IWYU pragma: begin_exports -#include -#include -#include -#include +#include +#include +#include +#include #ifndef MP_UNITS_IN_MODULE_INTERFACE #include diff --git a/src/systems/include/mp-units/systems/iec80000/binary_prefixes.h b/src/systems/include/mp-units/systems/iec/binary_prefixes.h similarity index 97% rename from src/systems/include/mp-units/systems/iec80000/binary_prefixes.h rename to src/systems/include/mp-units/systems/iec/binary_prefixes.h index e09a218d23..9249eba6d3 100644 --- a/src/systems/include/mp-units/systems/iec80000/binary_prefixes.h +++ b/src/systems/include/mp-units/systems/iec/binary_prefixes.h @@ -28,7 +28,7 @@ #include #endif -namespace mp_units::iec80000 { +namespace mp_units::iec { // clang-format off template struct kibi_ final : prefixed_unit<"Ki", mag_power<2, 10>, U{}> {}; @@ -54,4 +54,4 @@ template constexpr yobi_ -#include -#include +#include +#include #include MP_UNITS_EXPORT -namespace mp_units::iec80000::unit_symbols { +namespace mp_units::iec::unit_symbols { // bit inline constexpr auto kbit = si::kilo; @@ -108,4 +108,4 @@ inline constexpr auto QBd = si::quetta; // TODO do we need prefixed versions of Erlang? inline constexpr auto E = erlang; -} // namespace mp_units::iec80000::unit_symbols +} // namespace mp_units::iec::unit_symbols diff --git a/src/systems/include/mp-units/systems/iec80000/units.h b/src/systems/include/mp-units/systems/iec/units.h similarity index 94% rename from src/systems/include/mp-units/systems/iec80000/units.h rename to src/systems/include/mp-units/systems/iec/units.h index 4d1ee0983a..613c2535d5 100644 --- a/src/systems/include/mp-units/systems/iec80000/units.h +++ b/src/systems/include/mp-units/systems/iec/units.h @@ -23,7 +23,7 @@ #pragma once #include -#include +#include #include #ifndef MP_UNITS_IN_MODULE_INTERFACE @@ -31,7 +31,7 @@ #endif MP_UNITS_EXPORT -namespace mp_units::iec80000 { +namespace mp_units::iec { // clang-format off inline constexpr struct erlang final : named_unit<"E", kind_of> {} erlang; @@ -41,4 +41,4 @@ inline constexpr struct byte final : named_unit<"B", mag<8> * bit> {} byte; inline constexpr struct baud final : named_unit<"Bd", one / si::second, kind_of> {} baud; // clang-format on -} // namespace mp_units::iec80000 +} // namespace mp_units::iec diff --git a/src/systems/mp-units-systems.cpp b/src/systems/mp-units-systems.cpp index 6c2207ee41..72b8f71744 100644 --- a/src/systems/mp-units-systems.cpp +++ b/src/systems/mp-units-systems.cpp @@ -38,7 +38,7 @@ import std; #include #include #include -#include +#include #include #include #include diff --git a/test/static/CMakeLists.txt b/test/static/CMakeLists.txt index 2d8b5f67a5..a16b1edc5d 100644 --- a/test/static/CMakeLists.txt +++ b/test/static/CMakeLists.txt @@ -39,7 +39,7 @@ add_library( fixed_string_test.cpp hep_test.cpp iau_test.cpp - iec80000_test.cpp + iec_test.cpp imperial_test.cpp international_test.cpp isq_test.cpp diff --git a/test/static/iec80000_test.cpp b/test/static/iec_test.cpp similarity index 97% rename from test/static/iec80000_test.cpp rename to test/static/iec_test.cpp index 289d403ecd..6d5825c59a 100644 --- a/test/static/iec80000_test.cpp +++ b/test/static/iec_test.cpp @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include +#include #include #include @@ -29,8 +29,8 @@ namespace { using namespace mp_units; -using namespace mp_units::iec80000; -using namespace mp_units::iec80000::unit_symbols; +using namespace mp_units::iec; +using namespace mp_units::iec::unit_symbols; using namespace mp_units::si::unit_symbols; using enum mp_units::quantity_character; diff --git a/test/static/unit_symbol_test.cpp b/test/static/unit_symbol_test.cpp index 41af02f2f9..c87831be89 100644 --- a/test/static/unit_symbol_test.cpp +++ b/test/static/unit_symbol_test.cpp @@ -21,7 +21,7 @@ // SOFTWARE. #include -#include +#include #include #ifdef MP_UNITS_IMPORT_STD import std; @@ -33,7 +33,7 @@ namespace { using namespace mp_units; using namespace mp_units::si; -using namespace mp_units::iec80000; +using namespace mp_units::iec; using enum text_encoding; using enum unit_symbol_solidus; From 7a83c2b39164873020524d8084a429995c14a568 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 20 Sep 2024 10:57:23 -0600 Subject: [PATCH 030/213] feat: `iec::var` unit added --- src/systems/include/mp-units/systems/iec/unit_symbols.h | 4 ++++ src/systems/include/mp-units/systems/iec/units.h | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/systems/include/mp-units/systems/iec/unit_symbols.h b/src/systems/include/mp-units/systems/iec/unit_symbols.h index e6f621a9ae..08a25c8133 100644 --- a/src/systems/include/mp-units/systems/iec/unit_symbols.h +++ b/src/systems/include/mp-units/systems/iec/unit_symbols.h @@ -30,6 +30,10 @@ MP_UNITS_EXPORT namespace mp_units::iec::unit_symbols { +// Electromagnetism +inline constexpr auto var = volt_ampere_reactive_power; + +// Information science and technology // bit inline constexpr auto kbit = si::kilo; inline constexpr auto Mbit = si::mega; diff --git a/src/systems/include/mp-units/systems/iec/units.h b/src/systems/include/mp-units/systems/iec/units.h index 613c2535d5..597537b72b 100644 --- a/src/systems/include/mp-units/systems/iec/units.h +++ b/src/systems/include/mp-units/systems/iec/units.h @@ -24,6 +24,7 @@ #include #include +#include #include #ifndef MP_UNITS_IN_MODULE_INTERFACE @@ -34,6 +35,10 @@ MP_UNITS_EXPORT namespace mp_units::iec { // clang-format off +// Electromagnetism +inline constexpr struct volt_ampere_reactive_power final : named_unit<"var", si::volt * si::ampere, kind_of> {} volt_ampere_reactive_power; + +// Information science and technology inline constexpr struct erlang final : named_unit<"E", kind_of> {} erlang; inline constexpr struct bit final : named_unit<"bit", one, kind_of> {} bit; inline constexpr struct octet final : named_unit<"o", mag<8> * bit> {} octet; From 1933aadf9342dd8f73973ea9b43a5dba2ff5ccab Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 20 Sep 2024 14:17:18 -0600 Subject: [PATCH 031/213] docs: API Reference PDF generation dependencies added --- CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fe1708c2c4..47210ac2db 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -182,11 +182,13 @@ mkdocs build We need to take a few steps to set up our environment so that we are ready to generate API reference documents. -First, we need to satisfy the requirements described in . +First, we need to satisfy the requirements described in and +. On the Ubuntu platform, this is equivalent to the following instructions run from the user's home directory: ```bash +sudo apt install latexmk texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended lmodern sudo apt install haskell-stack graphviz nodejs npm ghc cabal-install npm install split mathjax-full mathjax-node-sre cabal update From 4492d42b9117b170f583f4ee4317c95fc988fd44 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 20 Sep 2024 17:05:52 -0600 Subject: [PATCH 032/213] Revert "build: `package_type` is dynamically set in conanfile.py depending if we build modules or not" This reverts commit bbdb7503624635486bcc7378584a685c23aa70c6. --- conanfile.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/conanfile.py b/conanfile.py index f7f3e97b5e..deb3b807bb 100644 --- a/conanfile.py +++ b/conanfile.py @@ -82,6 +82,7 @@ class MPUnitsConan(ConanFile): "example/*", "CMakeLists.txt", ) + package_type = "header-library" no_copy_source = True @property @@ -203,10 +204,6 @@ def config_options(self): self._set_default_option(key) def configure(self): - if self.options.cxx_modules: - self.package_type = "static-library" - else: - self.package_type = "header-library" if self.options.freestanding: self.options.rm_safe("std_format") From 3671f64153c7db13a9997909ba5930f51abc2f4d Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Mon, 23 Sep 2024 12:39:22 +0200 Subject: [PATCH 033/213] refactor: :boom: magnitudes code cleanup + `mag_pi` is now `mag` --- .../faster_than_lightspeed_constants.md | 2 +- .../framework_basics/systems_of_units.md | 7 +- src/core/CMakeLists.txt | 3 +- .../include/mp-units/bits/constexpr_math.h | 224 +++++ src/core/include/mp-units/bits/sudo_cast.h | 8 +- .../include/mp-units/framework/magnitude.h | 862 +++++------------- src/core/include/mp-units/framework/unit.h | 4 +- .../include/mp-units/systems/angular/units.h | 2 +- .../include/mp-units/systems/si/chrono.h | 2 +- .../include/mp-units/systems/si/constants.h | 2 +- .../include/mp-units/systems/si/units.h | 2 +- test/runtime/truncation_test.cpp | 4 +- test/static/magnitude_test.cpp | 6 +- test/static/unit_test.cpp | 4 +- 14 files changed, 480 insertions(+), 652 deletions(-) create mode 100644 src/core/include/mp-units/bits/constexpr_math.h diff --git a/docs/users_guide/framework_basics/faster_than_lightspeed_constants.md b/docs/users_guide/framework_basics/faster_than_lightspeed_constants.md index cef490db05..08dee8753c 100644 --- a/docs/users_guide/framework_basics/faster_than_lightspeed_constants.md +++ b/docs/users_guide/framework_basics/faster_than_lightspeed_constants.md @@ -45,7 +45,7 @@ inline constexpr struct speed_of_light_in_vacuum final : } // namespace si2019 inline constexpr struct magnetic_constant final : - named_unit<{u8"μ₀", "u_0"}, mag<4> * mag_pi * mag_power<10, -7> * henry / metre> {} magnetic_constant; + named_unit<{u8"μ₀", "u_0"}, mag<4> * mag * mag_power<10, -7> * henry / metre> {} magnetic_constant; } // namespace mp_units::si ``` diff --git a/docs/users_guide/framework_basics/systems_of_units.md b/docs/users_guide/framework_basics/systems_of_units.md index a45e82004d..7b1e3c10d7 100644 --- a/docs/users_guide/framework_basics/systems_of_units.md +++ b/docs/users_guide/framework_basics/systems_of_units.md @@ -184,11 +184,14 @@ For some units, a magnitude might also be irrational. The best example here is a is defined using a floating-point magnitude having a factor of the number π (Pi): ```cpp -inline constexpr struct mag_pi final : magnitude> {} mag_pi; +struct pi : mag_constant { + static constexpr auto value = std::numbers::pi_v; +}; +inline constexpr pi pi; ``` ```cpp -inline constexpr struct degree final : named_unit<{u8"°", "deg"}, mag_pi / mag<180> * si::radian> {} degree; +inline constexpr struct degree final : named_unit<{u8"°", "deg"}, mag / mag<180> * si::radian> {} degree; ``` diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1adba1e9b6..ffbfc17b5a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -31,7 +31,8 @@ endfunction() # core library definition add_mp_units_module( core mp-units-core - HEADERS include/mp-units/bits/core_gmf.h + HEADERS include/mp-units/bits/constexpr_math.h + include/mp-units/bits/core_gmf.h include/mp-units/bits/get_associated_quantity.h include/mp-units/bits/hacks.h include/mp-units/bits/math_concepts.h diff --git a/src/core/include/mp-units/bits/constexpr_math.h b/src/core/include/mp-units/bits/constexpr_math.h new file mode 100644 index 0000000000..c3c49307d7 --- /dev/null +++ b/src/core/include/mp-units/bits/constexpr_math.h @@ -0,0 +1,224 @@ +// 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 + +#ifndef MP_UNITS_IN_MODULE_INTERFACE +#ifdef MP_UNITS_IMPORT_STD +import std; +#else +#include +#include +#include +#include +#include +#endif +#endif + +namespace mp_units::detail { + +// Raise an arbitrary arithmetic type to a positive integer power at compile time. +template +[[nodiscard]] consteval T int_power(T base, std::integral auto exp) +{ + // As this function should only be called at compile time, the terminations herein function as + // "parameter-compatible static_asserts", and should not result in terminations at runtime. + if (exp < 0) { + std::abort(); // int_power only supports positive integer powers + } + + constexpr auto checked_multiply = [](auto a, auto b) { + const auto result = a * b; + MP_UNITS_DIAGNOSTIC_PUSH + MP_UNITS_DIAGNOSTIC_IGNORE_FLOAT_EQUAL + if (result / a != b) { + std::abort(); // Wraparound detected + } + MP_UNITS_DIAGNOSTIC_POP + return result; + }; + + constexpr auto checked_square = [checked_multiply](auto a) { return checked_multiply(a, a); }; + + if (exp == 0) return T{1}; + if (exp % 2 == 1) return checked_multiply(base, int_power(base, exp - 1)); + return checked_square(int_power(base, exp / 2)); +} + +template +[[nodiscard]] consteval std::optional checked_int_pow(T base, std::uintmax_t exp) +{ + T result = T{1}; + while (exp > 0u) { + if (exp % 2u == 1u) { + if (base > std::numeric_limits::max() / result) { + return std::nullopt; + } + result *= base; + } + + exp /= 2u; + + if (base > std::numeric_limits::max() / base) { + return (exp == 0u) ? std::make_optional(result) : std::nullopt; + } + base *= base; + } + return result; +} + +template +[[nodiscard]] consteval std::optional root(T x, std::uintmax_t n) +{ + // The "zeroth root" would be mathematically undefined. + if (n == 0) { + return std::nullopt; + } + + // The "first root" is trivial. + if (n == 1) { + return x; + } + + // We only support nontrivial roots of floating point types. + if (!std::is_floating_point::value) { + return std::nullopt; + } + + // Handle negative numbers: only odd roots are allowed. + if (x < 0) { + if (n % 2 == 0) { + return std::nullopt; + } else { + const auto negative_result = root(-x, n); + if (!negative_result.has_value()) { + return std::nullopt; + } + return static_cast(-negative_result.value()); + } + } + + // Handle special cases of zero and one. + if (x == 0 || x == 1) { + return x; + } + + // Handle numbers bewtween 0 and 1. + if (x < 1) { + const auto inverse_result = root(T{1} / x, n); + if (!inverse_result.has_value()) { + return std::nullopt; + } + return static_cast(T{1} / inverse_result.value()); + } + + // + // At this point, error conditions are finished, and we can proceed with the "core" algorithm. + // + + // Always use `long double` for intermediate computations. We don't ever expect people to be + // calling this at runtime, so we want maximum accuracy. + long double xld = static_cast(x); + long double lo = 1.0; + long double hi = xld; + + // Do a binary search to find the closest value such that `checked_int_pow` recovers the input. + // + // Because we know `n > 1`, and `x > 1`, and x^n is monotonically increasing, we know that + // `checked_int_pow(lo, n) < x < checked_int_pow(hi, n)`. We will preserve this as an + // invariant. + while (lo < hi) { + long double mid = lo + (hi - lo) / 2; + + auto result = checked_int_pow(mid, n); + + if (!result.has_value()) { + return std::nullopt; + } + + // Early return if we get lucky with an exact answer. + if (result.value() == xld) { + return static_cast(mid); + } + + // Check for stagnation. + if (mid == lo || mid == hi) { + break; + } + + // Preserve the invariant that `checked_int_pow(lo, n) < x < checked_int_pow(hi, n)`. + if (result.value() < xld) { + lo = mid; + } else { + hi = mid; + } + } + + // Pick whichever one gets closer to the target. + const auto lo_diff = xld - checked_int_pow(lo, n).value(); + const auto hi_diff = checked_int_pow(hi, n).value() - xld; + return static_cast(lo_diff < hi_diff ? lo : hi); +} + +// A converter for the value member variable of magnitude (below). +// +// The input is the desired result, but in a (wider) intermediate type. The point of this function +// is to cast to the desired type, but avoid overflow in doing so. +template + requires(!std::is_integral_v || std::is_integral_v) +[[nodiscard]] consteval To checked_static_cast(From x) +{ + // This function should only ever be called at compile time. The purpose of these terminations is + // to produce compiler errors, because we cannot `static_assert` on function arguments. + if constexpr (std::is_integral_v) { + if (!std::in_range(x)) { + std::abort(); // Cannot represent magnitude in this type + } + } + + return static_cast(x); +} + +// The exponent of `factor` in the prime factorization of `n`. +[[nodiscard]] consteval std::intmax_t multiplicity(std::intmax_t factor, std::intmax_t n) +{ + std::intmax_t m = 0; + while (n % factor == 0) { + n /= factor; + ++m; + } + return m; +} + +// Divide a number by a given base raised to some power. +// +// Undefined unless base > 1, pow >= 0, and (base ^ pow) evenly divides n. +// NOLINTNEXTLINE(bugprone-easily-swappable-parameters) +[[nodiscard]] consteval std::intmax_t remove_power(std::intmax_t base, std::intmax_t pow, std::intmax_t n) +{ + while (pow-- > 0) { + n /= base; + } + return n; +} + +} // namespace mp_units::detail diff --git a/src/core/include/mp-units/bits/sudo_cast.h b/src/core/include/mp-units/bits/sudo_cast.h index 845761b65e..9f24949ac7 100644 --- a/src/core/include/mp-units/bits/sudo_cast.h +++ b/src/core/include/mp-units/bits/sudo_cast.h @@ -76,8 +76,8 @@ struct conversion_type_traits { */ template struct conversion_value_traits { - static constexpr Magnitude auto num = numerator(M); - static constexpr Magnitude auto den = denominator(M); + static constexpr Magnitude auto num = _numerator(M); + static constexpr Magnitude auto den = _denominator(M); static constexpr Magnitude auto irr = M * (den / num); static constexpr T num_mult = get_value(num); static constexpr T den_mult = get_value(den); @@ -121,9 +121,9 @@ template(numerator(c_mag)); }); + return scale([&](auto value) { return value * get_value(_numerator(c_mag)); }); else if constexpr (is_integral(pow<-1>(c_mag))) - return scale([&](auto value) { return value / get_value(denominator(c_mag)); }); + return scale([&](auto value) { return value / get_value(_denominator(c_mag)); }); else { using value_traits = conversion_value_traits; if constexpr (std::is_floating_point_v) diff --git a/src/core/include/mp-units/framework/magnitude.h b/src/core/include/mp-units/framework/magnitude.h index c865cbdc06..7cdb9b30c0 100644 --- a/src/core/include/mp-units/framework/magnitude.h +++ b/src/core/include/mp-units/framework/magnitude.h @@ -23,13 +23,12 @@ #pragma once // IWYU pragma: private, include -#include +#include #include #include #include #include #include -#include #include #include #include @@ -42,6 +41,7 @@ import std; #include #include #include +#include #include #include #endif @@ -56,116 +56,28 @@ using factorizer = wheel_factorizer<4>; } // namespace detail -namespace detail { - -template -constexpr bool is_magnitude = false; +MP_UNITS_EXPORT struct mag_constant {}; template -constexpr bool is_specialization_of_magnitude = false; - -} // namespace detail - -/** - * @brief Concept to detect whether T is a valid Magnitude. - */ -MP_UNITS_EXPORT template -concept Magnitude = detail::is_magnitude; - -/** - * @brief A type to represent a standalone constant value. - */ -// template -// struct constant { -// static constexpr auto value = V; -// }; - -// // is_derived_from_specialization_of_constant -// namespace detail { - -// template -// void to_base_specialization_of_constant(const volatile constant*); - -// template -// constexpr bool is_derived_from_specialization_of_constant = -// requires(T * t) { to_base_specialization_of_constant(t); }; - -// } // namespace detail - - -// template -// concept Constant = detail::is_derived_from_specialization_of_constant; - -// struct pi_v : constant> {}; +concept MagConstant = std::derived_from && std::is_empty_v && requires { + { +T::value } -> std::same_as; +}; /** * @brief Any type which can be used as a basis vector in a PowerV. * * We have two categories. * - * The first is just an `std::intmax_t`. This is for prime number bases. These can always be used directly as NTTPs. - * - * The second category is a _custom type_, which has a static member variable of type `long double` that holds its - * value. We choose `long double` to get the greatest degree of precision; users who need a different type can convert - * from this at compile time. This category is for any irrational base we admit into our representation (on which, more - * details below). - * - * The reason we can't hold the value directly for floating point bases is so that we can support some compilers (e.g., - * GCC 10) which don't yet permit floating point NTTPs. - */ - -/** - * @brief A basis vector in our magnitude representation, raised to some rational power. - * - * The public API is that there is a `power` member variable (of type `ratio`), and a `get_base_value()` member function - * (of type either `std::intmax_t` or `long double`, as appropriate), for any specialization. - * - * These types exist to be used as NTTPs for the variadic `magnitude<...>` template. We represent a magnitude (which is - * a positive real number) as the product of rational powers of "basis vectors", where each "basis vector" is a positive - * real number. "Addition" in this vector space corresponds to _multiplying_ two real numbers. "Scalar multiplication" - * corresponds to _raising_ a real number to a _rational power_. Thus, this representation of positive real numbers - * maps them onto a vector space over the rationals, supporting the operations of products and rational powers. - * - * (Note that this is the same representation we already use for dimensions.) - * - * As in any vector space, the set of basis vectors must be linearly independent: that is, no product of basis powers - * can ever give the null vector (which in this case represents the number 1), unless all scalars (again, in this case, - * _powers_) are zero. To achieve this, we use the following kinds of basis vectors. - * - Prime numbers. (These are the only allowable values for `std::intmax_t` base.) - * - Certain selected irrational numbers, such as pi. + * The first is just an integral type (either `int` or `std::intmax_t`). This is for prime number bases. + * These can always be used directly as NTTPs. * - * Before allowing a new irrational base, make sure that it _cannot_ be represented as the product of rational powers of - * _existing_ bases, including both prime numbers and any other irrational bases. For example, even though `sqrt(2)` is - * irrational, we must not ever use it as a base; instead, we would use `base_power{2, ratio{1, 2}}`. + * The second category is a _custom tag type_, which inherits from `mag_constant` and has a static member variable + * `value` of type `long double` that holds its value. We choose `long double` to get the greatest degree of precision; + * users who need a different type can convert from this at compile time. This category is for any irrational base + * we admit into our representation (on which, more details below). */ - -namespace detail { - template -constexpr bool is_named_magnitude = Magnitude && !detail::is_specialization_of_magnitude; - -} - -#if MP_UNITS_COMP_CLANG - -template -struct mag_value { - T value; -}; - -template -mag_value(T) -> mag_value; - -template -concept PowerVBase = - one_of || is_specialization_of || detail::is_named_magnitude; - -#else - -template -concept PowerVBase = one_of || detail::is_named_magnitude; - -#endif +concept PowerVBase = one_of || MagConstant; // TODO Unify with `power` if UTPs (P1985) are accepted by the Committee template @@ -175,29 +87,25 @@ struct power_v { static constexpr detail::ratio exponent{Num, Den...}; }; -namespace detail { - template -constexpr bool is_specialization_of_power_v = false; - -template -constexpr bool is_specialization_of_power_v> = true; - -} // namespace detail - - -template -concept MagnitudeSpec = PowerVBase || detail::is_specialization_of_power_v; +concept MagnitudeSpec = PowerVBase || is_specialization_of_v; +// TODO refactor to typenames? template struct magnitude; +/** + * @brief Concept to detect whether T is a valid Magnitude. + */ +MP_UNITS_EXPORT template +concept Magnitude = is_specialization_of_v; + namespace detail { template [[nodiscard]] consteval auto get_base(Element element) { - if constexpr (detail::is_specialization_of_power_v) + if constexpr (is_specialization_of_v) return Element::base; else return element; @@ -206,11 +114,10 @@ template template [[nodiscard]] consteval auto get_base_value(Element element) { - if constexpr (detail::is_specialization_of_power_v) return get_base_value(Element::base); -#if MP_UNITS_COMP_CLANG - else if constexpr (is_specialization_of) + if constexpr (is_specialization_of_v) + return get_base_value(Element::base); + else if constexpr (MagConstant) return element.value; -#endif else return element; } @@ -218,34 +125,22 @@ template template [[nodiscard]] MP_UNITS_CONSTEVAL ratio get_exponent(Element) { - if constexpr (detail::is_specialization_of_power_v) + if constexpr (is_specialization_of_v) return Element::exponent; else return ratio{1}; } -} // namespace detail - - -/** - * @brief Concept to detect whether a _type_ is a valid base power. - * - * Note that this is somewhat incomplete. We must also detect whether a _value_ of that type is valid for use with - * `magnitude<...>`. We will defer that second check to the constraints on the `magnitude` template. - */ -namespace detail { - // We do not want magnitude type to have the `l` literal after a value for a small integral number. // For example this modifies `magnitude<3l>` to be `magnitude<3>` template [[nodiscard]] consteval auto shorten_T() { if constexpr (std::integral) { - if constexpr (V <= std::numeric_limits::max()) { + if constexpr (V <= std::numeric_limits::max()) return static_cast(V); - } else { + else return static_cast(V); - } } else if constexpr (std::floating_point) { return static_cast(V); } else { @@ -279,149 +174,6 @@ using widen_t = conditional, conditional, std::intmax_t, std::uintmax_t>>, T>; -// Raise an arbitrary arithmetic type to a positive integer power at compile time. -template -[[nodiscard]] consteval T int_power(T base, std::integral auto exp) -{ - // As this function should only be called at compile time, the terminations herein function as - // "parameter-compatible static_asserts", and should not result in terminations at runtime. - if (exp < 0) { - std::abort(); // int_power only supports positive integer powers - } - - constexpr auto checked_multiply = [](auto a, auto b) { - const auto result = a * b; - MP_UNITS_DIAGNOSTIC_PUSH - MP_UNITS_DIAGNOSTIC_IGNORE_FLOAT_EQUAL - if (result / a != b) { - std::abort(); // Wraparound detected - } - MP_UNITS_DIAGNOSTIC_POP - return result; - }; - - constexpr auto checked_square = [checked_multiply](auto a) { return checked_multiply(a, a); }; - - if (exp == 0) return T{1}; - if (exp % 2 == 1) return checked_multiply(base, int_power(base, exp - 1)); - return checked_square(int_power(base, exp / 2)); -} - -template -[[nodiscard]] consteval std::optional checked_int_pow(T base, std::uintmax_t exp) -{ - T result = T{1}; - while (exp > 0u) { - if (exp % 2u == 1u) { - if (base > std::numeric_limits::max() / result) { - return std::nullopt; - } - result *= base; - } - - exp /= 2u; - - if (base > std::numeric_limits::max() / base) { - return (exp == 0u) ? std::make_optional(result) : std::nullopt; - } - base *= base; - } - return result; -} - -template -[[nodiscard]] consteval std::optional root(T x, std::uintmax_t n) -{ - // The "zeroth root" would be mathematically undefined. - if (n == 0) { - return std::nullopt; - } - - // The "first root" is trivial. - if (n == 1) { - return x; - } - - // We only support nontrivial roots of floating point types. - if (!std::is_floating_point::value) { - return std::nullopt; - } - - // Handle negative numbers: only odd roots are allowed. - if (x < 0) { - if (n % 2 == 0) { - return std::nullopt; - } else { - const auto negative_result = root(-x, n); - if (!negative_result.has_value()) { - return std::nullopt; - } - return static_cast(-negative_result.value()); - } - } - - // Handle special cases of zero and one. - if (x == 0 || x == 1) { - return x; - } - - // Handle numbers bewtween 0 and 1. - if (x < 1) { - const auto inverse_result = root(T{1} / x, n); - if (!inverse_result.has_value()) { - return std::nullopt; - } - return static_cast(T{1} / inverse_result.value()); - } - - // - // At this point, error conditions are finished, and we can proceed with the "core" algorithm. - // - - // Always use `long double` for intermediate computations. We don't ever expect people to be - // calling this at runtime, so we want maximum accuracy. - long double xld = static_cast(x); - long double lo = 1.0; - long double hi = xld; - - // Do a binary search to find the closest value such that `checked_int_pow` recovers the input. - // - // Because we know `n > 1`, and `x > 1`, and x^n is monotonically increasing, we know that - // `checked_int_pow(lo, n) < x < checked_int_pow(hi, n)`. We will preserve this as an - // invariant. - while (lo < hi) { - long double mid = lo + (hi - lo) / 2; - - auto result = checked_int_pow(mid, n); - - if (!result.has_value()) { - return std::nullopt; - } - - // Early return if we get lucky with an exact answer. - if (result.value() == xld) { - return static_cast(mid); - } - - // Check for stagnation. - if (mid == lo || mid == hi) { - break; - } - - // Preserve the invariant that `checked_int_pow(lo, n) < x < checked_int_pow(hi, n)`. - if (result.value() < xld) { - lo = mid; - } else { - hi = mid; - } - } - - // Pick whichever one gets closer to the target. - const auto lo_diff = xld - checked_int_pow(lo, n).value(); - const auto hi_diff = checked_int_pow(hi, n).value() - xld; - return static_cast(lo_diff < hi_diff ? lo : hi); -} - template [[nodiscard]] consteval widen_t compute_base_power(MagnitudeSpec auto el) { @@ -455,132 +207,6 @@ template } } -// A converter for the value member variable of magnitude (below). -// -// The input is the desired result, but in a (wider) intermediate type. The point of this function -// is to cast to the desired type, but avoid overflow in doing so. -template - requires(!std::is_integral_v || std::is_integral_v) -[[nodiscard]] consteval To checked_static_cast(From x) -{ - // This function should only ever be called at compile time. The purpose of these terminations is - // to produce compiler errors, because we cannot `static_assert` on function arguments. - if constexpr (std::is_integral_v) { - if (!std::in_range(x)) { - std::abort(); // Cannot represent magnitude in this type - } - } - - return static_cast(x); -} - -} // namespace detail - - -// A variety of implementation detail helpers. -namespace detail { - -// The exponent of `factor` in the prime factorization of `n`. -[[nodiscard]] consteval std::intmax_t multiplicity(std::intmax_t factor, std::intmax_t n) -{ - std::intmax_t m = 0; - while (n % factor == 0) { - n /= factor; - ++m; - } - return m; -} - -// Divide a number by a given base raised to some power. -// -// Undefined unless base > 1, pow >= 0, and (base ^ pow) evenly divides n. -// NOLINTNEXTLINE(bugprone-easily-swappable-parameters) -[[nodiscard]] consteval std::intmax_t remove_power(std::intmax_t base, std::intmax_t pow, std::intmax_t n) -{ - while (pow-- > 0) { - n /= base; - } - return n; -} - -// A way to check whether a number is prime at compile time. -// [[nodiscard]] consteval bool is_prime(std::intmax_t n) -// { -// return (n >= 0) && factorizer::is_prime(static_cast(n)); -// } - -// template -// [[nodiscard]] consteval bool is_valid_element(Element element) -// { -// if (get_exponent(element) == 0) { -// return false; -// } -// if constexpr (std::integral) { -// // Some prime numbers are so big, that we can't check their primality without exhausting limits on constexpr -// steps -// // and/or iterations. We can still _perform_ the factorization for these by using the `known_first_factor` -// // workaround. However, we can't _check_ that they are prime, because this workaround depends on the input being -// // usable in a constexpr expression. This is true for `prime_factorization` (below), where the input `N` is a -// // template parameter, but is not true for our case, where the input `bp.get_base_value()` is a function -// parameter. (See -// // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1045r1.html for some background reading on this -// // distinction.) -// // -// // In our case: we simply give up on excluding every possible ill-formed base power, and settle for catching the -// // most likely and common mistakes. -// if (const bool too_big_to_check = (get_base_value(element) > 1'000'000'000)) { -// return true; -// } - -// return is_prime(get_base_value(element)); -// } else { -// return get_base_value(element) > 0; -// } -// } - -// A function object to apply a predicate to all consecutive pairs of values in a sequence. -// template -// struct pairwise_all { -// Predicate predicate; - -// template -// [[nodiscard]] consteval bool operator()(Ts&&... ts) const -// { -// // Carefully handle different sizes, avoiding unsigned integer underflow. -// constexpr auto num_comparisons = [](auto num_elements) { -// return (num_elements > 1) ? (num_elements - 1) : 0; -// }(sizeof...(Ts)); - -// // Compare zero or more pairs of neighbours as needed. -// return [this](std::tuple && t, std::index_sequence) -// { -// return (predicate(std::get(t), std::get(t)) && ...); -// } -// (std::make_tuple(std::forward(ts)...), std::make_index_sequence()); -// } -// }; - -// Deduction guide: permit constructions such as `pairwise_all{std::less{}}`. -// template -// pairwise_all(T) -> pairwise_all; - -// Check whether a sequence of (possibly heterogeneously typed) values are strictly increasing. -// template -// requires(std::is_signed_v && ...) -// [[nodiscard]] consteval bool strictly_increasing(Ts&&... ts) -// { -// return pairwise_all{std::less{}}(std::forward(ts)...); -// } - -// template -// constexpr bool all_elements_valid = (is_valid_element(Elements) && ...); - -// template -// constexpr bool all_elements_in_order = strictly_increasing(get_base_value(Elements)...); - -// template -// constexpr bool is_element_pack_valid = all_elements_valid && all_elements_in_order; - [[nodiscard]] consteval bool is_rational(MagnitudeSpec auto element) { static_assert(!Magnitude); // magnitudes are handles by another overload @@ -597,53 +223,98 @@ namespace detail { // Magnitude product implementation. [[nodiscard]] consteval bool less(MagnitudeSpec auto lhs, MagnitudeSpec auto rhs) { - using lhs_base_t = decltype(get_base_value(lhs)); - using rhs_base_t = decltype(get_base_value(rhs)); - - if constexpr (is_named_magnitude && is_named_magnitude) - return detail::type_name() < detail::type_name(); - else if constexpr (!is_named_magnitude && !is_named_magnitude) - return get_base_value(lhs) < get_base_value(rhs); - else - return is_named_magnitude; + return get_base_value(lhs) < get_base_value(rhs); } -template -[[nodiscard]] consteval Magnitude auto multiply_impl(magnitude, magnitude) -{ - using namespace detail; +// The largest integer which can be extracted from any magnitude with only a single basis vector. +template +[[nodiscard]] consteval auto integer_part(magnitude); +[[nodiscard]] consteval std::intmax_t integer_part(ratio r) { return r.num / r.den; } - if constexpr (less(H1, H2)) { - if constexpr (sizeof...(T1) == 0) { - // Shortcut for the "pure prepend" case, which makes it easier to implement some of the other cases. - return magnitude{}; - } else { - return magnitude

{} * (magnitude{} * magnitude{}); - } - } else if constexpr (less(H2, H1)) { - return magnitude

{} * (magnitude{} * magnitude{}); - } else { - if constexpr (is_same_v) { - constexpr auto partial_product = magnitude{} * magnitude{}; - if constexpr (get_exponent(H1) + get_exponent(H2) == 0) { - return partial_product; - } else { - // Make a new power_v with the common base of H1 and H2, whose power is their powers' sum. - constexpr auto new_head = power_v_or_T(); +template +[[nodiscard]] consteval auto remove_positive_power(magnitude m); - if constexpr (get_exponent(new_head) == 0) { +inline constexpr symbol_text base_multiplier(u8"× 10", "x 10"); + +template + requires detail::gt_zero +[[nodiscard]] consteval Magnitude auto mag_power_lazy(); + +template +struct magnitude_base {}; + +template +struct magnitude_base> { + template + [[nodiscard]] friend consteval Magnitude auto _multiply_impl(magnitude, magnitude) + { + if constexpr (less(H, H2)) { + if constexpr (sizeof...(T) == 0) { + // Shortcut for the "pure prepend" case, which makes it easier to implement some of the other cases. + return magnitude{}; + } else { + return magnitude{} * (magnitude{} * magnitude{}); + } + } else if constexpr (less(H2, H)) { + return magnitude

{} * (magnitude{} * magnitude{}); + } else { + if constexpr (is_same_v) { + constexpr auto partial_product = magnitude{} * magnitude{}; + if constexpr (get_exponent(H) + get_exponent(H2) == 0) { return partial_product; } else { - return magnitude{} * partial_product; + // Make a new power_v with the common base of H and H2, whose power is their powers' sum. + constexpr auto new_head = power_v_or_T(); + + if constexpr (get_exponent(new_head) == 0) { + return partial_product; + } else { + return magnitude{} * partial_product; + } } } - } else if constexpr (is_named_magnitude) { - return magnitude

{} * (magnitude{} * magnitude{}); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Common Magnitude. + // + // The "common Magnitude" C, of two Magnitudes M1 and M2, is the largest Magnitude such that each of its inputs is + // expressible by only positive basis powers relative to C. That is, both (M1 / C) and (M2 / C) contain only positive + // powers in the expansion on our basis. + // + // For rational Magnitudes (or, more precisely, Magnitudes that are rational _relative to each other_), this reduces + // to the familiar convention from the std::chrono library: it is the largest Magnitude C such that each input + // Magnitude is an _integer multiple_ of C. The connection can be seen by considering the definition in the above + // paragraph, and recognizing that both the bases and the powers are all integers for rational Magnitudes. + // + // For relatively _irrational_ Magnitudes (whether from irrational bases, or fractional powers of integer bases), the + // notion of a "common type" becomes less important, because there is no way to preserve pure integer multiplication. + // When we go to retrieve our value, we'll be stuck with a floating point approximation no matter what choice we make. + // Thus, we make the _simplest_ choice which reproduces the correct convention in the rational case: namely, taking + // the minimum power for each base (where absent bases implicitly have a power of 0). + template + [[nodiscard]] friend consteval auto _common_magnitude(magnitude, magnitude) + { + using detail::remove_positive_power; + + if constexpr (detail::get_base_value(H) < detail::get_base_value(H2)) { + // When H1 has the smaller base, prepend to result from recursion. + return remove_positive_power(magnitude{}) * _common_magnitude(magnitude{}, magnitude{}); + } else if constexpr (detail::get_base_value(H2) < detail::get_base_value(H)) { + // When H2 has the smaller base, prepend to result from recursion. + return remove_positive_power(magnitude

{}) * _common_magnitude(magnitude{}, magnitude{}); } else { - return magnitude

{} * (magnitude{} * magnitude{}); + // When the bases are equal, pick whichever has the lower power. + constexpr auto common_tail = _common_magnitude(magnitude{}, magnitude{}); + if constexpr (detail::get_exponent(H) < detail::get_exponent(H2)) { + return magnitude{} * common_tail; + } else { + return magnitude

{} * common_tail; + } } } -} +}; } // namespace detail @@ -655,8 +326,7 @@ template * rational powers, and compare for equality. */ template -// requires detail::is_element_pack_valid -struct magnitude { +struct magnitude : detail::magnitude_base> { [[nodiscard]] friend consteval bool is_integral(const magnitude&) { using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec @@ -677,7 +347,7 @@ struct magnitude { else if constexpr (is_same_v>) return m1; else - return detail::multiply_impl(m1, m2); + return _multiply_impl(m1, m2); } [[nodiscard]] friend consteval auto operator/(magnitude l, Magnitude auto r) { return l * pow<-1>(r); } @@ -699,74 +369,102 @@ struct magnitude { constexpr T result = detail::checked_static_cast((detail::compute_base_power(Ms) * ... * T{1})); return result; } -}; - - -namespace detail { -template -void to_base_specialization_of_magnitude(const volatile magnitude*); - -template -constexpr bool is_derived_from_specialization_of_magnitude = - requires(T* type) { to_base_specialization_of_magnitude(type); }; - -template - requires is_derived_from_specialization_of_magnitude -constexpr bool is_magnitude = true; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Magnitude rational powers implementation. + template + [[nodiscard]] friend consteval auto pow(magnitude) + { + if constexpr (Num == 0) { + return magnitude<>{}; + } else { + return magnitude< + detail::power_v_or_T()...>{}; + } + } -template -constexpr bool is_specialization_of_magnitude> = true; + [[nodiscard]] friend consteval auto sqrt(magnitude m) { return pow<1, 2>(m); } + [[nodiscard]] friend consteval auto cbrt(magnitude m) { return pow<1, 3>(m); } -} // namespace detail +private: + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Magnitude numerator and denominator implementation. + [[nodiscard]] friend consteval auto _numerator(magnitude) + { + return (detail::integer_part(magnitude{}) * ... * magnitude<>{}); + } -MP_UNITS_EXPORT_BEGIN + [[nodiscard]] friend consteval auto _denominator(magnitude) { return _numerator(pow<-1>(magnitude{})); } -/** - * @brief A convenient Magnitude constant for pi, which we can manipulate like a regular number. - */ -#if MP_UNITS_COMP_CLANG -inline constexpr struct mag_pi : magnitude}> { -} mag_pi; + [[nodiscard]] friend consteval auto _remove_positive_powers(magnitude) + { + return (magnitude<>{} * ... * detail::remove_positive_power(magnitude{})); + } -#else + [[nodiscard]] friend consteval auto _common_magnitude_type_impl(magnitude) + { + return (std::intmax_t{} * ... * decltype(detail::get_base_value(Ms)){}); + } -inline constexpr struct mag_pi : magnitude> { -} mag_pi; + template + [[nodiscard]] friend consteval detail::ratio _get_power(T base, magnitude) + { + return ((detail::get_base_value(Ms) == base ? detail::get_exponent(Ms) : detail::ratio{0}) + ... + + detail::ratio{0}); + } -#endif + [[nodiscard]] friend consteval std::intmax_t _extract_power_of_10(magnitude m) + { + const auto power_of_2 = _get_power(2, m); + const auto power_of_5 = _get_power(5, m); -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Magnitude rational powers implementation. + if ((power_of_2 * power_of_5).num <= 0) return 0; -template -[[nodiscard]] consteval auto pow(magnitude) -{ - if constexpr (Num == 0) { - return magnitude<>{}; - } else { - return magnitude< - detail::power_v_or_T()...>{}; + return detail::integer_part((detail::abs(power_of_2) < detail::abs(power_of_5)) ? power_of_2 : power_of_5); } -} - -template -[[nodiscard]] consteval auto sqrt(magnitude m) -{ - return pow<1, 2>(m); -} -template -[[nodiscard]] consteval auto cbrt(magnitude m) -{ - return pow<1, 3>(m); -} + [[nodiscard]] friend consteval auto _magnitude_text(magnitude) + { + constexpr auto exp10 = _extract_power_of_10(magnitude{}); + + constexpr Magnitude auto base = magnitude{} / detail::mag_power_lazy<10, exp10>(); + constexpr Magnitude auto num = _numerator(base); + constexpr Magnitude auto den = _denominator(base); + // TODO address the below + static_assert(base == num / den, "Printing rational powers, or irrational bases, not yet supported"); + + constexpr auto num_value = get_value(num); + constexpr auto den_value = get_value(den); + + if constexpr (num_value == 1 && den_value == 1 && exp10 != 0) { + return detail::base_multiplier + detail::superscript(); + } else if constexpr (num_value != 1 || den_value != 1 || exp10 != 0) { + auto txt = symbol_text("[") + detail::regular(); + if constexpr (den_value == 1) { + if constexpr (exp10 == 0) { + return txt + symbol_text("]"); + } else { + return txt + symbol_text(" ") + detail::base_multiplier + detail::superscript() + symbol_text("]"); + } + } else { + if constexpr (exp10 == 0) { + return txt + symbol_text("/") + detail::regular() + symbol_text("]"); + } else { + return txt + symbol_text("/") + detail::regular() + symbol_text(" ") + detail::base_multiplier + + detail::superscript() + symbol_text("]"); + } + } + } else { + return symbol_text(""); + } + } +}; -MP_UNITS_EXPORT_END +[[nodiscard]] consteval auto _common_magnitude(magnitude<>, Magnitude auto m) { return _remove_positive_powers(m); } +[[nodiscard]] consteval auto _common_magnitude(Magnitude auto m, magnitude<>) { return _remove_positive_powers(m); } +[[nodiscard]] consteval auto _common_magnitude(magnitude<> m, magnitude<>) { return m; } -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Magnitude numerator and denominator implementation. namespace detail { @@ -785,32 +483,6 @@ template } } -template -[[nodiscard]] consteval auto numerator(magnitude) -{ - return (detail::integer_part(magnitude{}) * ... * magnitude<>{}); -} - -[[nodiscard]] consteval auto denominator(Magnitude auto m) { return numerator(pow<-1>(m)); } - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Common Magnitude. -// -// The "common Magnitude" C, of two Magnitudes M1 and M2, is the largest Magnitude such that each of its inputs is -// expressible by only positive basis powers relative to C. That is, both (M1 / C) and (M2 / C) contain only positive -// powers in the expansion on our basis. -// -// For rational Magnitudes (or, more precisely, Magnitudes that are rational _relative to each other_), this reduces to -// the familiar convention from the std::chrono library: it is the largest Magnitude C such that each input Magnitude is -// an _integer multiple_ of C. The connection can be seen by considering the definition in the above paragraph, and -// recognizing that both the bases and the powers are all integers for rational Magnitudes. -// -// For relatively _irrational_ Magnitudes (whether from irrational bases, or fractional powers of integer bases), the -// notion of a "common type" becomes less important, because there is no way to preserve pure integer multiplication. -// When we go to retrieve our value, we'll be stuck with a floating point approximation no matter what choice we make. -// Thus, we make the _simplest_ choice which reproduces the correct convention in the rational case: namely, taking the -// minimum power for each base (where absent bases implicitly have a power of 0). - template [[nodiscard]] consteval auto remove_positive_power(magnitude m) { @@ -821,55 +493,9 @@ template } } -template -[[nodiscard]] consteval auto remove_positive_powers(magnitude) -{ - return (magnitude<>{} * ... * remove_positive_power(magnitude{})); -} - -// Base cases, for when either (or both) inputs are the identity. -[[nodiscard]] consteval auto common_magnitude(magnitude<>, magnitude<>) { return magnitude<>{}; } -[[nodiscard]] consteval auto common_magnitude(magnitude<>, Magnitude auto m) -{ - return detail::remove_positive_powers(m); -} -[[nodiscard]] consteval auto common_magnitude(Magnitude auto m, magnitude<>) -{ - return detail::remove_positive_powers(m); -} - -// Recursive case for the common Magnitude of any two non-identity Magnitudes. -template -[[nodiscard]] consteval auto common_magnitude(magnitude, magnitude) -{ - using detail::remove_positive_power; - - if constexpr (detail::get_base_value(H1) < detail::get_base_value(H2)) { - // When H1 has the smaller base, prepend to result from recursion. - return remove_positive_power(magnitude

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

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

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

{} * common_tail; - } - } -} - -template -[[nodiscard]] consteval auto common_magnitude_type_impl(magnitude) -{ - return (std::intmax_t{} * ... * decltype(get_base_value(Ms)){}); -} - // Returns the most precise type to express the magnitude factor template -using common_magnitude_type = decltype(common_magnitude_type_impl(M)); +using common_magnitude_type = decltype(_common_magnitude_type_impl(M)); } // namespace detail @@ -920,85 +546,59 @@ constexpr auto prime_factorization_v = prime_factorization::value; } // namespace detail -/** - * @brief Convert any positive integer to a Magnitude. - * - * This will be the main way end users create Magnitudes. They should rarely (if ever) create a magnitude<...> by - * manually adding base powers. - */ -MP_UNITS_EXPORT template - requires detail::gt_zero -constexpr Magnitude auto mag = detail::prime_factorization_v; +template +concept MagArg = std::integral || MagConstant; + +namespace detail { + +template +[[nodiscard]] consteval Magnitude auto make_magnitude() +{ + if constexpr (MagConstant) + return magnitude{}; + else + return prime_factorization_v; +} + +} // namespace detail + +MP_UNITS_EXPORT_BEGIN + +template + requires detail::gt_zero +constexpr Magnitude auto mag = detail::make_magnitude(); -MP_UNITS_EXPORT template +template requires detail::gt_zero constexpr Magnitude auto mag_ratio = detail::prime_factorization_v / detail::prime_factorization_v; /** * @brief Create a Magnitude which is some rational number raised to a rational power. */ -MP_UNITS_EXPORT template +template requires detail::gt_zero constexpr Magnitude auto mag_power = pow(mag); -namespace detail { - -template -[[nodiscard]] consteval ratio get_power(T base, magnitude) -{ - return ((get_base_value(Ms) == base ? get_exponent(Ms) : ratio{0}) + ... + ratio{0}); -} - -[[nodiscard]] consteval std::intmax_t integer_part(ratio r) { return r.num / r.den; } - -[[nodiscard]] consteval std::intmax_t extract_power_of_10(Magnitude auto m) -{ - const auto power_of_2 = get_power(2, m); - const auto power_of_5 = get_power(5, m); - - if ((power_of_2 * power_of_5).num <= 0) return 0; +/** + * @brief A convenient Magnitude constant for pi, which we can manipulate like a regular number. + */ +struct pi : mag_constant { + static constexpr auto value = std::numbers::pi_v; +}; +inline constexpr pi pi; - return integer_part((detail::abs(power_of_2) < detail::abs(power_of_5)) ? power_of_2 : power_of_5); -} +MP_UNITS_EXPORT_END -inline constexpr symbol_text base_multiplier(u8"× 10", "x 10"); +namespace detail { -template -[[nodiscard]] consteval auto magnitude_text() +// This is introduced to break the dependency cycle between `magnitude::_magnitude_text` and `prime_factorization` +template + requires detail::gt_zero +[[nodiscard]] consteval Magnitude auto mag_power_lazy() { - constexpr auto exp10 = extract_power_of_10(M); - - constexpr Magnitude auto base = M / mag_power<10, exp10>; - constexpr Magnitude auto num = numerator(base); - constexpr Magnitude auto den = denominator(base); - // TODO address the below - static_assert(base == num / den, "Printing rational powers, or irrational bases, not yet supported"); - - constexpr auto num_value = get_value(num); - constexpr auto den_value = get_value(den); - - if constexpr (num_value == 1 && den_value == 1 && exp10 != 0) { - return base_multiplier + superscript(); - } else if constexpr (num_value != 1 || den_value != 1 || exp10 != 0) { - auto txt = symbol_text("[") + regular(); - if constexpr (den_value == 1) { - if constexpr (exp10 == 0) { - return txt + symbol_text("]"); - } else { - return txt + symbol_text(" ") + base_multiplier + superscript() + symbol_text("]"); - } - } else { - if constexpr (exp10 == 0) { - return txt + symbol_text("/") + regular() + symbol_text("]"); - } else { - return txt + symbol_text("/") + regular() + symbol_text(" ") + base_multiplier + - superscript() + symbol_text("]"); - } - } - } else { - return symbol_text(""); - } + return pow(mag); } } // namespace detail + } // namespace mp_units diff --git a/src/core/include/mp-units/framework/unit.h b/src/core/include/mp-units/framework/unit.h index 332586eb68..ec47afc96b 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -654,7 +654,7 @@ template else if constexpr (is_integral(canonical_rhs.mag / canonical_lhs.mag)) return u1; else { - constexpr auto common_magnitude = detail::common_magnitude(canonical_lhs.mag, canonical_rhs.mag); + constexpr auto common_magnitude = _common_magnitude(canonical_lhs.mag, canonical_rhs.mag); return scaled_unit{}; } } @@ -741,7 +741,7 @@ constexpr Out unit_symbol_impl(Out out, const scaled_unit_impl& u, const u // no ratio/prefix return unit_symbol_impl(out, u.reference_unit, fmt, negative_power); } else { - constexpr auto mag_txt = magnitude_text(); + constexpr auto mag_txt = _magnitude_text(M); out = copy(mag_txt, fmt.encoding, out); if constexpr (space_before_unit_symbol::reference_unit>) *out++ = ' '; diff --git a/src/systems/include/mp-units/systems/angular/units.h b/src/systems/include/mp-units/systems/angular/units.h index 25709fd95f..9a9a766f49 100644 --- a/src/systems/include/mp-units/systems/angular/units.h +++ b/src/systems/include/mp-units/systems/angular/units.h @@ -41,7 +41,7 @@ QUANTITY_SPEC(angle, dim_angle); QUANTITY_SPEC(solid_angle, pow<2>(angle)); inline constexpr struct radian final : named_unit<"rad", kind_of> {} radian; -inline constexpr struct revolution final : named_unit<"rev", mag<2> * mag_pi * radian> {} revolution; +inline constexpr struct revolution final : named_unit<"rev", mag<2> * mag * radian> {} revolution; inline constexpr struct degree final : named_unit * revolution> {} degree; inline constexpr struct gradian final : named_unit * revolution> {} gradian; inline constexpr struct steradian final : named_unit<"sr", square(radian)> {} steradian; diff --git a/src/systems/include/mp-units/systems/si/chrono.h b/src/systems/include/mp-units/systems/si/chrono.h index 0ded9689fc..f809f7b500 100644 --- a/src/systems/include/mp-units/systems/si/chrono.h +++ b/src/systems/include/mp-units/systems/si/chrono.h @@ -122,7 +122,7 @@ namespace detail { [[nodiscard]] constexpr auto as_ratio(Magnitude auto m) requires(is_rational(m)) { - return std::ratio(numerator(m)), get_value(denominator(m))>{}; + return std::ratio(_numerator(m)), get_value(_denominator(m))>{}; } } // namespace detail diff --git a/src/systems/include/mp-units/systems/si/constants.h b/src/systems/include/mp-units/systems/si/constants.h index a43c871c64..f032bf7482 100644 --- a/src/systems/include/mp-units/systems/si/constants.h +++ b/src/systems/include/mp-units/systems/si/constants.h @@ -57,7 +57,7 @@ inline constexpr struct luminous_efficacy final : inline constexpr struct standard_gravity final : named_unit * metre / square(second)> {} standard_gravity; inline constexpr struct magnetic_constant final : - named_unit * mag_pi * mag_power<10, -7> * henry / metre> {} magnetic_constant; + named_unit * mag * mag_power<10, -7> * henry / metre> {} magnetic_constant; // clang-format on } // namespace mp_units::si diff --git a/src/systems/include/mp-units/systems/si/units.h b/src/systems/include/mp-units/systems/si/units.h index af14f2df47..54130dd00e 100644 --- a/src/systems/include/mp-units/systems/si/units.h +++ b/src/systems/include/mp-units/systems/si/units.h @@ -100,7 +100,7 @@ inline constexpr struct minute final : named_unit<"min", mag<60> * si::second> { inline constexpr struct hour final : named_unit<"h", mag<60> * minute> {} hour; inline constexpr struct day final : named_unit<"d", mag<24> * hour> {} day; inline constexpr struct astronomical_unit final : named_unit<"au", mag<149'597'870'700> * si::metre> {} astronomical_unit; -inline constexpr struct degree final : named_unit * si::radian> {} degree; +inline constexpr struct degree final : named_unit / mag<180> * si::radian> {} degree; inline constexpr struct arcminute final : named_unit * degree> {} arcminute; inline constexpr struct arcsecond final : named_unit * arcminute> {} arcsecond; inline constexpr struct are final : named_unit<"a", square(si::deca)> {} are; diff --git a/test/runtime/truncation_test.cpp b/test/runtime/truncation_test.cpp index 3ff1004679..0944210d4a 100644 --- a/test/runtime/truncation_test.cpp +++ b/test/runtime/truncation_test.cpp @@ -42,11 +42,11 @@ using namespace mp_units; using namespace mp_units::angular; using namespace mp_units::angular::unit_symbols; -inline constexpr struct half_revolution final : named_unit<"hrev", mag_pi * radian> { +inline constexpr struct half_revolution final : named_unit<"hrev", mag * radian> { } half_revolution; inline constexpr auto hrev = half_revolution; -// constexpr auto revb6 = mag_ratio<1,3> * mag_pi * rad; +// constexpr auto revb6 = mag_ratio<1,3> * mag * rad; TEST_CASE("value_cast should not truncate for valid inputs", "[value_cast]") { diff --git a/test/static/magnitude_test.cpp b/test/static/magnitude_test.cpp index ab82cbfc90..329ac016ed 100644 --- a/test/static/magnitude_test.cpp +++ b/test/static/magnitude_test.cpp @@ -216,9 +216,9 @@ static_assert(std::is_same_v{})), mag_2_> // SECTION("pi to the 1 supplies correct values") // { -// check_same_type_and_value(get_value(mag_pi), std::numbers::pi_v); -// check_same_type_and_value(get_value(mag_pi), std::numbers::pi_v); -// check_same_type_and_value(get_value(mag_pi), std::numbers::pi_v); +// check_same_type_and_value(get_value(mag), std::numbers::pi_v); +// check_same_type_and_value(get_value(mag), std::numbers::pi_v); +// check_same_type_and_value(get_value(mag), std::numbers::pi_v); // } // SECTION("pi to arbitrary power performs computations in most accurate type at compile time") diff --git a/test/static/unit_test.cpp b/test/static/unit_test.cpp index ba7ecb4c73..33cf200b66 100644 --- a/test/static/unit_test.cpp +++ b/test/static/unit_test.cpp @@ -74,7 +74,7 @@ inline constexpr struct degree_Celsius_ final : named_unit * second> {} minute; inline constexpr struct hour_ final : named_unit<"h", mag<60> * minute> {} hour; -inline constexpr struct degree_ final : named_unit * radian> {} degree; +inline constexpr struct degree_ final : named_unit / mag<180> * radian> {} degree; inline constexpr struct yard_ final : named_unit<"yd", mag_ratio<9'144, 10'000> * metre> {} yard; inline constexpr struct mile_ final : named_unit<"mi", mag<1760> * yard> {} mile; @@ -140,7 +140,7 @@ static_assert(get_canonical_unit(radian).mag == mag<1>); static_assert(is_of_type); static_assert(is_of_type); -static_assert(get_canonical_unit(degree).mag == mag_pi / mag<180>); +static_assert(get_canonical_unit(degree).mag == mag / mag<180>); static_assert(convertible(radian, degree)); static_assert(radian != degree); From dca6bc35555ba3f52ed7bd0588a5bf664e9dd7fa Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Mon, 23 Sep 2024 14:17:49 +0200 Subject: [PATCH 034/213] refactor: unnecessary custom versions of `is_specialization_of` removed --- .../mp-units/framework/expression_template.h | 8 +------- .../mp-units/framework/quantity_spec_concepts.h | 8 +------- .../include/mp-units/framework/reference_concepts.h | 13 +------------ src/core/include/mp-units/framework/unit.h | 8 +------- 4 files changed, 4 insertions(+), 33 deletions(-) diff --git a/src/core/include/mp-units/framework/expression_template.h b/src/core/include/mp-units/framework/expression_template.h index 45d3d4b6d5..5195000c2b 100644 --- a/src/core/include/mp-units/framework/expression_template.h +++ b/src/core/include/mp-units/framework/expression_template.h @@ -56,12 +56,6 @@ struct per {}; namespace detail { -template -constexpr bool is_specialization_of_per = false; - -template -constexpr bool is_specialization_of_per> = true; - template constexpr bool valid_ratio = true; @@ -312,7 +306,7 @@ template typename OneType, typename List> else { using last_element = type_list_back; - if constexpr (is_specialization_of_per) { + if constexpr (is_specialization_of) { if constexpr (size == 2 && OneType>::value) return expr_fractions_result, type_list_map>{}; else { diff --git a/src/core/include/mp-units/framework/quantity_spec_concepts.h b/src/core/include/mp-units/framework/quantity_spec_concepts.h index 545b1875c7..9712a434d4 100644 --- a/src/core/include/mp-units/framework/quantity_spec_concepts.h +++ b/src/core/include/mp-units/framework/quantity_spec_concepts.h @@ -53,13 +53,7 @@ struct kind_of_; namespace detail { template -constexpr bool is_specialization_of_kind_of = false; - -template -constexpr bool is_specialization_of_kind_of> = true; - -template -concept QuantityKindSpec = is_specialization_of_kind_of; +concept QuantityKindSpec = is_specialization_of; #if MP_UNITS_API_NO_CRTP template diff --git a/src/core/include/mp-units/framework/reference_concepts.h b/src/core/include/mp-units/framework/reference_concepts.h index ffed704b84..abebe0c85f 100644 --- a/src/core/include/mp-units/framework/reference_concepts.h +++ b/src/core/include/mp-units/framework/reference_concepts.h @@ -32,17 +32,6 @@ namespace mp_units { MP_UNITS_EXPORT template struct reference; -namespace detail { - -// do not refactor below to a variable template - GCC-11 does not like it -template -struct is_specialization_of_reference : std::false_type {}; - -template -struct is_specialization_of_reference> : std::true_type {}; - -} // namespace detail - MP_UNITS_EXPORT_BEGIN [[nodiscard]] consteval QuantitySpec auto get_quantity_spec(AssociatedUnit auto u); @@ -67,7 +56,7 @@ template * Satisfied by all specializations of @c reference. */ template -concept Reference = AssociatedUnit || detail::is_specialization_of_reference::value; +concept Reference = AssociatedUnit || is_specialization_of; /** * @brief A concept matching all references with provided quantity spec diff --git a/src/core/include/mp-units/framework/unit.h b/src/core/include/mp-units/framework/unit.h index ec47afc96b..4446ec3b1c 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -533,12 +533,6 @@ template return canonical_unit{num.mag / den.mag, num.reference_unit / den.reference_unit}; } -template -constexpr bool is_specialization_of_derived_unit = false; - -template -constexpr bool is_specialization_of_derived_unit> = true; - } // namespace detail MP_UNITS_EXPORT_BEGIN @@ -564,7 +558,7 @@ template return u; else if constexpr (detail::is_specialization_of_scaled_unit) return scaled_unit(U::mag), decltype(pow(U::reference_unit))>{}; - else if constexpr (detail::is_specialization_of_derived_unit) + else if constexpr (is_specialization_of) return detail::expr_pow(u); else if constexpr (Den == 1) return derived_unit>{}; From 0b265f362d7e580ad3f899689092c056b8d85561 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Mon, 23 Sep 2024 14:25:33 +0200 Subject: [PATCH 035/213] refactor: `is_derived_from_specialization_of_v` added and applied to remove custom traits --- src/core/include/mp-units/ext/type_traits.h | 6 +++++ .../mp-units/framework/dimension_concepts.h | 9 +------ .../mp-units/framework/quantity_point.h | 24 ++++++++--------- .../framework/quantity_point_concepts.h | 26 +++++-------------- .../mp-units/framework/unit_concepts.h | 13 +--------- 5 files changed, 26 insertions(+), 52 deletions(-) diff --git a/src/core/include/mp-units/ext/type_traits.h b/src/core/include/mp-units/ext/type_traits.h index 2ee78e2ac5..74a14139a3 100644 --- a/src/core/include/mp-units/ext/type_traits.h +++ b/src/core/include/mp-units/ext/type_traits.h @@ -89,11 +89,17 @@ namespace detail { template typename Type, typename... Params> void to_base_specialization_of(const volatile Type*); +template typename Type, auto... Params> +void to_base_specialization_of_v(const volatile Type*); + } // namespace detail template typename Type> constexpr bool is_derived_from_specialization_of = requires(T* t) { detail::to_base_specialization_of(t); }; +template typename Type> +constexpr bool is_derived_from_specialization_of_v = requires(T* t) { detail::to_base_specialization_of_v(t); }; + namespace detail { template diff --git a/src/core/include/mp-units/framework/dimension_concepts.h b/src/core/include/mp-units/framework/dimension_concepts.h index 90330eb29e..68f6f15dee 100644 --- a/src/core/include/mp-units/framework/dimension_concepts.h +++ b/src/core/include/mp-units/framework/dimension_concepts.h @@ -49,20 +49,13 @@ struct base_dimension; namespace detail { -template -void to_base_specialization_of_base_dimension(const volatile base_dimension*); - -template -constexpr bool is_derived_from_specialization_of_base_dimension = - requires(T* type) { to_base_specialization_of_base_dimension(type); }; - /** * @brief A concept matching all named base dimensions in the library. * * Satisfied by all dimension types derived from a specialization of `base_dimension`. */ template -concept BaseDimension = Dimension && is_derived_from_specialization_of_base_dimension; +concept BaseDimension = Dimension && is_derived_from_specialization_of_v; template struct is_dimension_one : std::false_type {}; diff --git a/src/core/include/mp-units/framework/quantity_point.h b/src/core/include/mp-units/framework/quantity_point.h index 074cd8fac5..ba3125440d 100644 --- a/src/core/include/mp-units/framework/quantity_point.h +++ b/src/core/include/mp-units/framework/quantity_point.h @@ -76,13 +76,13 @@ struct point_origin_interface { template PO2> requires QuantitySpecOf, PO2::quantity_spec> && - (detail::is_derived_from_specialization_of_relative_point_origin || - detail::is_derived_from_specialization_of_relative_point_origin) + (is_derived_from_specialization_of_v || + is_derived_from_specialization_of_v) [[nodiscard]] friend constexpr Quantity auto operator-(PO1 po1, PO2 po2) { - if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin) { + if constexpr (is_derived_from_specialization_of_v) { return -(po2.quantity_point - po2.quantity_point.absolute_point_origin); - } else if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin) { + } else if constexpr (is_derived_from_specialization_of_v) { return po1.quantity_point - po1.quantity_point.absolute_point_origin; } else { return po1.quantity_point - po2.quantity_point; @@ -92,16 +92,16 @@ struct point_origin_interface { template [[nodiscard]] friend consteval bool operator==(PO1 po1, PO2 po2) { - if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin && - detail::is_derived_from_specialization_of_absolute_point_origin) + if constexpr (is_derived_from_specialization_of_v && + is_derived_from_specialization_of_v) return is_same_v || (detail::is_zeroth_point_origin(po1) && detail::is_zeroth_point_origin(po2) && interconvertible(po1.quantity_spec, po2.quantity_spec)); - else if constexpr (detail::is_derived_from_specialization_of_relative_point_origin && - detail::is_derived_from_specialization_of_relative_point_origin) + else if constexpr (is_derived_from_specialization_of_v && + is_derived_from_specialization_of_v) return PO1::quantity_point == PO2::quantity_point; - else if constexpr (detail::is_derived_from_specialization_of_relative_point_origin) + else if constexpr (is_derived_from_specialization_of_v) return detail::same_absolute_point_origins(po1, po2) && is_eq_zero(PO1::quantity_point.quantity_from_zero()); - else if constexpr (detail::is_derived_from_specialization_of_relative_point_origin) + else if constexpr (is_derived_from_specialization_of_v) return detail::same_absolute_point_origins(po1, po2) && is_eq_zero(PO2::quantity_point.quantity_from_zero()); } }; @@ -154,7 +154,7 @@ namespace detail { template [[nodiscard]] consteval PointOrigin auto get_absolute_point_origin(PO po) { - if constexpr (is_derived_from_specialization_of_absolute_point_origin) + if constexpr (is_derived_from_specialization_of_v) return po; else return po.quantity_point.absolute_point_origin; @@ -509,7 +509,7 @@ class quantity_point { { if constexpr (point_origin == po) return qp.quantity_ref_from(point_origin); - else if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin) { + else if constexpr (is_derived_from_specialization_of_v) { if constexpr (point_origin == absolute_point_origin) return qp.quantity_ref_from(point_origin); else diff --git a/src/core/include/mp-units/framework/quantity_point_concepts.h b/src/core/include/mp-units/framework/quantity_point_concepts.h index 89f49dbf6e..e1c32f7ffa 100644 --- a/src/core/include/mp-units/framework/quantity_point_concepts.h +++ b/src/core/include/mp-units/framework/quantity_point_concepts.h @@ -40,13 +40,6 @@ namespace detail { template constexpr bool is_quantity_point = false; -template -void to_base_specialization_of_absolute_point_origin(const volatile absolute_point_origin*); - -template -constexpr bool is_derived_from_specialization_of_absolute_point_origin = - requires(T* type) { to_base_specialization_of_absolute_point_origin(type); }; - } // namespace detail /** @@ -62,13 +55,6 @@ struct relative_point_origin; namespace detail { -template -void to_base_specialization_of_relative_point_origin(const volatile relative_point_origin*); - -template -constexpr bool is_derived_from_specialization_of_relative_point_origin = - requires(T* type) { to_base_specialization_of_relative_point_origin(type); }; - struct point_origin_interface; } // namespace detail @@ -109,15 +95,15 @@ constexpr bool is_quantity_point = true; template [[nodiscard]] consteval bool same_absolute_point_origins(PO1 po1, PO2 po2) { - if constexpr (is_derived_from_specialization_of_absolute_point_origin && - is_derived_from_specialization_of_absolute_point_origin) + if constexpr (is_derived_from_specialization_of_v && + is_derived_from_specialization_of_v) return po1 == po2; - else if constexpr (is_derived_from_specialization_of_relative_point_origin && - is_derived_from_specialization_of_relative_point_origin) + else if constexpr (is_derived_from_specialization_of_v && + is_derived_from_specialization_of_v) return po1.absolute_point_origin == po2.absolute_point_origin; - else if constexpr (is_derived_from_specialization_of_relative_point_origin) + else if constexpr (is_derived_from_specialization_of_v) return po1.absolute_point_origin == po2; - else if constexpr (is_derived_from_specialization_of_relative_point_origin) + else if constexpr (is_derived_from_specialization_of_v) return po1 == po2.absolute_point_origin; else return false; diff --git a/src/core/include/mp-units/framework/unit_concepts.h b/src/core/include/mp-units/framework/unit_concepts.h index 92a2199bd6..f752a22ec3 100644 --- a/src/core/include/mp-units/framework/unit_concepts.h +++ b/src/core/include/mp-units/framework/unit_concepts.h @@ -53,22 +53,11 @@ struct named_unit; MP_UNITS_EXPORT struct one; -namespace detail { - -template -void to_base_specialization_of_named_unit(const volatile named_unit*); - -template -constexpr bool is_derived_from_specialization_of_named_unit = - requires(T* t) { to_base_specialization_of_named_unit(t); }; - -} // namespace detail - /** * @brief A concept to be used to define prefixes for a unit */ MP_UNITS_EXPORT template -concept PrefixableUnit = Unit && detail::is_derived_from_specialization_of_named_unit; +concept PrefixableUnit = Unit && is_derived_from_specialization_of_v; namespace detail { From d4861a36fd29420ebfcafa8a0a9fff135f90cf5c Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 24 Sep 2024 08:54:55 +0200 Subject: [PATCH 036/213] docs: CHANGELOG updated --- CHANGELOG.md | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e86989e26..101e2faa81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,80 @@ ### 2.3.0 WIP { id="2.3.0" } - (!) feat: `delta` and `absolute` construction helpers +- (!) feat: Conan and CMake options changed as requested at ConanCenter code review +- (!) feat: `unit_can_be_prefixed` removed - from now on all named units can be prefixed +- feat: formatting functions for units and dimensions marked as `constexpr` to enable compile-time text formatting +- feat: `qp1.quantity_from(qp2)` added +- feat: non-member `swap` added for `fixed_string` +- feat: simplified `inplace_vector` added +- feat: text output for angular units improved (space before symbol removed) +- feat: representation type template parameter added to value conversion functions +- feat: all tags in the expression templates are now ordered by the type names +- feat: convertibility of a quantity with a unit `one` with the raw value added +- feat: `import std;` support added +- feat: compute values for rational magnitude powers (thanks [@chiphogg](https://github.com/chiphogg)) +- feat: `value_cast()` complementary conversion function added +- feat: `hw_voltage` example added +- feat: `MP_UNITS_IMPORT_STD` and `MP_UNITS_MODULES` handled properly in `test_package.cpp` +- feat: `complex` quantity character added +- feat: `iec::var` unit added +- (!) refactor: `quantity_point_like_traits` now use numerical value instead of the quantity +- (!) refactor: `iec80000` system renamed to `iec` +- (!) refactor: `mag_pi` is now `mag` +- refactor: error messages-related improvements +- refactor: `[[nodiscard]]` and `consteval` set for some magnitude-related functions +- refactor: degree Celsius and Fahrenheit symbols text now use Unicode codepoints +- refactor: `dimension_symbol` and `units_symbol` refactored to use `inplace_vector` +- refactor: `unit_symbol` and `dimension_symbol` refactored for readability and `consteval` +- refactor: binary operators of `quantity` and `quantity_point` are now hidden friends +- refactor: tag types-related operators are now hidden friends as well +- refactor: `ValuePreservingTo` concept added +- refactor: perfect forwarding interfaces improved +- refactor: `Representation` concepts now requires `WeaklyRegular` instead of `std::regular` +- refactor: `quantity_point` default-constructibility removed from the `quantity_from` constraints +- refactor: `has_common_type_v` simplified +- refactor: `is_power_of_quantity_spec` and `is_power_of_dim` variable templates converted to concepts +- refactor: unnecessary custom versions of `is_specialization_of` removed +- refactor: `is_derived_from_specialization_of_v` added and applied to remove custom traits +- fix: signatures of capacity functions of `fixed_string` fixed +- fix: `MP_UNITS_API_NO_CRTP` handling fixed +- fix: `MP_UNITS_HOSTED` branch added to `core.h` +- fix: mkdocs dependencies fixed in the gitpod dockerfile +- fix: `MP_UNITS_API_CONTRACTS` should have a priority over headers availability +- fix: `si.h` and `angular.h` now properly include `hacks.h` to define `MP_UNITS_HOSTED` before its usage +- fix: quantity scaling between different prefixes improved +- fix: conversion operator to `std::chrono` types fixed +- fix: `std::format` does not always use `Char*` as iterators +- fix: `complex_power` & co fixed +- test: conversion to chrono unit tests added +- test: unit tests for creating a quantity from a `volatile` variable +- test: increase tolerance for certain math tests to two epsilon by [@burnpanck](https://github.com/burnpanck) +- test: `std::complex`-based quantities tests added +- build: conanfile bumped to use `catch2/3.6.0` +- build: conanfile bumped to use `fmt/11.0.1` +- build: minimum required CMake version bumped to 3.25 +- build: duplicated `cmake_minimum_required` commands removed +- build: minimum conan version set to 2.0.15 +- build: MSVC compilation enabled for the library part by [@czjhoppe](https://github.com/czjhoppe) +- build: Dockerfile updated for gitpod +- build: `generate()` in `test_package` now correctly propagates project's options +- build: `target_include_directories` is not needed anymore +- build: `target_compile_features` now uses `CMAKE_CXX_STANDARD` +- ci: added test for upstream clang on macos-14, as an example for an arm64 platform by [@burnpanck](https://github.com/burnpanck) +- style: pre-commit updated to clang-format-18.1.8 +- docs: "Strong Angular System" chapter added +- docs: docs updated to use new spelling for Conan options +- docs: "Interface Introduction" chapter updated +- docs: St. Louis 2024 report added +- docs: "Radians and degrees support" added +- docs: Rework of "Getting Started" section +- docs: "API Reference" support added (thanks [@JohelEGP](https://github.com/JohelEGP)) +- docs: "Generating API reference" chapter added +- docs: minor formatting and spelling improvements to the "CONTRIBUTING.md" +- docs: "Unit symbols" chapter added +- docs: examples line numbers fixed +- docs: "Text Formatting" tag removed from avg_speed example +- docs: line breaks in mermaid graphs fixed ### 2.2.1 July 3, 2024 { id="2.2.1" } From 488572cbe0e5225e6b710acbd67ab8e4f1689a59 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 24 Sep 2024 09:00:56 +0200 Subject: [PATCH 037/213] docs: `import_std` Conan option marked as experimental --- docs/getting_started/installation_and_usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting_started/installation_and_usage.md b/docs/getting_started/installation_and_usage.md index d9fcbcb61f..24d110a823 100644 --- a/docs/getting_started/installation_and_usage.md +++ b/docs/getting_started/installation_and_usage.md @@ -105,7 +105,7 @@ dependencies by other means, some modifications to the library's CMake files mig [conan C++ modules support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 -[`import_std`](#import_std){ #import_std } +[`import_std`](#import_std){ #import_std } :test_tube:{ title="Experimental" } : [:octicons-tag-24: 2.3.0][conan import std support] · :octicons-milestone-24: `True`/`False` (Default: automatically determined from settings) From 226118852fb52654478723240ad6cc76f7f94606 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 24 Sep 2024 09:02:25 +0200 Subject: [PATCH 038/213] docs: imrpove "Superpowers of the unit `one`" chapter --- .../framework_basics/dimensionless_quantities.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/users_guide/framework_basics/dimensionless_quantities.md b/docs/users_guide/framework_basics/dimensionless_quantities.md index 9547aec1fe..8b4f72e5e4 100644 --- a/docs/users_guide/framework_basics/dimensionless_quantities.md +++ b/docs/users_guide/framework_basics/dimensionless_quantities.md @@ -166,10 +166,11 @@ inline constexpr auto ppm = parts_per_million; ### Superpowers of the unit `one` -Quantities of the unit `one` are the only ones that are implicitly convertible from a raw value -and explicitly convertible to it. This property also expands to usual arithmetic operators. +Quantities of the unit `one` are the only ones that are: -Thanks to the above, we can type: +- implicitly constructible from the raw value, +- explicitly convertible to a raw value, +- comparable to a raw value. ```cpp quantity inc(quantity q) { return q + 1; } @@ -179,6 +180,14 @@ if (auto q = inc(42); q != 0) legacy(static_cast(q)); ``` +This property also expands to usual arithmetic operators. + +!!! note + + Those rules do not apply to all the dimensionless quantities. It would be unsafe and misleading + to allow such operations on units with a magnitude different than `1` (e.g., `percent` or + `radian`). + ## Angular quantities From c05e1bcdb723fb097cbdbec94918062b51ce2de7 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 24 Sep 2024 09:02:48 +0200 Subject: [PATCH 039/213] docs: fix typo in the "Value conversions summary" chapter --- docs/users_guide/framework_basics/value_conversions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/users_guide/framework_basics/value_conversions.md b/docs/users_guide/framework_basics/value_conversions.md index 96181fd21f..2e2c671933 100644 --- a/docs/users_guide/framework_basics/value_conversions.md +++ b/docs/users_guide/framework_basics/value_conversions.md @@ -179,7 +179,7 @@ which may well be outside the range of one or both quantity types. ## Value conversions summary -The table below provides all the value conversions functions that may be run on `x` being the +The table below provides all the value conversion functions that may be run on `x` being the instance of either `quantity` or `quantity_point`: | Forcing | Representation | Unit | Member function | Non-member function | From 5f9a6e4a20b0a766078fb9810379461fe8a7ac6f Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 24 Sep 2024 09:03:06 +0200 Subject: [PATCH 040/213] docs: 2.3.0 release announcement updated --- docs/blog/posts/2.3.0-released.md | 350 +++++++++++++++++++++++++++++- 1 file changed, 342 insertions(+), 8 deletions(-) diff --git a/docs/blog/posts/2.3.0-released.md b/docs/blog/posts/2.3.0-released.md index ab1e87d727..4ce6bd8795 100644 --- a/docs/blog/posts/2.3.0-released.md +++ b/docs/blog/posts/2.3.0-released.md @@ -1,6 +1,6 @@ --- draft: true -date: 2024-06-15 +date: 2024-09-24 authors: - mpusz categories: @@ -13,12 +13,77 @@ categories: [GitHub](https://github.com/mpusz/mp-units/releases/tag/v2.3.0) and [Conan](https://conan.io/center/recipes/mp-units?version=2.3.0).** -This release fine-tunes many key features of the library. This post describes those and a few -other smaller interesting improvements, while a much longer list of the most significant changes -introduced by the new version can be found in our [Release Notes](../../release_notes.md#2.3.0). +This release fine-tunes many key features of the library. This post describes the most interesting +improvements, while a much longer list of the changes introduced by the new version can be found in +our [Release Notes](../../release_notes.md#2.3.0). +## CMake and Conan options changed + +During the review on the ConanCenter, we got feedback that we should improve the handling of +options for which value is automatically determined based on the current configuration. +Instead of explicitly setting the `auto` value, we defer the choice between `True`/`False` until +the configuration stage and set it there once all the settings are known. `auto` value for such +option was removed (:boom: **breaking change** :boom:). + +If you didn't set any value at the command line for such options, everything stays the same for +you. However, some changes are needed if you explicitly used `auto` like below: + +```bash +conan install . -o 'mp-units:std_format=auto' -s compiler.cppstd=23 -b missing +``` + +Now you have to either skip such an option to keep automatic deduction: + +```bash +conan install . -s compiler.cppstd=23 -b missing +``` + +or set it explicitly to `True` or `False` to force a specific configuration: + +```bash +conan install . -o 'mp-units:std_format=True' -s compiler.cppstd=23 -b missing +``` + + +## Representation type template parameter added to value conversion functions + +Previously, changing a representation type was only possible with a `value_cast(q)` +non-member function while a change of unit was supported by all `value_cast(q)`, +`q.in(NewU)`, and `q.force_in(NewU)`. The rationale for it was that passing an explicit type to +a member function template requires a `template` disambiguator when we are dealing with a dependent +name (e.g., `quantity` type is determined based on a template parameter). + +During a discussion in LEWGI at the St. Louis WG21 Meeting, we decided to provide such additional +overloads despite possible issues when a dependent name is used. In such case, a user needs +to provide a `template` disambiguator or switch back to using `value_cast`: + +*[LEWGI]: Library Evolution Working Group Incubator + +```cpp +// non-dependent name +auto f(quantity q) { return q.in(km); } +auto g(quantity q) { return value_cast(q); } + +// dependent name +auto h(QuantityOf auto q) { return q.template in(km); } +auto i(QuantityOf auto q) { return value_cast(q); } +``` + +The table below provides all the value conversion functions in **mp-units** that may be run on +`x` being the instance of either `quantity` or `quantity_point`: + +| Forcing | Representation | Unit | Member function | Non-member function | +|:-------:|:--------------:|:----:|--------------------|------------------------------------------------| +| No | Same | `u` | `x.in(u)` | | +| No | `T` | Same | `x.in()` | | +| No | `T` | `u` | `x.in(u)` | | +| Yes | Same | `u` | `x.force_in(u)` | `value_cast(x)` | +| Yes | `T` | Same | `x.force_in()` | `value_cast(x)` | +| Yes | `T` | `u` | `x.force_in(u)` | `value_cast(x)` or `value_cast(x)` | + + ## Quantity reference specifiers The features described in this chapter directly solve an issue raised on @@ -50,7 +115,7 @@ of the affine space abstractions and how they influence temperature handling. After a lengthy discussion on handling such scenarios, we decided to: -- make the above code ill-formed, +- make the above code ill-formed (:boom: **breaking change** :boom:), - provide an alternative way to create a `quantity` with the `delta` quantity construction helper. Here are the main points of this new design: @@ -94,9 +159,9 @@ Here are the main points of this new design: With such changes to the interface design, the offending code will not compile as initially written. Users will be forced to think more about what they write. To enable the compilation, the users have -to explicitly create a: +to create explicitly: -- `quantity_point` (the intended abstraction in this example) with any of the below syntaxes: +- a `quantity_point` (the intended abstraction in this example) with any of the below syntaxes: ```cpp quantity_point Temperature = absolute(28.0); @@ -104,7 +169,7 @@ to explicitly create a: quantity_point Temperature(delta(28.0)); ``` -- `quantity` (an incorrect abstraction in this example) with: +- a `quantity` (an incorrect abstraction in this example) with: ```cpp quantity Temperature = delta(28.0); @@ -113,3 +178,272 @@ to explicitly create a: Thanks to the new design, we can immediately see what happens here and why the result might be incorrect in the second case. + + +## `quantity_point_like_traits` are based on numerical value instead of a quantity + +In this release, we decided to fine-tune the traits that customize the conversion between custom +quantity point types and the ones provided with **mp-units** (:boom: **breaking change** :boom:). + +Previously, such type traits were based on the `quantity` type. This was inconsistent with +`quantity_like_traits`, that is working on raw values. Also, there are cases where a custom +quantity point abstraction is not modelled with a quantity type. In such cases, the previous +approach required additional types to be introduced for no good reason. + +=== "Now" + + ```cpp + template<> + struct mp_units::quantity_point_like_traits { + static constexpr auto reference = si::second; + static constexpr auto point_origin = default_point_origin(reference); + using rep = decltype(Timestamp::seconds); + + static constexpr convert_implicitly to_numerical_value(Timestamp ts) + { + return ts.seconds; + } + + static constexpr convert_explicitly from_numerical_value(rep v) + { + return Timestamp(v); + } + }; + ``` + +=== "Before" + + ```cpp + template<> + struct mp_units::quantity_point_like_traits { + static constexpr auto reference = si::second; + static constexpr auto point_origin = default_point_origin(reference); + using rep = decltype(Timestamp::seconds); + + static constexpr convert_implicitly> to_quantity(Timestamp ts) + { + return ts.seconds * si::second; + } + + static constexpr convert_explicitly from_quantity(quantity q) + { + return Timestamp(q.numerical_value_ref_in(si::second)); + } + }; + ``` + +## `mag` + +With this release, we introduced a new strongly-typed constant to create a magnitude involving +scaling by `pi`. The solution used before was not consistent with magnitudes of integral values +and also was leaking a floating-point value of `std::numbers::pi_v` to the resulting +magnitude type. With the new approach, this is no longer the case, and the user-facing interface +is more consistent: + +=== "Now" + + ```cpp + inline constexpr struct degree final : named_unit<{u8"°", "deg"}, mag / mag<180> * si::radian> {} degree; + ``` + +=== "Before" + + ```cpp + inline constexpr struct degree final : named_unit<{u8"°", "deg"}, mag_pi / mag<180> * si::radian> {} degree; + ``` + + +## Superpowers of the unit `one` + +In this release, we also added a long-awaited change. From now on a quantity of a unit `one` can be: + +- **implicitly constructed from** the raw value, +- **explicitly converted to** a raw value, +- **compared to** a raw value. + +=== "Now" + + ```cpp + quantity inc(quantity q) { return q + 1; } + void legacy(double) { /* ... */ } + + if (auto q = inc(42); q != 0) + legacy(static_cast(q)); + ``` + +=== "Before" + + ```cpp + quantity inc(quantity q) { return q + 1 * one; } + void legacy(double) { /* ... */ } + + if (auto q = inc(42 * one); q != 0 * one) + legacy(q.numerical_value_in(one)); + ``` + +This property also expands to usual arithmetic operators. + +With the above change, we can now achieve the same results in a terser way: + +=== "Now" + + ```cpp + static_assert(10 * km / (5 * km) == 2); + const quantity gain = 1. / index; + ``` + +=== "Before" + + ```cpp + static_assert(10 * km / (5 * km) == 2 * one); + const quantity gain = 1. / index * one; + ``` + +!!! note + + Those rules do not apply to all the dimensionless quantities. It would be unsafe and misleading + to allow such operations on units with a magnitude different than `1` (e.g., `percent` or + `radian`). + + +## `import std;` support + +This release brings experimental support for `import std;`. The only compiler that supports +it for now is clang-18+. Until all the compilers start to support it and CMake removes +the experimental tag from this feature, we will also keep it experimental. + +As all of the C++ compilers are buggy for now, it is not allowed to bring the same definitions +through the `import std;` and regular header files. This applies not only to the current project +but also to all its dependencies. This is why, in order to use it with **mp-units**, we need to +disable all the dependencies as well (enforced with `conanfile.py`). It means that we have to use +`std::format` (instead of [fmtlib](https://github.com/fmtlib/fmt)) and remove functions contract +checking. + +With the above assumptions, we can refactor our smoot example to: + +```cpp hl_lines="2" +import mp_units; +import std; + +using namespace mp_units; + +inline constexpr struct smoot final : named_unit<"smoot", mag<67> * usc::inch> {} smoot; + +int main() +{ + constexpr quantity dist = 364.4 * smoot; + std::println("Harvard Bridge length = {::N[.1f]} ({::N[.1f]}, {::N[.2f]}) ± 1 εar", + dist, dist.in(usc::foot), dist.in(si::metre)); +} +``` + + +## `unit_can_be_prefixed` removed + +Previously, the `unit_can_be_prefixed` type trait was used to limit the possibility to prefix +some units that are officially known as non-prefixable (e.g., hour, minute). + +It turned out that it is not easy to determine whether some units can be prefixed. For example, +for degree Celsius, the ISO 80000-5 standard explicitly states: + +> Prefixes are not allowed in combination with the unit °C. + +On the other hand [this NIST page](https://www.nist.gov/pml/owm/writing-si-metric-system-units) +says: + +> Prefix symbols may be used with the unit symbol ºC and prefix names may be used with the unit +> name “degree Celsius.” For example, 12 mºC (12 millidegrees Celsius) is acceptable. + +It seems that it is also a [common engineering practice](https://github.com/search?q=repo%3Atorvalds%2Flinux+millidegree&type=code). + +To prevent such issues, we decided to simplify the library's design and remove the +`unit_can_be_prefixed` type trait (:boom: **breaking change** :boom:). + +From now on, every named unit in the library can be prefixed with the SI or IEC prefix. + + +## `iec80000` system renamed to `iec` + +As we mentioned IEC already, in this release, we decided to rename the name of the system and its +corresponding namespace from `iec80000` to `iec` (:boom: **breaking change** :boom:). + +This should be easier to type and is more correct for some quantities and units that are introduced +by IEC but not necessarily in the ISO/IEC 80000 series of documents (e.g., `iec::var`). + + +## Error messages-related improvements + +The readability of compile-time error messages is always a challenge for generic C++ libraries. +However, for quantities and units library, generating readable errors is the most important +requirement. If you do not make errors, you do not need such a library in your project :wink:. + +This is why we put lots of effort into improving here. Besides submitting compiler bugs to improve +on their part, we also try to do our best here. + +Some compilers do not present the type resulting from calling a function within a template +argument. This ends up with statements like `get_quantity_spec(si::second{})` in the error message. +Some less experienced users of the library may not know what this mean, and then why the +conversion error happens. + +To improve this, we injected additional helper concepts into the definitions. It results with +a bit longer but a more readable error in the end. + +For example: + +=== "Now" + + ```text hl_lines="19" + error: no matching member function for call to 'in' + 15 | const quantity time_to_goal = (distance * speed).in(s); + | ~~~~~~~~~~~~~~~~~~~^~ + note: candidate template ignored: constraints not satisfied [with ToU = struct second] + 221 | [[nodiscard]] constexpr QuantityOf auto in(ToU) const + | ^ + note: because 'detail::UnitCompatibleWith' evaluated to false + 219 | template ToU> + | ^ + note: because '!AssociatedUnit' evaluated to false + 164 | (!AssociatedUnit || UnitOf) && detail::UnitConvertibleTo; + | ^ + note: and 'UnitOf, per > >{}>' evaluated to false + 164 | (!AssociatedUnit || UnitOf) && detail::UnitConvertibleTo; + | ^ + note: because 'detail::QuantitySpecConvertibleTo, per > >{}>' evaluated to false + 141 | detail::QuantitySpecConvertibleTo && + | ^ + note: because 'implicitly_convertible(kind_of_{}, kind_of_, per > >{})' evaluated to false + 151 | implicitly_convertible(From, To); + | ^ + 1 error generated. + Compiler returned: 1 + ``` + +=== "Before" + + ```text hl_lines="16" + error: no matching member function for call to 'in' + 15 | const quantity time_to_goal = (distance * speed).in(s); + | ~~~~~~~~~~~~~~~~~~~^~ + note: candidate template ignored: constraints not satisfied [with U = struct second] + 185 | [[nodiscard]] constexpr QuantityOf auto in(U) const + | ^ + note: because 'detail::UnitCompatibleWith' evaluated to false + 183 | template U> + | ^ + note: because '!AssociatedUnit' evaluated to false + 207 | (!AssociatedUnit || UnitOf)&&(detail::have_same_canonical_reference_unit(U{}, U2)); + | ^ + note: and 'UnitOf, per > >{}>' evaluated to false + 207 | (!AssociatedUnit || UnitOf)&&(detail::have_same_canonical_reference_unit(U{}, U2)); + | ^ + note: because 'implicitly_convertible(get_quantity_spec(si::second{}), kind_of_, per