diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a5ae64e..cc38d895 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,12 @@ ### 2.2.0 WIP { id="2.2.0" } - (!) feat: C++ modules support added by [@JohelEGP](https://github.com/JohelEGP) -- (!) feat: formatting grammar improved and units formatting support added +- (!) feat: New formatting specification implemented - (!) feat: `has_unit_symbol` support removed - (!) feat: ABI concerns resolved with introduction of u8 strings for symbols - (!) feat: API-related Conan, CMake, and preprocessor options redesigned -- (!) feat: :boom: `core.h` removed +- (!) feat: `core.h` removed +- (!) feat: from now on units, dimensions, quantity specifications, and point origins have to be marked as `final` - feat: implicit point origins support added - feat: unit default point origin support added - feat: `fma`, `isfinite`, `isinf`, and `isnan` math function added by [@NAThompson](https://github.com/NAThompson) @@ -29,10 +30,10 @@ - feat: unit text output support added - feat: formatting error messages improved - feat: improve types readability by eliminating extraneous `()` in references, prefixes, and `kind_of` -- feat: dimension text output added +- feat: dimension and unit text output added - feat: some light and radiation ISQ quantities added -- feat: New formatting specification implemented - feat: allow configuring GSL library use +- feat: freestanding support added - (!) refactor: `zero_Fahrenheit` renamed to `zeroth_degree_Fahrenheit` - (!) refactor: SI-related trigonometric functions moved to the `si` subnamespace - (!) refactor: `math.h` header file broke up to smaller pieces @@ -47,6 +48,9 @@ - (!) refactor: `framework.h` introduced - (!) refactor: type list tools made an implementation detail of the library - (!) refactor: header files with the entire system definitions moved up in the directory tree +- (!) refactor: `absolute_point_origin` does not use CRTP anymore +- refactor: system's units do not inherit from one another anymore +- refactor: all units made `final` - refactor: math functions constraints refactored - refactor: `si_quantities.h` added to improve compile-times - refactor: `validate_ascii_string` refactored to `is_basic_literal_character_set` @@ -54,6 +58,8 @@ - refactor: code refactored to comply with clang-tidy - refactor: remove dependency on `` header and switch to use an iterator-based `copy` algorithm - refactor: `terminate` replaced with `abort` and a header file added +- refactor: most `std::remove_const_t` removed and some replaced with the GCC-specific workaround +- refactor: not needed `remove_reference_t` and `remove_cvref_t` removed - fix: `QuantityLike` conversions required `Q::rep` instead of using one provided by `quantity_like_traits` - fix: `QuantitySpec[Unit]` replaced with `make_reference` in `value_cast` - fix: `ice_point` is now defined with the integral offset from `absolute_zero` @@ -82,6 +88,7 @@ - docs: "Text Output" chapter updated with the recent formatting changes - docs: formatting grammar language changed to EBNF - docs: "Project structure" chapter expanded +- docs: CITATION.cff updated - (!) build: Conan and CMake options refactored - (!) build: `MP_UNITS_AS_SYSTEM_HEADERS` renamed to `MP_UNITS_BUILD_AS_SYSTEM_HEADERS` - (!) build: `MP_UNITS_BUILD_LA` and `MP_UNITS_IWYU` CMake options now have `_DEV_` in the name @@ -97,6 +104,7 @@ - build(conan): `can_run` check added before running tests - ci: Conan and CMake CI now use different cache names - ci: gcc-14 added +- ci: `clang-tidy` CI added ### 2.1.1 May 16, 2024 { id="2.1.1" } diff --git a/conanfile.py b/conanfile.py index 0d2cb39b..abdebc30 100644 --- a/conanfile.py +++ b/conanfile.py @@ -207,12 +207,13 @@ class MPUnitsConan(ConanFile): self.version = version.strip() def requirements(self): - if self.options.contracts == "gsl-lite": - self.requires("gsl-lite/0.41.0") - elif self.options.contracts == "ms-gsl": - self.requires("ms-gsl/4.0.0") - if self._use_fmtlib and not self.options.freestanding: - self.requires("fmt/10.2.1") + if not self.options.freestanding: + if self.options.contracts == "gsl-lite": + self.requires("gsl-lite/0.41.0") + elif self.options.contracts == "ms-gsl": + self.requires("ms-gsl/4.0.0") + if self._use_fmtlib: + self.requires("fmt/10.2.1") def build_requirements(self): if self._build_all: @@ -248,9 +249,12 @@ class MPUnitsConan(ConanFile): tc.cache_variables["MP_UNITS_BUILD_CXX_MODULES"] = str( self.options.cxx_modules ).upper() - tc.cache_variables["MP_UNITS_API_STD_FORMAT"] = str( - self.options.std_format - ).upper() + if self.options.freestanding: + tc.cache_variables["MP_UNITS_API_FREESTANDING"] = True + else: + tc.cache_variables["MP_UNITS_API_STD_FORMAT"] = str( + self.options.std_format + ).upper() tc.cache_variables["MP_UNITS_API_STRING_VIEW_RET"] = str( self.options.string_view_ret ).upper() @@ -258,7 +262,6 @@ class MPUnitsConan(ConanFile): tc.cache_variables["MP_UNITS_API_CONTRACTS"] = str( self.options.contracts ).upper() - tc.cache_variables["MP_UNITS_API_FREESTANDING"] = self.options.freestanding tc.generate() deps = CMakeDeps(self) deps.generate() @@ -285,13 +288,39 @@ class MPUnitsConan(ConanFile): def package_info(self): compiler = self.settings.compiler - self.cpp_info.components["core"] - if self.options.contracts == "gsl-lite": - self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"] + + # handle contracts + if self.options.contracts == "none": + self.cpp_info.components["core"].defines.append("MP_UNITS_API_CONTRACTS=0") + elif self.options.contracts == "gsl-lite": + self.cpp_info.components["core"].requires.append("gsl-lite::gsl-lite") + self.cpp_info.components["core"].defines.append("MP_UNITS_API_CONTRACTS=2") elif self.options.contracts == "ms-gsl": - self.cpp_info.components["core"].requires = ["ms-gsl::ms-gsl"] - if self._use_fmtlib and not self.options.freestanding: + self.cpp_info.components["core"].requires.append("ms-gsl::ms-gsl") + self.cpp_info.components["core"].defines.append("MP_UNITS_API_CONTRACTS=3") + + # handle API options + if self.options.string_view_ret != "auto": + self.cpp_info.components["core"].defines.append( + "MP_UNITS_API_STRING_VIEW_RET=" + + str(int(self.options.string_view_ret == True)) + ) + if self.options.no_crtp != "auto": + self.cpp_info.components["core"].defines.append( + "MP_UNITS_API_NO_CRTP=" + str(int(self.options.no_crtp == True)) + ) + if self.options.std_format != "auto": + self.cpp_info.components["core"].defines.append( + "MP_UNITS_API_STD_FORMAT=" + str(int(self.options.std_format == True)) + ) + if self._use_fmtlib: self.cpp_info.components["core"].requires.append("fmt::fmt") + + # handle hosted configuration + if not self.options.freestanding: + self.cpp_info.components["core"].defines.append("MP_UNITS_HOSTED=1") + if compiler == "msvc": self.cpp_info.components["core"].cxxflags = ["/utf-8"] + self.cpp_info.components["systems"].requires = ["core"] diff --git a/docs/blog/posts/2.2.0-released.md b/docs/blog/posts/2.2.0-released.md index 936840a1..6dd7bd46 100644 --- a/docs/blog/posts/2.2.0-released.md +++ b/docs/blog/posts/2.2.0-released.md @@ -172,7 +172,7 @@ Additionally, some CMake options were renamed to better express the impact on ou Before this release, the library always depended on [gsl-lite](https://github.com/gsl-lite/gsl-lite) to perform runtime contract and asserts checking. In this release we introduced new options to control if contract checking should be based on [gsl-lite](https://github.com/gsl-lite/gsl-lite), -[ms-gsl](https://github.com/microsoft/GSL), or maybe completely disabled. +[ms-gsl](https://github.com/microsoft/GSL), or may be completely disabled. ## Freestanding support @@ -205,7 +205,7 @@ origins. For example: === "Before" ```cpp - constexpr struct zero : absolute_point_origin {} zero; + constexpr struct zero final : absolute_point_origin {} zero; quantity_point price_usd = zero + 100 * USD; ``` @@ -260,6 +260,16 @@ named with its corresponding unit and with the `si::zeroth_degree_Celsius` ## Changes to units definitions +There were several known issues when units were deriving from each other +(e.g., [#512](https://github.com/mpusz/mp-units/issues/512) and +[#537](https://github.com/mpusz/mp-units/issues/537)). We could either highly complicate the +framework to allow these which could result in much longer compilation times or disallow inheriting +from units at all. We chose the second option. + +With this release all of of the units must be marked as `final`. To enforce this we have changed +the definition of the `Unit` concept, which now requires type `T` to be `final` +(:boom: **breaking change** :boom:). + [WG21 Study Group 16 (Unicode) raised concerns](https://github.com/sg16-unicode/sg16-meetings#january-24th-2024) about potential ABI issues when different translation units are compiled with different ordinary literal encodings. Those issues were resolved with a change to units definitions @@ -272,7 +282,7 @@ is why it was renamed to `symbol_text` (:boom: **breaking change** :boom:). === "Now" ```cpp - inline constexpr struct ohm : named_unit {} ohm; + inline constexpr struct ohm final : named_unit {} ohm; ``` === "Before" @@ -286,9 +296,48 @@ is why it was renamed to `symbol_text` (:boom: **breaking change** :boom:). On C++20-compliant compilers it should be enough to type the following in the unit's definition: ```cpp - inline constexpr struct ohm : named_unit<{u8"Ω", "ohm"}, volt / ampere> {} ohm; + inline constexpr struct ohm final : named_unit<{u8"Ω", "ohm"}, volt / ampere> {} ohm; ``` + +## Changes to dimension, quantity specification, and point origins definitions + +Similarly to units, now also all dimensions, quantity specifications, and point origins have to be +marked `final` (:boom: **breaking change** :boom:). + +=== "Now" + + ```cpp + inline constexpr struct dim_length final : base_dimension<"L"> {} dim_length; + inline constexpr struct length final : quantity_spec {} length; + + inline constexpr struct absolute_zero final : absolute_point_origin {} absolute_zero; + inline constexpr auto zeroth_kelvin = absolute_zero; + inline constexpr struct kelvin final : named_unit<"K", kind_of, zeroth_kelvin> {} kelvin; + + inline constexpr struct ice_point final : relative_point_origin}> {} ice_point; + inline constexpr auto zeroth_degree_Celsius = ice_point; + inline constexpr struct degree_Celsius final : named_unit {} degree_Celsius; + ``` + +=== "Before" + + ```cpp + inline constexpr struct dim_length : base_dimension<"L"> {} dim_length; + inline constexpr struct length : quantity_spec {} length; + + inline constexpr struct absolute_zero : absolute_point_origin {} absolute_zero; + inline constexpr struct zeroth_kelvin : decltype(absolute_zero) {} zeroth_kelvin; + inline constexpr struct kelvin : named_unit<"K", kind_of, zeroth_kelvin> {} kelvin; + + inline constexpr struct ice_point : relative_point_origin}> {} ice_point; + inline constexpr struct zeroth_degree_Celsius : decltype(ice_point) {} zeroth_degree_Celsius; + inline constexpr struct degree_Celsius : named_unit {} degree_Celsius; + ``` + +Please also note, that the `absolute_point_origin` does not use CRTP idiom anymore (:boom: **breaking change** :boom:). + + ## Improved text output With this release, we can print not only whole quantities but also just their units or dimensions. @@ -461,8 +510,8 @@ conversion factor. Here is a comparison of the code with previous and current de === "Now" ```cpp - inline constexpr struct yard : named_unit<"yd", mag_ratio<9'144, 10'000> * si::metre> {} yard; - inline constexpr struct foot : named_unit<"ft", mag_ratio<1, 3> * yard> {} foot; + inline constexpr struct yard final : named_unit<"yd", mag_ratio<9'144, 10'000> * si::metre> {} yard; + inline constexpr struct foot final : named_unit<"ft", mag_ratio<1, 3> * yard> {} foot; ``` === "Before" diff --git a/docs/getting_started/installation_and_usage.md b/docs/getting_started/installation_and_usage.md index 175bf545..d1c4a079 100644 --- a/docs/getting_started/installation_and_usage.md +++ b/docs/getting_started/installation_and_usage.md @@ -342,7 +342,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [`MP_UNITS_API_STD_FORMAT`](#MP_UNITS_API_STD_FORMAT){ #MP_UNITS_API_STD_FORMAT } -: [:octicons-tag-24: 2.2.0][use fmtlib support] · :octicons-milestone-24: `AUTO`/`ON`/`OFF` (Default: `AUTO`) +: [:octicons-tag-24: 2.2.0][use fmtlib support] · :octicons-milestone-24: `AUTO`/`TRUE`/`FALSE` (Default: `AUTO`) Enables the usage of [`std::format`](https://en.cppreference.com/w/cpp/utility/format/format) and associated facilities for text formatting. If it is not supported, then @@ -352,7 +352,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [`MP_UNITS_API_STRING_VIEW_RET`](#MP_UNITS_API_STRING_VIEW_RET){ #MP_UNITS_API_STRING_VIEW_RET } -: [:octicons-tag-24: 2.2.0][cmake returning string_view] · :octicons-milestone-24: `AUTO`/`ON`/`OFF` (Default: `AUTO`) +: [:octicons-tag-24: 2.2.0][cmake returning string_view] · :octicons-milestone-24: `AUTO`/`TRUE`/`FALSE` (Default: `AUTO`) Enables returning `std::string_view` from the [`unit_symbol()`](../users_guide/framework_basics/text_output.md#unit_symbol) @@ -364,7 +364,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [`MP_UNITS_API_NO_CRTP`](#MP_UNITS_API_NO_CRTP){ #MP_UNITS_API_NO_CRTP } -: [:octicons-tag-24: 2.2.0][cmake no crtp support] · :octicons-milestone-24: `AUTO`/`ON`/`OFF` (Default: `AUTO`) +: [:octicons-tag-24: 2.2.0][cmake no crtp support] · :octicons-milestone-24: `AUTO`/`TRUE`/`FALSE` (Default: `AUTO`) Removes the need for the usage of the CRTP idiom in the [`quantity_spec` definitions](../users_guide/framework_basics/systems_of_quantities.md#defining-quantities). diff --git a/docs/index.md b/docs/index.md index ea4b50d0..260316e1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -33,7 +33,7 @@ The library source code is hosted on [GitHub](https://github.com/mpusz/mp-units) using namespace mp_units; - inline constexpr struct smoot : named_unit<"smoot", mag<67> * usc::inch> {} smoot; + inline constexpr struct smoot final : named_unit<"smoot", mag<67> * usc::inch> {} smoot; int main() { @@ -53,7 +53,7 @@ The library source code is hosted on [GitHub](https://github.com/mpusz/mp-units) using namespace mp_units; - inline constexpr struct smoot : named_unit<"smoot", mag<67> * usc::inch> {} smoot; + inline constexpr struct smoot final : named_unit<"smoot", mag<67> * usc::inch> {} smoot; int main() { @@ -69,7 +69,7 @@ Output: Harvard Bridge length = 364.4 smoot (2034.6 ft, 620.14 m) ± 1 εar ``` -!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/zsW1f6Tn1)" +!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/f8f4KnKh8)" ??? question "What is `smoot`?" diff --git a/docs/users_guide/examples/avg_speed.md b/docs/users_guide/examples/avg_speed.md index 73bba732..397cafc1 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/EYo7879qd)" +!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/TnqGa4sdn)" 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 diff --git a/docs/users_guide/examples/hello_units.md b/docs/users_guide/examples/hello_units.md index c77f2287..dd69fcf4 100644 --- a/docs/users_guide/examples/hello_units.md +++ b/docs/users_guide/examples/hello_units.md @@ -6,7 +6,7 @@ tags: # `hello_units` -!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/KKGGhKjqn)" +!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/bT4GGPbef)" This is a really simple example showcasing the features of the **mp-units** library. @@ -18,20 +18,20 @@ First, we either import the `mp_units` module or include the headers for: - text formatting and stream output support ```cpp title="hello_units.cpp" linenums="1" ---8<-- "example/hello_units.cpp:28:40" +--8<-- "example/hello_units.cpp:28:41" ``` Also, to shorten the definitions, we "import" all the symbols from the `mp_units` namespace. -```cpp title="hello_units.cpp" linenums="13" ---8<-- "example/hello_units.cpp:41:42" +```cpp title="hello_units.cpp" linenums="14" +--8<-- "example/hello_units.cpp:42:43" ``` 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="14" ---8<-- "example/hello_units.cpp:43:46" +```cpp title="hello_units.cpp" linenums="15" +--8<-- "example/hello_units.cpp:44:47" ``` 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="18" ---8<-- "example/hello_units.cpp:48:51" +```cpp title="hello_units.cpp" linenums="19" +--8<-- "example/hello_units.cpp:49:52" ``` 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="22" ---8<-- "example/hello_units.cpp:53:59" +```cpp title="hello_units.cpp" linenums="23" +--8<-- "example/hello_units.cpp:54:60" ``` -- Lines `21` & `22` create a quantity of kind `isq::length / isq::time` with the numbers +- Lines `23` & `24` 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 `23` calls our function template with quantities of kind `isq::length` and +- Line `25` calls our function template with quantities of kind `isq::length` and `isq::time` and number and units provided. -- Line `24` explicitly provides quantity types of the quantities passed to a function template. +- Line `26` 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 `25` changes the unit of a quantity `v3` to `m / s` in a +- Line `27` 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 `26` does a similar operation, but this time, it would also succeed for +- Line `28` 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 `27` does a [value-truncating conversion](../framework_basics/value_conversions.md#value-truncating-conversions) +- Line `29` 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="29" ---8<-- "example/hello_units.cpp:61" +```cpp title="hello_units.cpp" linenums="30" +--8<-- "example/hello_units.cpp:62" ``` The above presents [various ways to print a quantity](../framework_basics/text_output.md). diff --git a/docs/users_guide/examples/si_constants.md b/docs/users_guide/examples/si_constants.md index ab331bc6..05dbfbc2 100644 --- a/docs/users_guide/examples/si_constants.md +++ b/docs/users_guide/examples/si_constants.md @@ -6,14 +6,14 @@ tags: # `si_constants` -!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/sEqWcchdE)" +!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/MevcK8vYT)" 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) work in practice. ```cpp title="si_constants.cpp" linenums="1" ---8<-- "example/si_constants.cpp:28:39" +--8<-- "example/si_constants.cpp:28:40" ``` As always, we start with the inclusion of all the needed header files. After that, for @@ -21,8 +21,8 @@ the simplicity of this example, we [hack the character of quantities](../framework_basics/character_of_a_quantity.md#hacking-the-character) to be able to express vector quantities with simple scalar types. -```cpp title="si_constants.cpp" linenums="13" ---8<-- "example/si_constants.cpp:41:" +```cpp title="si_constants.cpp" linenums="14" +--8<-- "example/si_constants.cpp:42:" ``` The main part of the example prints all of the SI-defining constants. While analyzing the output of diff --git a/docs/users_guide/framework_basics/character_of_a_quantity.md b/docs/users_guide/framework_basics/character_of_a_quantity.md index ad366078..26b358af 100644 --- a/docs/users_guide/framework_basics/character_of_a_quantity.md +++ b/docs/users_guide/framework_basics/character_of_a_quantity.md @@ -97,15 +97,15 @@ enumeration can be appended to the `quantity_spec` describing such a quantity ty === "C++23" ```cpp - inline constexpr struct position_vector : quantity_spec {} position_vector; - inline constexpr struct displacement : quantity_spec {} displacement; + inline constexpr struct position_vector final : quantity_spec {} position_vector; + inline constexpr struct displacement final : quantity_spec {} displacement; ``` === "C++20" ```cpp - inline constexpr struct position_vector : quantity_spec {} position_vector; - inline constexpr struct displacement : quantity_spec {} displacement; + inline constexpr struct position_vector final : quantity_spec {} position_vector; + inline constexpr struct displacement final : quantity_spec {} displacement; ``` === "Portable" @@ -126,13 +126,13 @@ character override is needed): === "C++23" ```cpp - inline constexpr struct velocity : quantity_spec {} velocity; + inline constexpr struct velocity final : quantity_spec {} velocity; ``` === "C++20" ```cpp - inline constexpr struct velocity : quantity_spec {} velocity; + inline constexpr struct velocity final : quantity_spec {} velocity; ``` === "Portable" diff --git a/docs/users_guide/framework_basics/concepts.md b/docs/users_guide/framework_basics/concepts.md index 494675a6..da21dcd5 100644 --- a/docs/users_guide/framework_basics/concepts.md +++ b/docs/users_guide/framework_basics/concepts.md @@ -16,6 +16,8 @@ or derived [quantity](../../appendix/glossary.md#quantity): by the library's framework based on the [quantity equation](../../appendix/glossary.md#quantity-equation) provided in the [quantity specification](../../appendix/glossary.md#quantity_spec). +All of the above dimensions have to be marked as `final`. + ### `DimensionOf` { #DimensionOf } @@ -42,6 +44,8 @@ including: - Intermediate [derived quantity](../../appendix/glossary.md#derived-quantity) specifications being a result of a [quantity equations](../../appendix/glossary.md#quantity-equation) on other specifications. +All of the above quantity specifications have to be marked as `final`. + ### `QuantitySpecOf` { #QuantitySpecOf } @@ -84,6 +88,8 @@ and when `T` is implicitly convertible to `V`. - [Derived unnamed units](../../appendix/glossary.md#derived-unit) being a result of a [unit equations](../../appendix/glossary.md#unit-equation) on other units. +All of the above units have to be marked as `final`. + !!! note In the **mp-units** library, [physical constants are also implemented as units](faster_than_lightspeed_constants.md). @@ -235,7 +241,7 @@ implicitly convertible from quantity specification `V`, which means that `V` mus However, if we define `mean_sea_level` in the following way: ```cpp - inline constexpr struct mean_sea_level : absolute_point_origin {} mean_sea_level; + inline constexpr struct mean_sea_level final : absolute_point_origin {} mean_sea_level; ``` then it can't be used as a point origin for _points_ of `isq::length` or `isq::width` as none of them @@ -328,7 +334,7 @@ for which an instantiation of `quantity_point_like_traits` type trait yields a v struct mp_units::quantity_point_like_traits> { using T = std::chrono::time_point; static constexpr auto reference = si::second; - static constexpr struct point_origin : absolute_point_origin {} point_origin{}; + static constexpr struct point_origin final : absolute_point_origin {} point_origin{}; using rep = std::chrono::seconds::rep; [[nodiscard]] static constexpr convert_implicitly> to_quantity(const T& qp) diff --git a/docs/users_guide/framework_basics/design_overview.md b/docs/users_guide/framework_basics/design_overview.md index da38cd95..04857726 100644 --- a/docs/users_guide/framework_basics/design_overview.md +++ b/docs/users_guide/framework_basics/design_overview.md @@ -60,8 +60,8 @@ For example: the following way: ```cpp -inline constexpr struct dim_length : base_dimension<"L"> {} dim_length; -inline constexpr struct dim_time : base_dimension<"T"> {} dim_time; +inline constexpr struct dim_length final : base_dimension<"L"> {} dim_length; +inline constexpr struct dim_time final : base_dimension<"T"> {} dim_time; ``` [Derived dimensions](../../appendix/glossary.md#derived-dimension) are implicitly created @@ -71,9 +71,9 @@ provided in the [quantity specification](../../appendix/glossary.md#quantity_spe === "C++23" ```cpp - inline constexpr struct length : quantity_spec {} length; - inline constexpr struct time : quantity_spec {} time; - inline constexpr struct speed : quantity_spec {} speed; + inline constexpr struct length final : quantity_spec {} length; + inline constexpr struct time final : quantity_spec {} time; + inline constexpr struct speed final : quantity_spec {} speed; static_assert(speed.dimension == dim_length / dim_time); ``` @@ -81,9 +81,9 @@ provided in the [quantity specification](../../appendix/glossary.md#quantity_spe === "C++20" ```cpp - inline constexpr struct length : quantity_spec {} length; - inline constexpr struct time : quantity_spec {} time; - inline constexpr struct speed : quantity_spec {} speed; + inline constexpr struct length final : quantity_spec {} length; + inline constexpr struct time final : quantity_spec {} time; + inline constexpr struct speed final : quantity_spec {} speed; static_assert(speed.dimension == dim_length / dim_time); ``` @@ -183,17 +183,17 @@ Quantity specification can be defined by the user in one of the following ways: === "C++23" ```cpp - inline constexpr struct length : quantity_spec {} length; - inline constexpr struct height : quantity_spec {} height; - inline constexpr struct speed : quantity_spec {} speed; + inline constexpr struct length final : quantity_spec {} length; + inline constexpr struct height final : quantity_spec {} height; + inline constexpr struct speed final : quantity_spec {} speed; ``` === "C++20" ```cpp - inline constexpr struct length : quantity_spec {} length; - inline constexpr struct height : quantity_spec {} height; - inline constexpr struct speed : quantity_spec {} speed; + inline constexpr struct length final : quantity_spec {} length; + inline constexpr struct height final : quantity_spec {} height; + inline constexpr struct speed final : quantity_spec {} speed; ``` === "Portable" @@ -234,13 +234,13 @@ A unit can be defined by the user in one of the following ways: template struct kilo_ : prefixed_unit<"k", mag_power<10, 3>, U{}> {}; template inline constexpr kilo_ kilo; -inline constexpr struct second : named_unit<"s", kind_of> {} second; -inline constexpr struct minute : named_unit<"min", mag<60> * second> {} minute; -inline constexpr struct gram : named_unit<"g", kind_of> {} gram; -inline constexpr struct kilogram : decltype(kilo) {} kilogram; -inline constexpr struct newton : named_unit<"N", kilogram * metre / square(second)> {} newton; +inline constexpr struct second final : named_unit<"s", kind_of> {} second; +inline constexpr struct minute final : named_unit<"min", mag<60> * second> {} minute; +inline constexpr struct gram final : named_unit<"g", kind_of> {} gram; +inline constexpr auto kilogram = kilo; +inline constexpr struct newton final : named_unit<"N", kilogram * metre / square(second)> {} newton; -inline constexpr struct speed_of_light_in_vacuum : named_unit<"c", mag<299'792'458> * metre / second> {} speed_of_light_in_vacuum; +inline constexpr struct speed_of_light_in_vacuum final : named_unit<"c", mag<299'792'458> * metre / second> {} speed_of_light_in_vacuum; ``` The [unit equation](../../appendix/glossary.md#unit-equation) of `si::metre / si::second` results @@ -346,13 +346,13 @@ For example: - the absolute point origin can be defined in the following way: ```cpp - inline constexpr struct absolute_zero : absolute_point_origin {} absolute_zero; + inline constexpr struct absolute_zero final : absolute_point_origin {} absolute_zero; ``` - the relative point origin can be defined in the following way: ```cpp - inline constexpr struct ice_point : relative_point_origin> {} ice_point; + inline constexpr struct ice_point final : relative_point_origin> {} ice_point; ``` diff --git a/docs/users_guide/framework_basics/dimensionless_quantities.md b/docs/users_guide/framework_basics/dimensionless_quantities.md index 3019828f..16d272f2 100644 --- a/docs/users_guide/framework_basics/dimensionless_quantities.md +++ b/docs/users_guide/framework_basics/dimensionless_quantities.md @@ -113,7 +113,7 @@ that uses a unit that is proportional to the ratio of kilometers per megaparsecs units of _length_: ```cpp -inline constexpr struct hubble_constant : +inline constexpr struct hubble_constant final : named_unit<{u8"H₀", "H_0"}, mag_ratio<701, 10> * si::kilo / si::second / si::mega> {} hubble_constant; ``` @@ -158,9 +158,9 @@ Besides the unit `one`, there are a few other scaled units predefined in the lib with dimensionless quantities: ```cpp -inline constexpr struct percent : named_unit<"%", mag_ratio<1, 100> * one> {} percent; -inline constexpr struct per_mille : named_unit<{u8"‰", "%o"}, mag_ratio<1, 1000> * one> {} per_mille; -inline constexpr struct parts_per_million : named_unit<"ppm", mag_ratio<1, 1'000'000> * one> {} parts_per_million; +inline constexpr struct percent final : named_unit<"%", mag_ratio<1, 100> * one> {} percent; +inline constexpr struct per_mille final : named_unit<{u8"‰", "%o"}, mag_ratio<1, 1000> * one> {} per_mille; +inline constexpr struct parts_per_million final : named_unit<"ppm", mag_ratio<1, 1'000'000> * one> {} parts_per_million; inline constexpr auto ppm = parts_per_million; ``` @@ -217,17 +217,17 @@ to the quantity specification: === "C++23" ```cpp - inline constexpr struct angular_measure : quantity_spec {} angular_measure; - inline constexpr struct solid_angular_measure : quantity_spec(radius), is_kind> {} solid_angular_measure; - inline constexpr struct storage_capacity : quantity_spec {} storage_capacity; + inline constexpr struct angular_measure final : quantity_spec {} angular_measure; + inline constexpr struct solid_angular_measure final : quantity_spec(radius), is_kind> {} solid_angular_measure; + inline constexpr struct storage_capacity final : quantity_spec {} storage_capacity; ``` === "C++20" ```cpp - inline constexpr struct angular_measure : quantity_spec {} angular_measure; - inline constexpr struct solid_angular_measure : quantity_spec(radius), is_kind> {} solid_angular_measure; - inline constexpr struct storage_capacity : quantity_spec {} storage_capacity; + inline constexpr struct angular_measure final : quantity_spec {} angular_measure; + inline constexpr struct solid_angular_measure final : quantity_spec(radius), is_kind> {} solid_angular_measure; + inline constexpr struct storage_capacity final : quantity_spec {} storage_capacity; ``` === "Portable" @@ -242,9 +242,9 @@ With the above, we can constrain `radian`, `steradian`, and `bit` to be allowed specific quantity kinds only: ```cpp -inline constexpr struct radian : named_unit<"rad", metre / metre, kind_of> {} radian; -inline constexpr struct steradian : named_unit<"sr", square(metre) / square(metre), kind_of> {} steradian; -inline constexpr struct bit : named_unit<"bit", one, kind_of> {} bit; +inline constexpr struct radian final : named_unit<"rad", metre / metre, kind_of> {} radian; +inline constexpr struct steradian final : named_unit<"sr", square(metre) / square(metre), kind_of> {} steradian; +inline constexpr struct bit final : named_unit<"bit", one, kind_of> {} bit; ``` but still allow the usage of `one` and its scaled versions for such quantities. 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 d69ef39c..cef490db 100644 --- a/docs/users_guide/framework_basics/faster_than_lightspeed_constants.md +++ b/docs/users_guide/framework_basics/faster_than_lightspeed_constants.md @@ -39,12 +39,12 @@ namespace si { namespace si2019 { -inline constexpr struct speed_of_light_in_vacuum : +inline constexpr struct speed_of_light_in_vacuum final : named_unit<"c", mag<299'792'458> * metre / second> {} speed_of_light_in_vacuum; } // namespace si2019 -inline constexpr struct magnetic_constant : +inline constexpr struct magnetic_constant final : named_unit<{u8"μ₀", "u_0"}, mag<4> * mag_pi * mag_power<10, -7> * henry / metre> {} magnetic_constant; } // namespace mp_units::si diff --git a/docs/users_guide/framework_basics/interface_introduction.md b/docs/users_guide/framework_basics/interface_introduction.md index f1bcc16f..9f3de85c 100644 --- a/docs/users_guide/framework_basics/interface_introduction.md +++ b/docs/users_guide/framework_basics/interface_introduction.md @@ -6,8 +6,8 @@ The **mp-units** library decided to use a rather unusual pattern to define entit Here is how we define `metre` and `second` [SI](../../appendix/glossary.md#si) base units: ```cpp -inline constexpr struct metre : named_unit<"m", kind_of> {} metre; -inline constexpr struct second : named_unit<"s", kind_of> {} second; +inline constexpr struct metre final : named_unit<"m", kind_of> {} metre; +inline constexpr struct second final : named_unit<"s", kind_of> {} second; ``` Please note that the above reuses the same identifier for a type and its value. The rationale @@ -94,9 +94,9 @@ the value-based [unit equation](../../appendix/glossary.md#unit-equation) to a c definition: ```cpp -inline constexpr struct newton : named_unit<"N", kilogram * metre / square(second)> {} newton; -inline constexpr struct pascal : named_unit<"Pa", newton / square(metre)> {} pascal; -inline constexpr struct joule : named_unit<"J", newton * metre> {} joule; +inline constexpr struct newton final : named_unit<"N", kilogram * metre / square(second)> {} newton; +inline constexpr struct pascal final : named_unit<"Pa", newton / square(metre)> {} pascal; +inline constexpr struct joule final : named_unit<"J", newton * metre> {} joule; ``` diff --git a/docs/users_guide/framework_basics/simple_and_typed_quantities.md b/docs/users_guide/framework_basics/simple_and_typed_quantities.md index 8dc98ee8..178c36e2 100644 --- a/docs/users_guide/framework_basics/simple_and_typed_quantities.md +++ b/docs/users_guide/framework_basics/simple_and_typed_quantities.md @@ -316,13 +316,13 @@ Let's see another example: using namespace mp_units; // add a custom quantity type of kind isq::length - inline constexpr struct horizontal_length - : quantity_spec {} horizontal_length; + inline constexpr struct horizontal_length final : + quantity_spec {} horizontal_length; // add a custom derived quantity type of kind isq::area // with a constrained quantity equation - inline constexpr struct horizontal_area - : quantity_spec {} horizontal_area; + inline constexpr struct horizontal_area final : + quantity_spec {} horizontal_area; class StorageTank { quantity base_; @@ -429,13 +429,13 @@ Let's see another example: using namespace mp_units; // add a custom quantity type of kind isq::length - inline constexpr struct horizontal_length - : quantity_spec {} horizontal_length; + inline constexpr struct horizontal_length final : + quantity_spec {} horizontal_length; // add a custom derived quantity type of kind isq::area // with a constrained quantity equation - inline constexpr struct horizontal_area - : quantity_spec {} horizontal_area; + inline constexpr struct horizontal_area final : + quantity_spec {} horizontal_area; class StorageTank { quantity base_; diff --git a/docs/users_guide/framework_basics/systems_of_quantities.md b/docs/users_guide/framework_basics/systems_of_quantities.md index 65605f2b..07660e1d 100644 --- a/docs/users_guide/framework_basics/systems_of_quantities.md +++ b/docs/users_guide/framework_basics/systems_of_quantities.md @@ -148,45 +148,45 @@ For example, here is how the above quantity kind tree can be modeled in the libr === "C++23" ```cpp - inline constexpr struct length : quantity_spec {} length; - inline constexpr struct width : quantity_spec {} width; + inline constexpr struct length final : quantity_spec {} length; + inline constexpr struct width final : quantity_spec {} width; inline constexpr auto breadth = width; - inline constexpr struct height : quantity_spec {} height; + inline constexpr struct height final : quantity_spec {} height; inline constexpr auto depth = height; inline constexpr auto altitude = height; - inline constexpr struct thickness : quantity_spec {} thickness; - inline constexpr struct diameter : quantity_spec {} diameter; - inline constexpr struct radius : quantity_spec {} radius; - inline constexpr struct radius_of_curvature : quantity_spec {} radius_of_curvature; - inline constexpr struct path_length : quantity_spec {} path_length; + inline constexpr struct thickness final : quantity_spec {} thickness; + inline constexpr struct diameter final : quantity_spec {} diameter; + inline constexpr struct radius final : quantity_spec {} radius; + inline constexpr struct radius_of_curvature final : quantity_spec {} radius_of_curvature; + inline constexpr struct path_length final : quantity_spec {} path_length; inline constexpr auto arc_length = path_length; - inline constexpr struct distance : quantity_spec {} distance; - inline constexpr struct radial_distance : quantity_spec {} radial_distance; - inline constexpr struct wavelength : quantity_spec {} wavelength; - inline constexpr struct position_vector : quantity_spec {} position_vector; - inline constexpr struct displacement : quantity_spec {} displacement; + inline constexpr struct distance final : quantity_spec {} distance; + inline constexpr struct radial_distance final : quantity_spec {} radial_distance; + inline constexpr struct wavelength final : quantity_spec {} wavelength; + inline constexpr struct position_vector final : quantity_spec {} position_vector; + inline constexpr struct displacement final : quantity_spec {} displacement; ``` === "C++20" ```cpp - inline constexpr struct length : quantity_spec {} length; - inline constexpr struct width : quantity_spec {} width; + inline constexpr struct length final : quantity_spec {} length; + inline constexpr struct width final : quantity_spec {} width; inline constexpr auto breadth = width; - inline constexpr struct height : quantity_spec {} height; + inline constexpr struct height final : quantity_spec {} height; inline constexpr auto depth = height; inline constexpr auto altitude = height; - inline constexpr struct thickness : quantity_spec {} thickness; - inline constexpr struct diameter : quantity_spec {} diameter; - inline constexpr struct radius : quantity_spec {} radius; - inline constexpr struct radius_of_curvature : quantity_spec {} radius_of_curvature; - inline constexpr struct path_length : quantity_spec {} path_length; + inline constexpr struct thickness final : quantity_spec {} thickness; + inline constexpr struct diameter final : quantity_spec {} diameter; + inline constexpr struct radius final : quantity_spec {} radius; + inline constexpr struct radius_of_curvature final : quantity_spec {} radius_of_curvature; + inline constexpr struct path_length final : quantity_spec {} path_length; inline constexpr auto arc_length = path_length; - inline constexpr struct distance : quantity_spec {} distance; - inline constexpr struct radial_distance : quantity_spec {} radial_distance; - inline constexpr struct wavelength : quantity_spec {} wavelength; - inline constexpr struct position_vector : quantity_spec {} position_vector; - inline constexpr struct displacement : quantity_spec {} displacement; + inline constexpr struct distance final : quantity_spec {} distance; + inline constexpr struct radial_distance final : quantity_spec {} radial_distance; + inline constexpr struct wavelength final : quantity_spec {} wavelength; + inline constexpr struct position_vector final : quantity_spec {} position_vector; + inline constexpr struct displacement final : quantity_spec {} displacement; ``` === "Portable" diff --git a/docs/users_guide/framework_basics/systems_of_units.md b/docs/users_guide/framework_basics/systems_of_units.md index 3d263c11..df37b6bf 100644 --- a/docs/users_guide/framework_basics/systems_of_units.md +++ b/docs/users_guide/framework_basics/systems_of_units.md @@ -26,7 +26,7 @@ this is expressed by associating a quantity kind (that we discussed in detail in previous chapter) with a unit that is used to express it: ```cpp -inline constexpr struct metre : named_unit<"m", kind_of> {} metre; +inline constexpr struct metre final : named_unit<"m", kind_of> {} metre; ``` !!! important @@ -67,7 +67,7 @@ of a specific predefined [unit equation](../../appendix/glossary.md#unit-equatio For example, a unit of _power_ quantity is defined in the library as: ```cpp -inline constexpr struct watt : named_unit<"W", joule / second> {} watt; +inline constexpr struct watt final : named_unit<"W", joule / second> {} watt; ``` However, a _power_ quantity can be expressed in other units as well. For example, @@ -110,8 +110,8 @@ The library allows constraining such units to work only with quantities of a spe the following way: ```cpp -inline constexpr struct hertz : named_unit<"Hz", one / second, kind_of> {} hertz; -inline constexpr struct becquerel : named_unit<"Bq", one / second, kind_of> {} becquerel; +inline constexpr struct hertz final : named_unit<"Hz", one / second, kind_of> {} hertz; +inline constexpr struct becquerel final : named_unit<"Bq", one / second, kind_of> {} becquerel; ``` With the above, `hertz` can only be used with _frequencies_, while `becquerel` should only be used with @@ -168,25 +168,25 @@ be explicitly expressed with predefined SI prefixes. Those include units like mi electronvolt: ```cpp -inline constexpr struct minute : named_unit<"min", mag<60> * si::second> {} minute; -inline constexpr struct hour : named_unit<"h", mag<60> * minute> {} hour; -inline constexpr struct electronvolt : named_unit<"eV", mag_ratio<1'602'176'634, 1'000'000'000> * mag_power<10, -19> * si::joule> {} electronvolt; +inline constexpr struct minute final : named_unit<"min", mag<60> * si::second> {} minute; +inline constexpr struct hour final : named_unit<"h", mag<60> * minute> {} hour; +inline constexpr struct electronvolt final : named_unit<"eV", mag_ratio<1'602'176'634, 1'000'000'000> * mag_power<10, -19> * si::joule> {} electronvolt; ``` Also, units of other [systems of units](../../appendix/glossary.md#system-of-units) are often defined in terms of scaled versions of the SI units. For example, the international yard is defined as: ```cpp -inline constexpr struct yard : named_unit<"yd", mag_ratio<9'144, 10'000> * si::metre> {} yard; +inline constexpr struct yard final : named_unit<"yd", mag_ratio<9'144, 10'000> * si::metre> {} yard; ``` For some units, a magnitude might also be irrational. The best example here is a `degree` which is defined using a floating-point magnitude having a factor of the number π (Pi): ```cpp -inline constexpr struct mag_pi : magnitude> {} mag_pi; +inline constexpr struct mag_pi final : magnitude> {} mag_pi; ``` ```cpp -inline constexpr struct degree : named_unit<{u8"°", "deg"}, mag_pi / mag<180> * si::radian> {} degree; +inline constexpr struct degree final : named_unit<{u8"°", "deg"}, mag_pi / mag<180> * si::radian> {} degree; ``` diff --git a/docs/users_guide/framework_basics/text_output.md b/docs/users_guide/framework_basics/text_output.md index de8c96ea..cc6c347d 100644 --- a/docs/users_guide/framework_basics/text_output.md +++ b/docs/users_guide/framework_basics/text_output.md @@ -33,30 +33,30 @@ and units of derived quantities. === "Dimensions" ```cpp - inline constexpr struct dim_length : base_dimension<"L"> {} dim_length; - inline constexpr struct dim_mass : base_dimension<"M"> {} dim_mass; - inline constexpr struct dim_time : base_dimension<"T"> {} dim_time; - inline constexpr struct dim_electric_current : base_dimension<"I"> {} dim_electric_current; - inline constexpr struct dim_thermodynamic_temperature : base_dimension<{u8"Θ", "O"}> {} dim_thermodynamic_temperature; - inline constexpr struct dim_amount_of_substance : base_dimension<"N"> {} dim_amount_of_substance; - inline constexpr struct dim_luminous_intensity : base_dimension<"J"> {} dim_luminous_intensity; + inline constexpr struct dim_length final : base_dimension<"L"> {} dim_length; + inline constexpr struct dim_mass final : base_dimension<"M"> {} dim_mass; + inline constexpr struct dim_time final : base_dimension<"T"> {} dim_time; + inline constexpr struct dim_electric_current final : base_dimension<"I"> {} dim_electric_current; + inline constexpr struct dim_thermodynamic_temperature final : base_dimension<{u8"Θ", "O"}> {} dim_thermodynamic_temperature; + inline constexpr struct dim_amount_of_substance final : base_dimension<"N"> {} dim_amount_of_substance; + inline constexpr struct dim_luminous_intensity final : base_dimension<"J"> {} dim_luminous_intensity; ``` === "Units" ```cpp - inline constexpr struct second : named_unit<"s", kind_of> {} second; - inline constexpr struct metre : named_unit<"m", kind_of> {} metre; - inline constexpr struct gram : named_unit<"g", kind_of> {} gram; - inline constexpr struct kilogram : decltype(kilo) {} kilogram; + inline constexpr struct second final : named_unit<"s", kind_of> {} second; + inline constexpr struct metre final : named_unit<"m", kind_of> {} metre; + inline constexpr struct gram final : named_unit<"g", kind_of> {} gram; + inline constexpr auto kilogram = kilo; - inline constexpr struct newton : named_unit<"N", kilogram * metre / square(second)> {} newton; - inline constexpr struct joule : named_unit<"J", newton * metre> {} joule; - inline constexpr struct watt : named_unit<"W", joule / second> {} watt; - inline constexpr struct coulomb : named_unit<"C", ampere * second> {} coulomb; - inline constexpr struct volt : named_unit<"V", watt / ampere> {} volt; - inline constexpr struct farad : named_unit<"F", coulomb / volt> {} farad; - inline constexpr struct ohm : named_unit<{u8"Ω", "ohm"}, volt / ampere> {} ohm; + inline constexpr struct newton final : named_unit<"N", kilogram * metre / square(second)> {} newton; + inline constexpr struct joule final : named_unit<"J", newton * metre> {} joule; + inline constexpr struct watt final : named_unit<"W", joule / second> {} watt; + inline constexpr struct coulomb final : named_unit<"C", ampere * second> {} coulomb; + inline constexpr struct volt final : named_unit<"V", watt / ampere> {} volt; + inline constexpr struct farad final : named_unit<"F", coulomb / volt> {} farad; + inline constexpr struct ohm final : named_unit<{u8"Ω", "ohm"}, volt / ampere> {} ohm; ``` === "Prefixes" @@ -75,13 +75,13 @@ and units of derived quantities. === "Constants" ```cpp - inline constexpr struct hyperfine_structure_transition_frequency_of_cs : named_unit<{u8"Δν_Cs", "dv_Cs"}, mag<9'192'631'770> * hertz> {} hyperfine_structure_transition_frequency_of_cs; - inline constexpr struct speed_of_light_in_vacuum : named_unit<"c", mag<299'792'458> * metre / second> {} speed_of_light_in_vacuum; - inline constexpr struct planck_constant : named_unit<"h", mag_ratio<662'607'015, 100'000'000> * mag_power<10, -34> * joule * second> {} planck_constant; - inline constexpr struct elementary_charge : named_unit<"e", mag_ratio<1'602'176'634, 1'000'000'000> * mag_power<10, -19> * coulomb> {} elementary_charge; - inline constexpr struct boltzmann_constant : named_unit<"k", mag_ratio<1'380'649, 1'000'000> * mag_power<10, -23> * joule / kelvin> {} boltzmann_constant; - inline constexpr struct avogadro_constant : named_unit<"N_A", mag_ratio<602'214'076, 100'000'000> * mag_power<10, 23> / mole> {} avogadro_constant; - inline constexpr struct luminous_efficacy : named_unit<"K_cd", mag<683> * lumen / watt> {} luminous_efficacy; + inline constexpr struct hyperfine_structure_transition_frequency_of_cs final : named_unit<{u8"Δν_Cs", "dv_Cs"}, mag<9'192'631'770> * hertz> {} hyperfine_structure_transition_frequency_of_cs; + inline constexpr struct speed_of_light_in_vacuum final : named_unit<"c", mag<299'792'458> * metre / second> {} speed_of_light_in_vacuum; + inline constexpr struct planck_constant final : named_unit<"h", mag_ratio<662'607'015, 100'000'000> * mag_power<10, -34> * joule * second> {} planck_constant; + inline constexpr struct elementary_charge final : named_unit<"e", mag_ratio<1'602'176'634, 1'000'000'000> * mag_power<10, -19> * coulomb> {} elementary_charge; + inline constexpr struct boltzmann_constant final : named_unit<"k", mag_ratio<1'380'649, 1'000'000> * mag_power<10, -23> * joule / kelvin> {} boltzmann_constant; + inline constexpr struct avogadro_constant final : named_unit<"N_A", mag_ratio<602'214'076, 100'000'000> * mag_power<10, 23> / mole> {} avogadro_constant; + inline constexpr struct luminous_efficacy final : named_unit<"K_cd", mag<683> * lumen / watt> {} luminous_efficacy; ``` !!! important @@ -105,7 +105,7 @@ and units of derived quantities. template name to initialize it with two symbols: ```cpp - inline constexpr struct ohm : named_unit {} ohm; + inline constexpr struct ohm final : named_unit {} ohm; ``` diff --git a/docs/users_guide/framework_basics/the_affine_space.md b/docs/users_guide/framework_basics/the_affine_space.md index c21bceea..54fe7cf2 100644 --- a/docs/users_guide/framework_basics/the_affine_space.md +++ b/docs/users_guide/framework_basics/the_affine_space.md @@ -172,7 +172,7 @@ origin. ![affine_space_2](affine_space_2.svg){style="width:80%;display: block;margin: 0 auto;"} ```cpp -inline constexpr struct origin : absolute_point_origin {} origin; +inline constexpr struct origin final : absolute_point_origin {} origin; // quantity_point qp1{100 * m}; // Compile-time error // quantity_point qp2{120 * m}; // Compile-time error @@ -197,14 +197,6 @@ assert(origin - qp2 == -120 * m); // assert(origin - origin == 0 * m); // Compile-time error ``` -!!! info - - The `absolute_point_origin` class template uses the CRTP idiom to enforce the uniqueness of - such a type. You should pass the type of a derived class as the first argument of the template - instantiation. - -*[CRTP]: Curiously Recurring Template Parameter - We can't construct a quantity point directly from the quantity anymore when a custom, named origin is used. To prevent potential safety and maintenance issues, we always need to explicitly provide both a compatible origin and a quantity measured from it to construct a quantity @@ -249,8 +241,8 @@ type and unit is being used: ![affine_space_3](affine_space_3.svg){style="width:80%;display: block;margin: 0 auto;"} ```cpp -inline constexpr struct origin1 : absolute_point_origin {} origin1; -inline constexpr struct origin2 : absolute_point_origin {} origin2; +inline constexpr struct origin1 final : absolute_point_origin {} origin1; +inline constexpr struct origin2 final : absolute_point_origin {} origin2; quantity_point qp1 = origin1 + 100 * m; quantity_point qp2 = origin2 + 120 * m; @@ -284,10 +276,10 @@ For such cases, relative point origins should be used: ![affine_space_4](affine_space_4.svg){style="width:80%;display: block;margin: 0 auto;"} ```cpp -inline constexpr struct A : absolute_point_origin {} A; -inline constexpr struct B : relative_point_origin {} B; -inline constexpr struct C : relative_point_origin {} C; -inline constexpr struct D : relative_point_origin {} D; +inline constexpr struct A final : absolute_point_origin {} A; +inline constexpr struct B final : relative_point_origin {} B; +inline constexpr struct C final : relative_point_origin {} C; +inline constexpr struct D final : relative_point_origin {} D; quantity_point qp1 = C + 100 * m; quantity_point qp2 = D + 120 * m; @@ -392,17 +384,17 @@ point origins for this purpose: ```cpp namespace si { -inline constexpr struct absolute_zero : absolute_point_origin {} absolute_zero; -inline constexpr struct zeroth_kelvin : decltype(absolute_zero) {} zeroth_kelvin; +inline constexpr struct absolute_zero final : absolute_point_origin {} absolute_zero; +inline constexpr auto zeroth_kelvin = absolute_zero; -inline constexpr struct ice_point : relative_point_origin}> {} ice_point; -inline constexpr struct zeroth_degree_Celsius : decltype(ice_point) {} zeroth_degree_Celsius; +inline constexpr struct ice_point final : relative_point_origin}> {} ice_point; +inline constexpr auto zeroth_degree_Celsius = ice_point; } namespace usc { -inline constexpr struct zeroth_degree_Fahrenheit : +inline constexpr struct zeroth_degree_Fahrenheit final : relative_point_origin * si::degree_Celsius)}> {} zeroth_degree_Fahrenheit; } @@ -426,16 +418,16 @@ definitions: ```cpp namespace si { -inline constexpr struct kelvin : +inline constexpr struct kelvin final : named_unit<"K", kind_of, zeroth_kelvin> {} kelvin; -inline constexpr struct degree_Celsius : +inline constexpr struct degree_Celsius final : named_unit<{u8"°C", "`C"}, kelvin, zeroth_degree_Celsius> {} degree_Celsius; } namespace usc { -inline constexpr struct degree_Fahrenheit : +inline constexpr struct degree_Fahrenheit final : named_unit<{u8"°F", "`F"}, mag_ratio<5, 9> * si::degree_Celsius, zeroth_degree_Fahrenheit> {} degree_Fahrenheit; @@ -481,7 +473,7 @@ the following way: ![affine_space_6](affine_space_6.svg){style="width:80%;display: block;margin: 0 auto;"} ```cpp -constexpr struct room_reference_temp : relative_point_origin {} room_reference_temp; +constexpr struct room_reference_temp final : relative_point_origin {} room_reference_temp; using room_temp = quantity_point; constexpr auto step_delta = isq::Celsius_temperature(0.5 * deg_C); diff --git a/docs/users_guide/framework_basics/value_conversions.md b/docs/users_guide/framework_basics/value_conversions.md index 8b57100d..bb9d6559 100644 --- a/docs/users_guide/framework_basics/value_conversions.md +++ b/docs/users_guide/framework_basics/value_conversions.md @@ -85,11 +85,11 @@ the `value_cast(q)` which always returns the most precise result: === "C++23" ```cpp - inline constexpr struct dim_currency : base_dimension<"$"> {} dim_currency; - inline constexpr struct currency : quantity_spec {} currency; + inline constexpr struct dim_currency final : base_dimension<"$"> {} dim_currency; + inline constexpr struct currency final : quantity_spec {} currency; - inline constexpr struct us_dollar : named_unit<"USD", kind_of> {} us_dollar; - inline constexpr struct scaled_us_dollar : named_unit<"USD_s", mag_power<10, -8> * us_dollar> {} scaled_us_dollar; + inline constexpr struct us_dollar final : named_unit<"USD", kind_of> {} us_dollar; + inline constexpr struct scaled_us_dollar final : named_unit<"USD_s", mag_power<10, -8> * us_dollar> {} scaled_us_dollar; namespace unit_symbols { @@ -105,11 +105,11 @@ the `value_cast(q)` which always returns the most precise result: === "C++20" ```cpp - inline constexpr struct dim_currency : base_dimension<"$"> {} dim_currency; - inline constexpr struct currency : quantity_spec {} currency; + inline constexpr struct dim_currency final : base_dimension<"$"> {} dim_currency; + inline constexpr struct currency final : quantity_spec {} currency; - inline constexpr struct us_dollar : named_unit<"USD", kind_of> {} us_dollar; - inline constexpr struct scaled_us_dollar : named_unit<"USD_s", mag_power<10, -8> * us_dollar> {} scaled_us_dollar; + inline constexpr struct us_dollar final : named_unit<"USD", kind_of> {} us_dollar; + inline constexpr struct scaled_us_dollar final : named_unit<"USD_s", mag_power<10, -8> * us_dollar> {} scaled_us_dollar; namespace unit_symbols { @@ -125,11 +125,11 @@ the `value_cast(q)` which always returns the most precise result: === "Portable" ```cpp - inline constexpr struct dim_currency : base_dimension<"$"> {} dim_currency; + inline constexpr struct dim_currency final : base_dimension<"$"> {} dim_currency; QUANTITY_SPEC(currency, dim_currency); - inline constexpr struct us_dollar : named_unit<"USD", kind_of> {} us_dollar; - inline constexpr struct scaled_us_dollar : named_unit<"USD_s", mag_power<10, -8> * us_dollar> {} scaled_us_dollar; + inline constexpr struct us_dollar final : named_unit<"USD", kind_of> {} us_dollar; + inline constexpr struct scaled_us_dollar final : named_unit<"USD_s", mag_power<10, -8> * us_dollar> {} scaled_us_dollar; namespace unit_symbols { diff --git a/docs/users_guide/use_cases/interoperability_with_other_libraries.md b/docs/users_guide/use_cases/interoperability_with_other_libraries.md index ee3bbf77..4ffddfe3 100644 --- a/docs/users_guide/use_cases/interoperability_with_other_libraries.md +++ b/docs/users_guide/use_cases/interoperability_with_other_libraries.md @@ -256,8 +256,8 @@ include the _mp-units/systems/si/chrono.h_ file to benefit from it. This file pr to the `std::chrono` abstractions: ```cpp - inline constexpr struct ts_origin : relative_point_origin + 1 * h> {} ts_origin; - inline constexpr struct my_origin : absolute_point_origin {} my_origin; + inline constexpr struct ts_origin final : relative_point_origin + 1 * h> {} ts_origin; + inline constexpr struct my_origin final : absolute_point_origin {} my_origin; quantity_point qp1 = sys_seconds{1s}; auto tp1 = to_chrono_time_point(qp1); // OK diff --git a/docs/users_guide/use_cases/wide_compatibility.md b/docs/users_guide/use_cases/wide_compatibility.md index 85dd916d..b3420d12 100644 --- a/docs/users_guide/use_cases/wide_compatibility.md +++ b/docs/users_guide/use_cases/wide_compatibility.md @@ -29,7 +29,7 @@ your code using **mp-units**: // ... - inline constexpr struct horizontal_length : quantity_spec {} horizontal_length; + inline constexpr struct horizontal_length final : quantity_spec {} horizontal_length; // ... @@ -45,7 +45,7 @@ your code using **mp-units**: // ... - inline constexpr struct horizontal_length : quantity_spec {} horizontal_length; + inline constexpr struct horizontal_length final : quantity_spec {} horizontal_length; // ... @@ -65,7 +65,7 @@ your code using **mp-units**: // ... - inline constexpr struct horizontal_length : quantity_spec {} horizontal_length; + inline constexpr struct horizontal_length final : quantity_spec {} horizontal_length; // ... @@ -85,7 +85,7 @@ your code using **mp-units**: // ... - inline constexpr struct horizontal_length : quantity_spec {} horizontal_length; + inline constexpr struct horizontal_length final : quantity_spec {} horizontal_length; // ... @@ -96,6 +96,7 @@ your code using **mp-units**: ```cpp #include + #include #ifdef MP_UNITS_MODULES #include import mp_units; @@ -157,3 +158,30 @@ from additional features provided with the library). This macro resolves to either the `std` or `fmt` namespace, depending on the value of [MP_UNITS_API_STD_FORMAT](../../getting_started/installation_and_usage.md#MP_UNITS_API_STD_FORMAT) CMake option. + +To include the header files of the underlying text formatting framework, the following include +should be used: + +```cpp +#include +``` + +### Contracts + +The mp-units library internally does contract checking by default. It can be disabled with a Conan +or CMake option. However, when enabled, it can use either [gsl-lite](https://github.com/gsl-lite/gsl-lite) +or [ms-gsl](https://github.com/microsoft/GSL). To write a code that is independent from the +underlying framework, the following preprocessor macros are exposed: + +- `MP_UNITS_EXPECTS(expr)` +- `MP_UNITS_EXPECTS_DEBUG(expr)` +- `MP_UNITS_ASSERT(expr)` +- `MP_UNITS_ASSERT_DEBUG(expr)` + +Their meaning is consistent with respective [gsl-lite](https://github.com/gsl-lite/gsl-lite?tab=readme-ov-file#contract-checking-configuration-macros). + +Also, to include the header files of the underlying framework, the following include should be used: + +```cpp +#include +``` diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 5325cd8d..0ce2d875 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -28,32 +28,12 @@ if(${projectPrefix}BUILD_CXX_MODULES) target_compile_features(example_utils INTERFACE cxx_std_20) target_compile_definitions(example_utils INTERFACE ${projectPrefix}MODULES) target_include_directories(example_utils INTERFACE include) + target_link_libraries(example_utils INTERFACE mp-units-contracts) endif() add_library(example_utils-headers INTERFACE) target_include_directories(example_utils-headers INTERFACE include) - -if(${projectPrefix}API_CONTRACTS STREQUAL "NONE") - target_compile_definitions(mp-units-core INTERFACE ${projectPrefix}API_CONTRACTS=0) -elseif(${projectPrefix}API_CONTRACTS STREQUAL "GSL-LITE") - if(NOT TARGET gsl::gsl-lite) - find_package(gsl-lite REQUIRED) - endif() - if(${projectPrefix}BUILD_CXX_MODULES) - target_link_libraries(example_utils INTERFACE gsl::gsl-lite) - endif() - target_link_libraries(example_utils-headers INTERFACE gsl::gsl-lite) - target_compile_definitions(mp-units-core INTERFACE ${projectPrefix}API_CONTRACTS=2) -elseif(${projectPrefix}API_CONTRACTS STREQUAL "MS-GSL") - if(NOT TARGET Microsoft.GSL::GSL) - find_package(Microsoft.GSL REQUIRED) - endif() - if(${projectPrefix}BUILD_CXX_MODULES) - target_link_libraries(example_utils INTERFACE Microsoft.GSL::GSL) - endif() - target_link_libraries(example_utils-headers INTERFACE Microsoft.GSL::GSL) - target_compile_definitions(mp-units-core INTERFACE ${projectPrefix}API_CONTRACTS=3) -endif() +target_link_libraries(example_utils-headers INTERFACE mp-units-contracts) # # add_example(target ...) diff --git a/example/clcpp_response.cpp b/example/clcpp_response.cpp index dbdca1e3..a1f252ad 100644 --- a/example/clcpp_response.cpp +++ b/example/clcpp_response.cpp @@ -16,6 +16,7 @@ */ #include +#include #include #ifdef MP_UNITS_MODULES import mp_units; diff --git a/example/conversion_factor.cpp b/example/conversion_factor.cpp index b95c018e..cc5ff291 100644 --- a/example/conversion_factor.cpp +++ b/example/conversion_factor.cpp @@ -16,6 +16,7 @@ */ #include +#include #include #include #include diff --git a/example/currency.cpp b/example/currency.cpp index a638fe86..b34fdc40 100644 --- a/example/currency.cpp +++ b/example/currency.cpp @@ -36,14 +36,14 @@ import mp_units.core; using namespace mp_units; // clang-format off -inline constexpr struct dim_currency : base_dimension<"$"> {} dim_currency; +inline constexpr struct dim_currency final : base_dimension<"$"> {} dim_currency; QUANTITY_SPEC(currency, dim_currency); -inline constexpr struct euro : named_unit<"EUR", kind_of> {} euro; -inline constexpr struct us_dollar : named_unit<"USD", kind_of> {} us_dollar; -inline constexpr struct great_british_pound : named_unit<"GBP", kind_of> {} great_british_pound; -inline constexpr struct japanese_jen : named_unit<"JPY", kind_of> {} japanese_jen; +inline constexpr struct euro final : named_unit<"EUR", kind_of> {} euro; +inline constexpr struct us_dollar final : named_unit<"USD", kind_of> {} us_dollar; +inline constexpr struct great_british_pound final : named_unit<"GBP", kind_of> {} great_british_pound; +inline constexpr struct japanese_jen final : named_unit<"JPY", kind_of> {} japanese_jen; // clang-format on namespace unit_symbols { diff --git a/example/foot_pound_second.cpp b/example/foot_pound_second.cpp index f62804f5..2bb1e7d0 100644 --- a/example/foot_pound_second.cpp +++ b/example/foot_pound_second.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include +#include #include #include #include diff --git a/example/glide_computer.cpp b/example/glide_computer.cpp index 4da86872..e263d149 100644 --- a/example/glide_computer.cpp +++ b/example/glide_computer.cpp @@ -24,6 +24,7 @@ #include "glide_computer_lib.h" #include #include +#include #include #include #include diff --git a/example/glide_computer_lib/glide_computer_lib.cpp b/example/glide_computer_lib/glide_computer_lib.cpp index 7c144939..6342a905 100644 --- a/example/glide_computer_lib/glide_computer_lib.cpp +++ b/example/glide_computer_lib/glide_computer_lib.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include "glide_computer_lib.h" +#include #include #include #include diff --git a/example/hello_units.cpp b/example/hello_units.cpp index 283b223e..f11f8a3a 100644 --- a/example/hello_units.cpp +++ b/example/hello_units.cpp @@ -26,6 +26,7 @@ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #include +#include #include #include #ifdef MP_UNITS_MODULES diff --git a/example/include/geographic.h b/example/include/geographic.h index 5366303c..accda550 100644 --- a/example/include/geographic.h +++ b/example/include/geographic.h @@ -24,6 +24,7 @@ #include "ranged_representation.h" #include +#include #include #include #include @@ -39,7 +40,7 @@ import mp_units; namespace geographic { -inline constexpr struct mean_sea_level : mp_units::absolute_point_origin { +inline constexpr struct mean_sea_level final : mp_units::absolute_point_origin { } mean_sea_level; using msl_altitude = mp_units::quantity_point; @@ -67,10 +68,9 @@ struct MP_UNITS_STD_FMT::formatter : namespace geographic { -inline constexpr struct equator : mp_units::absolute_point_origin { +inline constexpr struct equator final : mp_units::absolute_point_origin { } equator; -inline constexpr struct prime_meridian : - mp_units::absolute_point_origin { +inline constexpr struct prime_meridian final : mp_units::absolute_point_origin { } prime_meridian; diff --git a/example/include/ranged_representation.h b/example/include/ranged_representation.h index 9fc493cd..350a3b04 100644 --- a/example/include/ranged_representation.h +++ b/example/include/ranged_representation.h @@ -25,6 +25,7 @@ #include "validated_type.h" #include #include +#include #include #include #include diff --git a/example/include/validated_type.h b/example/include/validated_type.h index 9a429e26..30722c50 100644 --- a/example/include/validated_type.h +++ b/example/include/validated_type.h @@ -24,6 +24,8 @@ #include #include +#include +#include #include // IWYU pragma: export #include #include diff --git a/example/kalman_filter/kalman.h b/example/kalman_filter/kalman.h index 86659065..501dd2cc 100644 --- a/example/kalman_filter/kalman.h +++ b/example/kalman_filter/kalman.h @@ -23,6 +23,7 @@ #pragma once #include +#include #include #include #include diff --git a/example/kalman_filter/kalman_filter-example_1.cpp b/example/kalman_filter/kalman_filter-example_1.cpp index a5f8778a..7bfb370a 100644 --- a/example/kalman_filter/kalman_filter-example_1.cpp +++ b/example/kalman_filter/kalman_filter-example_1.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include "kalman.h" +#include #include #include #ifdef MP_UNITS_MODULES diff --git a/example/kalman_filter/kalman_filter-example_2.cpp b/example/kalman_filter/kalman_filter-example_2.cpp index d9fab7f9..a291813f 100644 --- a/example/kalman_filter/kalman_filter-example_2.cpp +++ b/example/kalman_filter/kalman_filter-example_2.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include "kalman.h" +#include #include #include #ifdef MP_UNITS_MODULES diff --git a/example/kalman_filter/kalman_filter-example_3.cpp b/example/kalman_filter/kalman_filter-example_3.cpp index 931cba60..a69c39e6 100644 --- a/example/kalman_filter/kalman_filter-example_3.cpp +++ b/example/kalman_filter/kalman_filter-example_3.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include "kalman.h" +#include #include #include #ifdef MP_UNITS_MODULES diff --git a/example/kalman_filter/kalman_filter-example_4.cpp b/example/kalman_filter/kalman_filter-example_4.cpp index 073bc4a1..b04ae971 100644 --- a/example/kalman_filter/kalman_filter-example_4.cpp +++ b/example/kalman_filter/kalman_filter-example_4.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include "kalman.h" +#include #include #include #ifdef MP_UNITS_MODULES diff --git a/example/kalman_filter/kalman_filter-example_5.cpp b/example/kalman_filter/kalman_filter-example_5.cpp index 8a594ded..d12a22fe 100644 --- a/example/kalman_filter/kalman_filter-example_5.cpp +++ b/example/kalman_filter/kalman_filter-example_5.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include "kalman.h" +#include #include #include #ifdef MP_UNITS_MODULES diff --git a/example/kalman_filter/kalman_filter-example_6.cpp b/example/kalman_filter/kalman_filter-example_6.cpp index 59ce7a0f..2dc55ba1 100644 --- a/example/kalman_filter/kalman_filter-example_6.cpp +++ b/example/kalman_filter/kalman_filter-example_6.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include "kalman.h" +#include #include #include #ifdef MP_UNITS_MODULES @@ -63,7 +64,7 @@ int main() const quantity process_noise_variance = 0.0001 * pow<2>(deg_C); const estimate initial{state{qp{60. * deg_C}}, 100. * deg_C}; - const std::array measurements = {qp{49.986 * deg_C}, qp{49.963 * deg_C}, qp{50.09 * deg_C}, qp{50.001 * deg_C}, + const std::array measurements = {qp{49.986 * deg_C}, qp{49.963 * deg_C}, qp{50.097 * deg_C}, qp{50.001 * deg_C}, qp{50.018 * deg_C}, qp{50.05 * deg_C}, qp{49.938 * deg_C}, qp{49.858 * deg_C}, qp{49.965 * deg_C}, qp{50.114 * deg_C}}; const quantity measurement_error = 0.1 * deg_C; diff --git a/example/kalman_filter/kalman_filter-example_7.cpp b/example/kalman_filter/kalman_filter-example_7.cpp index ecf52f96..62ce652b 100644 --- a/example/kalman_filter/kalman_filter-example_7.cpp +++ b/example/kalman_filter/kalman_filter-example_7.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include "kalman.h" +#include #include #include #ifdef MP_UNITS_MODULES diff --git a/example/kalman_filter/kalman_filter-example_8.cpp b/example/kalman_filter/kalman_filter-example_8.cpp index e9e06da0..760212ec 100644 --- a/example/kalman_filter/kalman_filter-example_8.cpp +++ b/example/kalman_filter/kalman_filter-example_8.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include "kalman.h" +#include #include #include #ifdef MP_UNITS_MODULES diff --git a/example/si_constants.cpp b/example/si_constants.cpp index f88b08fe..56b016a8 100644 --- a/example/si_constants.cpp +++ b/example/si_constants.cpp @@ -26,6 +26,7 @@ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #include +#include #include #ifdef MP_UNITS_MODULES import mp_units; diff --git a/example/spectroscopy_units.cpp b/example/spectroscopy_units.cpp index ae12080b..e64eee8f 100644 --- a/example/spectroscopy_units.cpp +++ b/example/spectroscopy_units.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include +#include #include #include #ifdef MP_UNITS_MODULES diff --git a/example/storage_tank.cpp b/example/storage_tank.cpp index 3622e081..7e004cf0 100644 --- a/example/storage_tank.cpp +++ b/example/storage_tank.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include +#include #include #include #include diff --git a/example/unmanned_aerial_vehicle.cpp b/example/unmanned_aerial_vehicle.cpp index 23b05ccf..bfcf536d 100644 --- a/example/unmanned_aerial_vehicle.cpp +++ b/example/unmanned_aerial_vehicle.cpp @@ -23,6 +23,7 @@ #include "geographic.h" #include "ranged_representation.h" #include +#include #include #include #include @@ -30,6 +31,7 @@ #ifdef MP_UNITS_MODULES import mp_units; #else +#include #include #include #include @@ -43,7 +45,7 @@ using namespace geographic; enum class earth_gravity_model : std::int8_t { egm84_15, egm95_5, egm2008_1 }; template -struct height_above_ellipsoid_t : absolute_point_origin, isq::altitude> { +struct height_above_ellipsoid_t final : absolute_point_origin { static constexpr earth_gravity_model egm = M; }; template @@ -113,7 +115,7 @@ hae_altitude to_hae(msl_altitude msl, position pos) // **** HAL **** // clang-format off -inline constexpr struct height_above_launch : absolute_point_origin {} height_above_launch; +inline constexpr struct height_above_launch final : absolute_point_origin {} height_above_launch; // clang-format on using hal_altitude = quantity_point; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cee2b7f1..d51ddfda 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -83,26 +83,29 @@ check_cxx_feature_supported(__cpp_lib_format ${projectPrefix}LIB_FORMAT_SUPPORTE check_cxx_feature_supported("__cpp_constexpr >= 202211L" ${projectPrefix}STATIC_CONSTEXPR_VARS_IN_CONSTEXPR_FUNCTIONS) check_cxx_feature_supported(__cpp_explicit_this_parameter ${projectPrefix}EXPLICIT_THIS_PARAMETER_SUPPORTED) +# libc++ has a basic supports for std::format but does not set __cpp_lib_format +# https://github.com/llvm/llvm-project/issues/77773 +if(NOT ${projectPrefix}LIB_FORMAT_SUPPORTED + AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang" + AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "17" + AND ${projectPrefix}LIBCXX +) + message(STATUS "Clang 17+ with libc++ detected, overriding `std::format` support") + set(${projectPrefix}LIB_FORMAT_SUPPORTED 1) +endif() + # validate settings -if(NOT ${projectPrefix}API_FREESTANDING - AND ${projectPrefix}API_STD_FORMAT STREQUAL "TRUE" - AND NOT - (${projectPrefix}LIB_FORMAT_SUPPORTED - # libc++ has a basic supports for std::format but does not set __cpp_lib_format - # https://github.com/llvm/llvm-project/issues/77773 - OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "17" - AND ${projectPrefix}LIBCXX)) +if(NOT ${projectPrefix}API_FREESTANDING AND "${projectPrefix}API_STD_FORMAT" AND NOT + ${projectPrefix}LIB_FORMAT_SUPPORTED ) message(FATAL_ERROR "`std::format` enabled but not supported") endif() -if(${projectPrefix}API_STRING_VIEW_RET STREQUAL "TRUE" AND NOT - ${projectPrefix}STATIC_CONSTEXPR_VARS_IN_CONSTEXPR_FUNCTIONS -) +if("${projectPrefix}API_STRING_VIEW_RET" AND NOT ${projectPrefix}STATIC_CONSTEXPR_VARS_IN_CONSTEXPR_FUNCTIONS) message(FATAL_ERROR "`std::string_view` returns enabled but not supported") endif() -if(${projectPrefix}API_NO_CRTP STREQUAL "TRUE" AND NOT ${projectPrefix}EXPLICIT_THIS_PARAMETER_SUPPORTED) +if("${projectPrefix}API_NO_CRTP" AND NOT ${projectPrefix}EXPLICIT_THIS_PARAMETER_SUPPORTED) message(FATAL_ERROR "`NO_CRTP` mode enabled but explicit `this` parameter is not supported") endif() @@ -116,6 +119,7 @@ else() endif() # components/modules +include(MPUnitsContracts) add_subdirectory(core) add_subdirectory(systems) diff --git a/src/cmake/MPUnitsContracts.cmake b/src/cmake/MPUnitsContracts.cmake new file mode 100644 index 00000000..6b45c24e --- /dev/null +++ b/src/cmake/MPUnitsContracts.cmake @@ -0,0 +1,41 @@ +# 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. + +add_library(mp-units-contracts INTERFACE) + +if(${projectPrefix}API_CONTRACTS STREQUAL "NONE") + target_compile_definitions(mp-units-contracts INTERFACE ${projectPrefix}API_CONTRACTS=0) +elseif(${projectPrefix}API_CONTRACTS STREQUAL "GSL-LITE") + if(NOT TARGET gsl::gsl-lite) + find_package(gsl-lite REQUIRED) + endif() + target_link_libraries(mp-units-contracts INTERFACE gsl::gsl-lite) + target_compile_definitions(mp-units-contracts INTERFACE ${projectPrefix}API_CONTRACTS=2) +elseif(${projectPrefix}API_CONTRACTS STREQUAL "MS-GSL") + if(NOT TARGET Microsoft.GSL::GSL) + find_package(Microsoft.GSL REQUIRED) + endif() + target_link_libraries(mp-units-contracts INTERFACE Microsoft.GSL::GSL) + target_compile_definitions(mp-units-contracts INTERFACE ${projectPrefix}API_CONTRACTS=3) +endif() + +install(TARGETS mp-units-contracts EXPORT mp-unitsTargets) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9d2839f3..e49a1203 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -23,8 +23,7 @@ cmake_minimum_required(VERSION 3.23) function(set_feature_flag name) - set(val_list "TRUE" "FALSE") - if(${projectPrefix}${name} IN_LIST val_list) + if(NOT ${projectPrefix}${name} STREQUAL "AUTO") target_compile_definitions( mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}${name}=$ ) @@ -36,15 +35,16 @@ add_mp_units_module( core mp-units-core HEADERS include/mp-units/bits/core_gmf.h include/mp-units/bits/get_associated_quantity.h - include/mp-units/bits/get_common_base.h include/mp-units/bits/hacks.h include/mp-units/bits/math_concepts.h include/mp-units/bits/module_macros.h + include/mp-units/bits/quantity_spec_hierarchy.h include/mp-units/bits/ratio.h include/mp-units/bits/sudo_cast.h include/mp-units/bits/text_tools.h include/mp-units/bits/type_list.h include/mp-units/ext/algorithm.h + include/mp-units/ext/contracts.h include/mp-units/ext/fixed_string.h include/mp-units/ext/prime.h include/mp-units/ext/type_name.h @@ -86,6 +86,7 @@ if(NOT ${projectPrefix}API_FREESTANDING) FILES include/mp-units/bits/fmt.h include/mp-units/bits/requires_hosted.h + include/mp-units/ext/format.h include/mp-units/math.h include/mp-units/ostream.h include/mp-units/format.h @@ -99,8 +100,8 @@ set_feature_flag(API_NO_CRTP) # Text formatting if(NOT ${projectPrefix}API_FREESTANDING - AND (${projectPrefix}API_STD_FORMAT STREQUAL "FALSE" OR (${projectPrefix}API_STD_FORMAT STREQUAL "AUTO" - AND NOT ${projectPrefix}LIB_FORMAT_SUPPORTED)) + AND (NOT ${projectPrefix}API_STD_FORMAT OR (${projectPrefix}API_STD_FORMAT STREQUAL "AUTO" + AND NOT ${projectPrefix}LIB_FORMAT_SUPPORTED)) ) if(NOT TARGET fmt::fmt) find_package(fmt REQUIRED) @@ -109,21 +110,7 @@ if(NOT ${projectPrefix}API_FREESTANDING endif() # Contracts checking -if(${projectPrefix}API_CONTRACTS STREQUAL "NONE") - target_compile_definitions(mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}API_CONTRACTS=0) -elseif(${projectPrefix}API_CONTRACTS STREQUAL "GSL-LITE") - if(NOT TARGET gsl::gsl-lite) - find_package(gsl-lite REQUIRED) - endif() - target_link_libraries(mp-units-core ${${projectPrefix}TARGET_SCOPE} gsl::gsl-lite) - target_compile_definitions(mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}API_CONTRACTS=2) -elseif(${projectPrefix}API_CONTRACTS STREQUAL "MS-GSL") - if(NOT TARGET Microsoft.GSL::GSL) - find_package(Microsoft.GSL REQUIRED) - endif() - target_link_libraries(mp-units-core ${${projectPrefix}TARGET_SCOPE} Microsoft.GSL::GSL) - target_compile_definitions(mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}API_CONTRACTS=3) -endif() +target_link_libraries(mp-units-core ${${projectPrefix}TARGET_SCOPE} mp-units-contracts) # C++20 modules if(${projectPrefix}BUILD_CXX_MODULES) diff --git a/src/core/include/mp-units/bits/core_gmf.h b/src/core/include/mp-units/bits/core_gmf.h index 21d97e8f..c920c6ae 100644 --- a/src/core/include/mp-units/bits/core_gmf.h +++ b/src/core/include/mp-units/bits/core_gmf.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -44,23 +45,13 @@ #include #if MP_UNITS_HOSTED +#include #include #include #include #include #include #include - -#if MP_UNITS_USE_FMTLIB -MP_UNITS_DIAGNOSTIC_PUSH -MP_UNITS_DIAGNOSTIC_IGNORE_UNREACHABLE -MP_UNITS_DIAGNOSTIC_IGNORE_SHADOW -#include -MP_UNITS_DIAGNOSTIC_POP -#else -#include -#endif - #endif #if __cpp_lib_text_encoding diff --git a/src/core/include/mp-units/bits/fmt.h b/src/core/include/mp-units/bits/fmt.h index 8e84ac0b..e48b39e7 100644 --- a/src/core/include/mp-units/bits/fmt.h +++ b/src/core/include/mp-units/bits/fmt.h @@ -35,6 +35,8 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE +#include +#include #include #include #include diff --git a/src/core/include/mp-units/bits/get_common_base.h b/src/core/include/mp-units/bits/quantity_spec_hierarchy.h similarity index 78% rename from src/core/include/mp-units/bits/get_common_base.h rename to src/core/include/mp-units/bits/quantity_spec_hierarchy.h index e703a41f..46c2006a 100644 --- a/src/core/include/mp-units/bits/get_common_base.h +++ b/src/core/include/mp-units/bits/quantity_spec_hierarchy.h @@ -56,7 +56,7 @@ template } template -[[nodiscard]] consteval auto have_common_base(A a, B b) +[[nodiscard]] consteval bool have_common_base(A a, B b) { constexpr std::size_t a_length = hierarchy_path_length(A{}); constexpr std::size_t b_length = hierarchy_path_length(B{}); @@ -68,7 +68,7 @@ template template requires(have_common_base_in_hierarchy_of_equal_length(A{}, B{})) -[[nodiscard]] consteval auto get_common_base_for_hierarchy_of_equal_length(A a, B b) +[[nodiscard]] consteval QuantitySpec auto get_common_base_for_hierarchy_of_equal_length(A a, B b) { if constexpr (is_same_v) return a; @@ -78,14 +78,29 @@ template template requires(have_common_base(A{}, B{})) -[[nodiscard]] consteval auto get_common_base(A a, B b) +[[nodiscard]] consteval QuantitySpec auto get_common_base(A a, B b) { - constexpr int a_length = hierarchy_path_length(A{}); - constexpr int b_length = hierarchy_path_length(B{}); + constexpr std::size_t a_length = hierarchy_path_length(A{}); + constexpr std::size_t b_length = hierarchy_path_length(B{}); if constexpr (a_length > b_length) return get_common_base_for_hierarchy_of_equal_length(hierarchy_path_advance(a), b); else return get_common_base_for_hierarchy_of_equal_length(a, hierarchy_path_advance(b)); } +template +[[nodiscard]] consteval bool is_child_of(Child ch, Parent p) +{ + if constexpr (Child{} == Parent{}) + return std::true_type{}; + else { + constexpr std::size_t child_length = hierarchy_path_length(Child{}); + constexpr std::size_t parent_length = hierarchy_path_length(Parent{}); + if constexpr (parent_length > child_length) + return false; + else + return hierarchy_path_advance(ch) == p; + } +} + } // namespace mp_units::detail diff --git a/src/core/include/mp-units/bits/ratio.h b/src/core/include/mp-units/bits/ratio.h index bcf778dd..85e49afc 100644 --- a/src/core/include/mp-units/bits/ratio.h +++ b/src/core/include/mp-units/bits/ratio.h @@ -27,6 +27,7 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE +#include #include // IWYU pragma: export #include #include diff --git a/src/core/include/mp-units/compat_macros.h b/src/core/include/mp-units/compat_macros.h index 026f04e9..99fafa40 100644 --- a/src/core/include/mp-units/compat_macros.h +++ b/src/core/include/mp-units/compat_macros.h @@ -28,14 +28,14 @@ #ifdef MP_UNITS_API_NO_CRTP -#define QUANTITY_SPEC(name, ...) \ - inline constexpr struct name : ::mp_units::quantity_spec<__VA_ARGS__> { \ +#define QUANTITY_SPEC(name, ...) \ + inline constexpr struct name final : ::mp_units::quantity_spec<__VA_ARGS__> { \ } name #else -#define QUANTITY_SPEC(name, ...) \ - inline constexpr struct name : ::mp_units::quantity_spec { \ +#define QUANTITY_SPEC(name, ...) \ + inline constexpr struct name final : ::mp_units::quantity_spec { \ } name #endif @@ -83,26 +83,12 @@ #ifndef MP_UNITS_IN_MODULE_INTERFACE -// IWYU pragma: begin_exports -#if MP_UNITS_USE_FMTLIB -MP_UNITS_DIAGNOSTIC_PUSH -MP_UNITS_DIAGNOSTIC_IGNORE_UNREACHABLE -MP_UNITS_DIAGNOSTIC_IGNORE_SHADOW -#include -MP_UNITS_DIAGNOSTIC_POP -#else -#include -#endif -// IWYU pragma: end_exports - #endif #endif // MP_UNITS_HOSTED #if MP_UNITS_API_CONTRACTS == 2 || __has_include() -#include - #define MP_UNITS_EXPECTS(expr) gsl_Expects(expr) #define MP_UNITS_EXPECTS_DEBUG(expr) gsl_ExpectsDebug(expr) #define MP_UNITS_ASSERT(expr) gsl_Assert(expr) @@ -110,9 +96,6 @@ MP_UNITS_DIAGNOSTIC_POP #elif MP_UNITS_API_CONTRACTS == 3 || __has_include() -#include -#include - #define MP_UNITS_EXPECTS(expr) Expects(expr) #if defined NDEBUG #define MP_UNITS_EXPECTS_DEBUG(expr) static_cast(0) diff --git a/src/core/include/mp-units/ext/contracts.h b/src/core/include/mp-units/ext/contracts.h new file mode 100644 index 00000000..b274b021 --- /dev/null +++ b/src/core/include/mp-units/ext/contracts.h @@ -0,0 +1,49 @@ +// 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 + +#include +#include + +#if MP_UNITS_API_CONTRACTS == 2 || __has_include() + +#if MP_UNITS_HOSTED +#include +#else +#include +#endif + +#elif MP_UNITS_API_CONTRACTS == 3 || __has_include() + +#if MP_UNITS_HOSTED +#include +#include +#else +#include +#endif + +#endif + +#endif // MP_UNITS_IN_MODULE_INTERFACE diff --git a/src/core/include/mp-units/ext/fixed_string.h b/src/core/include/mp-units/ext/fixed_string.h index 1cfe2369..c562fdfe 100644 --- a/src/core/include/mp-units/ext/fixed_string.h +++ b/src/core/include/mp-units/ext/fixed_string.h @@ -32,12 +32,14 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE +#include #include // IWYU pragma: export #include #include #include #include #if MP_UNITS_HOSTED +#include #include #endif #endif diff --git a/src/core/include/mp-units/ext/format.h b/src/core/include/mp-units/ext/format.h new file mode 100644 index 00000000..72aca3a9 --- /dev/null +++ b/src/core/include/mp-units/ext/format.h @@ -0,0 +1,42 @@ +// 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 + +#include +// +#include +#include + +#if MP_UNITS_USE_FMTLIB +MP_UNITS_DIAGNOSTIC_PUSH +MP_UNITS_DIAGNOSTIC_IGNORE_UNREACHABLE +MP_UNITS_DIAGNOSTIC_IGNORE_SHADOW +#include +MP_UNITS_DIAGNOSTIC_POP +#else +#include +#endif + +#endif diff --git a/src/core/include/mp-units/framework/dimension.h b/src/core/include/mp-units/framework/dimension.h index 4aa746dd..71d24719 100644 --- a/src/core/include/mp-units/framework/dimension.h +++ b/src/core/include/mp-units/framework/dimension.h @@ -34,6 +34,7 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE +#include #include #include #include @@ -61,9 +62,9 @@ namespace mp_units { * For example: * * @code{.cpp} - * inline constexpr struct dim_length : base_dimension<"L"> {} dim_length; - * inline constexpr struct dim_time : base_dimension<"T"> {} dim_time; - * inline constexpr struct dim_mass : base_dimension<"M"> {} dim_mass; + * inline constexpr struct dim_length final : base_dimension<"L"> {} dim_length; + * inline constexpr struct dim_time final : base_dimension<"T"> {} dim_time; + * inline constexpr struct dim_mass final : base_dimension<"M"> {} dim_mass; * @endcode * * @note A common convention in this library is to assign the same name for a type and an object of this type. @@ -86,6 +87,9 @@ struct base_dimension_less : std::bool_constant<(Lhs::symbol < Rhs::symbol)> {}; template using type_list_of_base_dimension_less = expr_less; +template +struct derived_dimension_impl : detail::expr_fractions {}; + } // namespace detail /** @@ -131,7 +135,7 @@ using type_list_of_base_dimension_less = expr_less; * instantiate this type automatically based on the dimensional arithmetic equation provided by the user. */ template -struct derived_dimension : detail::expr_fractions {}; +struct derived_dimension final : detail::derived_dimension_impl {}; /** * @brief Dimension one @@ -140,7 +144,7 @@ struct derived_dimension : detail::expr_fractions { +MP_UNITS_EXPORT inline constexpr struct dimension_one final : detail::derived_dimension_impl<> { } dimension_one; namespace detail { @@ -279,13 +283,13 @@ constexpr Out dimension_symbol_impl(Out out, const type_list& nums, con } template Out, typename... Expr> -constexpr Out dimension_symbol_impl(Out out, const derived_dimension&, const dimension_symbol_formatting& fmt, - bool negative_power) +constexpr Out dimension_symbol_impl(Out out, const derived_dimension_impl&, + const dimension_symbol_formatting& fmt, bool negative_power) { (void)negative_power; MP_UNITS_EXPECTS(negative_power == false); - return dimension_symbol_impl(out, typename derived_dimension::_num_{}, - typename derived_dimension::_den_{}, fmt); + return dimension_symbol_impl(out, typename derived_dimension_impl::_num_{}, + typename derived_dimension_impl::_den_{}, fmt); } diff --git a/src/core/include/mp-units/framework/dimension_concepts.h b/src/core/include/mp-units/framework/dimension_concepts.h index 11be622d..35913606 100644 --- a/src/core/include/mp-units/framework/dimension_concepts.h +++ b/src/core/include/mp-units/framework/dimension_concepts.h @@ -42,20 +42,13 @@ template inline constexpr bool is_derived_from_specialization_of_base_dimension = requires(T* t) { to_base_specialization_of_base_dimension(t); }; -template -inline constexpr bool is_specialization_of_base_dimension = false; - -template -inline constexpr bool is_specialization_of_base_dimension> = true; - /** * @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 = - is_derived_from_specialization_of_base_dimension && (!is_specialization_of_base_dimension); +concept BaseDimension = is_derived_from_specialization_of_base_dimension && std::is_final_v; template struct is_dimension_one : std::false_type {}; @@ -91,7 +84,8 @@ namespace detail { * being the `dimension_one`. */ template -concept DerivedDimension = is_specialization_of || is_dimension_one::value; +concept DerivedDimension = + (is_specialization_of || is_dimension_one::value) && std::is_final_v; } // namespace detail diff --git a/src/core/include/mp-units/framework/expression_template.h b/src/core/include/mp-units/framework/expression_template.h index 291bfedb..0e1c0dc2 100644 --- a/src/core/include/mp-units/framework/expression_template.h +++ b/src/core/include/mp-units/framework/expression_template.h @@ -29,6 +29,7 @@ #ifndef MP_UNITS_IN_MODULE_INTERFACE #include +#include #endif namespace mp_units { diff --git a/src/core/include/mp-units/framework/magnitude.h b/src/core/include/mp-units/framework/magnitude.h index bd18ab52..fb547c62 100644 --- a/src/core/include/mp-units/framework/magnitude.h +++ b/src/core/include/mp-units/framework/magnitude.h @@ -517,8 +517,7 @@ template constexpr T get_value(const magnitude&) { // Force the expression to be evaluated in a constexpr context, to catch, e.g., overflow. - constexpr auto result = detail::checked_static_cast((detail::compute_base_power(Ms) * ... * T{1})); - + constexpr T result = detail::checked_static_cast((detail::compute_base_power(Ms) * ... * T{1})); return result; } @@ -677,16 +676,6 @@ template [[nodiscard]] consteval auto denominator(Magnitude auto m) { return numerator(pow<-1>(m)); } -// TODO This probably should not be exported but is used in chrono.h -MP_UNITS_EXPORT constexpr ratio as_ratio(Magnitude auto m) - requires(is_rational(decltype(m){})) -{ - return ratio{ - get_value(numerator(m)), - get_value(denominator(m)), - }; -} - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Common Magnitude. // @@ -758,7 +747,7 @@ template template [[nodiscard]] consteval auto common_magnitude_type_impl(magnitude) { - return (decltype(get_base_value(Ms)){} * ... * std::intmax_t{}); + return (std::intmax_t{} * ... * decltype(get_base_value(Ms)){}); } // Returns the most precise type to express the magnitude factor diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index fb63eb82..587ab2a2 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -37,6 +37,7 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE +#include #include // IWYU pragma: export #include #endif @@ -45,16 +46,21 @@ namespace mp_units { namespace detail { -template -concept IntegralConversionFactor = Unit && Unit && - is_integral(get_canonical_unit(UFrom).mag / get_canonical_unit(UTo).mag); +template +[[nodiscard]] consteval bool integral_conversion_factor(UFrom from, UTo to) +{ + if constexpr (is_same_v) + return true; + else + return is_integral(get_canonical_unit(from).mag / get_canonical_unit(to).mag); +} template concept QuantityConvertibleTo = Quantity && Quantity && implicitly_convertible(QFrom::quantity_spec, QTo::quantity_spec) && convertible(QFrom::unit, QTo::unit) && (treat_as_floating_point || - (!treat_as_floating_point && IntegralConversionFactor)) && + (!treat_as_floating_point && (integral_conversion_factor(QFrom::unit, QTo::unit)))) && // TODO consider providing constraints of sudo_cast here rather than testing if it can be called (its return type is // deduced thus the function is evaluated here and may emit truncating conversion or other warnings) requires(QFrom q) { detail::sudo_cast(q); }; diff --git a/src/core/include/mp-units/framework/quantity_point.h b/src/core/include/mp-units/framework/quantity_point.h index 24eb9957..35288afb 100644 --- a/src/core/include/mp-units/framework/quantity_point.h +++ b/src/core/include/mp-units/framework/quantity_point.h @@ -35,11 +35,10 @@ namespace mp_units { -MP_UNITS_EXPORT template +MP_UNITS_EXPORT template // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility) struct absolute_point_origin { static constexpr QuantitySpec auto quantity_spec = QS; - using _type_ = absolute_point_origin; }; MP_UNITS_EXPORT template @@ -56,7 +55,7 @@ struct relative_point_origin { }; template -struct zeroth_point_origin_ : absolute_point_origin, QS> {}; +struct zeroth_point_origin_ final : absolute_point_origin {}; MP_UNITS_EXPORT template inline constexpr zeroth_point_origin_ zeroth_point_origin; @@ -81,9 +80,8 @@ MP_UNITS_EXPORT template [[nodiscard]] consteval bool operator==(PO1 po1, PO2 po2) { if constexpr (detail::AbsolutePointOrigin && detail::AbsolutePointOrigin) - return is_same_v || - (detail::is_zeroth_point_origin(po1) && detail::is_zeroth_point_origin(po2) && - interconvertible(po1.quantity_spec, po2.quantity_spec)); + 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::RelativePointOrigin && detail::RelativePointOrigin) return PO1::quantity_point == PO2::quantity_point; else if constexpr (detail::RelativePointOrigin) 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 aa2a13cc..f5d8af83 100644 --- a/src/core/include/mp-units/framework/quantity_point_concepts.h +++ b/src/core/include/mp-units/framework/quantity_point_concepts.h @@ -32,7 +32,7 @@ namespace mp_units { -MP_UNITS_EXPORT template +MP_UNITS_EXPORT template struct absolute_point_origin; namespace detail { @@ -40,22 +40,15 @@ namespace detail { template inline constexpr bool is_quantity_point = false; -template -inline constexpr bool is_specialization_of_absolute_point_origin = false; - -template -inline constexpr bool is_specialization_of_absolute_point_origin> = true; - -template -void to_base_specialization_of_absolute_point_origin(const volatile absolute_point_origin*); +template +void to_base_specialization_of_absolute_point_origin(const volatile absolute_point_origin*); template inline constexpr bool is_derived_from_specialization_of_absolute_point_origin = requires(T* t) { to_base_specialization_of_absolute_point_origin(t); }; template -concept AbsolutePointOrigin = - is_derived_from_specialization_of_absolute_point_origin && !is_specialization_of_absolute_point_origin; +concept AbsolutePointOrigin = is_derived_from_specialization_of_absolute_point_origin && std::is_final_v; } // namespace detail @@ -72,12 +65,6 @@ struct relative_point_origin; namespace detail { -template -inline constexpr bool is_specialization_of_relative_point_origin = false; - -template -inline constexpr bool is_specialization_of_relative_point_origin> = true; - template void to_base_specialization_of_relative_point_origin(const volatile relative_point_origin*); @@ -86,8 +73,7 @@ inline constexpr bool is_derived_from_specialization_of_relative_point_origin = requires(T* t) { to_base_specialization_of_relative_point_origin(t); }; template -concept RelativePointOrigin = - is_derived_from_specialization_of_relative_point_origin && !is_specialization_of_relative_point_origin; +concept RelativePointOrigin = is_derived_from_specialization_of_relative_point_origin && std::is_final_v; } // namespace detail diff --git a/src/core/include/mp-units/framework/quantity_spec.h b/src/core/include/mp-units/framework/quantity_spec.h index 383a61f3..855dd25c 100644 --- a/src/core/include/mp-units/framework/quantity_spec.h +++ b/src/core/include/mp-units/framework/quantity_spec.h @@ -23,9 +23,9 @@ #pragma once // IWYU pragma: private, include -#include #include #include +#include #include #include #include @@ -199,13 +199,13 @@ MP_UNITS_EXPORT_END * For example: * * @code{.cpp} - * inline constexpr struct dim_length : base_dimension<"L"> {} dim_length; - * inline constexpr struct dim_mass : base_dimension<"M"> {} dim_mass; - * inline constexpr struct dim_time : base_dimension<"T"> {} dim_time; + * inline constexpr struct dim_length final : base_dimension<"L"> {} dim_length; + * inline constexpr struct dim_mass final : base_dimension<"M"> {} dim_mass; + * inline constexpr struct dim_time final : base_dimension<"T"> {} dim_time; * - * inline constexpr struct length : quantity_spec {} length; - * inline constexpr struct mass : quantity_spec {} mass; - * inline constexpr struct time : quantity_spec {} time; + * inline constexpr struct length final : quantity_spec {} length; + * inline constexpr struct mass final : quantity_spec {} mass; + * inline constexpr struct time final : quantity_spec {} time; * @endcode * * @note A common convention in this library is to assign the same name for a type and an object of this type. @@ -225,6 +225,7 @@ template) struct quantity_spec : detail::quantity_spec_interface { #endif + using _base_type_ = quantity_spec; static constexpr detail::BaseDimension auto dimension = Dim; static constexpr quantity_character character = detail::quantity_character_init(quantity_character::scalar); }; @@ -243,12 +244,12 @@ struct quantity_spec : detail::quantity_spec_interface * For example: * * @code{.cpp} - * inline constexpr struct area : quantity_spec(length)> {} area; - * inline constexpr struct volume : quantity_spec(length)> {} volume; - * inline constexpr struct velocity : quantity_spec {} velocity; - * inline constexpr struct speed : quantity_spec {} speed; - * inline constexpr struct force : quantity_spec {} force; - * inline constexpr struct power : quantity_spec {} power; + * inline constexpr struct area final : quantity_spec(length)> {} area; + * inline constexpr struct volume final : quantity_spec(length)> {} volume; + * inline constexpr struct velocity final : quantity_spec {} velocity; + * inline constexpr struct speed final : quantity_spec {} speed; + * inline constexpr struct force final : quantity_spec {} force; + * inline constexpr struct power final : quantity_spec {} power; * @endcode * * @note A common convention in this library is to assign the same name for a type and an object of this type. @@ -260,19 +261,32 @@ struct quantity_spec : detail::quantity_spec_interface * @tparam Args optionally a value of a `quantity_character` in case the base quantity should not be scalar */ #ifdef MP_UNITS_API_NO_CRTP -template auto... Args> +template auto... Args> requires(... && !QuantitySpec) struct quantity_spec : detail::quantity_spec_interface { #else -template auto... Args> +template auto... Args> requires(... && !QuantitySpec) struct quantity_spec : detail::quantity_spec_interface { #endif + using _base_type_ = quantity_spec; static constexpr auto _equation_ = Eq; static constexpr Dimension auto dimension = Eq.dimension; static constexpr quantity_character character = detail::quantity_character_init(Eq.character); }; +namespace detail { + +template +struct propagate_equation {}; + +template +struct propagate_equation { + static constexpr auto _equation_ = Q._equation_; +}; + +} // namespace detail + /** * @brief Specialization defining a leaf quantity in the hierarchy * @@ -285,10 +299,10 @@ struct quantity_spec : detail::quantity_spec_interface * For example: * * @code{.cpp} - * inline constexpr struct width : quantity_spec {} width; - * inline constexpr struct height : quantity_spec {} height; - * inline constexpr struct diameter : quantity_spec {} diameter; - * inline constexpr struct position_vector : quantity_spec {} position_vector; + * inline constexpr struct width final : quantity_spec {} width; + * inline constexpr struct height final : quantity_spec {} height; + * inline constexpr struct diameter final : quantity_spec {} diameter; + * inline constexpr struct position_vector final : quantity_spec {} position_vector; * @endcode * * @note A common convention in this library is to assign the same name for a type and an object of this type. @@ -303,13 +317,15 @@ struct quantity_spec : detail::quantity_spec_interface #ifdef MP_UNITS_API_NO_CRTP template auto... Args> requires(... && !QuantitySpec) -struct quantity_spec : decltype(QS) { +struct quantity_spec : detail::propagate_equation, detail::quantity_spec_interface { #else template auto... Args> requires(... && !QuantitySpec) -struct quantity_spec : decltype(QS) { +struct quantity_spec : detail::propagate_equation, detail::quantity_spec_interface { #endif + using _base_type_ = quantity_spec; static constexpr auto _parent_ = QS; + static constexpr Dimension auto dimension = _parent_.dimension; static constexpr quantity_character character = detail::quantity_character_init(QS.character); #ifndef MP_UNITS_API_NO_CRTP @@ -330,6 +346,7 @@ struct quantity_spec : decltype(QS) { #endif }; +// clang-format off /** * @brief Specialization defining a leaf derived quantity in the hierarchy and refining paren't equation * @@ -343,10 +360,10 @@ struct quantity_spec : decltype(QS) { * For example: * * @code{.cpp} - * inline constexpr struct angular_measure : quantity_spec {} - * angular_measure; inline constexpr struct velocity : quantity_spec {} velocity; - * inline constexpr struct weight : quantity_spec {} weight; - * inline constexpr struct kinetic_energy : quantity_spec(speed)> {} kinetic_energy; + * inline constexpr struct angular_measure final : quantity_spec {} angular_measure; + * inline constexpr struct velocity final : quantity_spec {} velocity; + * inline constexpr struct weight final : quantity_spec {} weight; + * inline constexpr struct kinetic_energy final : quantity_spec(speed)> {} kinetic_energy; * @endcode * * @note A common convention in this library is to assign the same name for a type and an object of this type. @@ -358,25 +375,50 @@ struct quantity_spec : decltype(QS) { * @tparam Args optionally a value of a `quantity_character` in case the base quantity should not be scalar * or `is_kind` in case the quantity starts a new hierarchy tree of a kind */ +// clang-format on #ifdef MP_UNITS_API_NO_CRTP -template auto... Args> requires(!requires { QS._equation_; } || (requires { QS._equation_; } && (explicitly_convertible(Eq, QS._equation_)))) && (... && !QuantitySpec) -struct quantity_spec : quantity_spec { +struct quantity_spec : detail::quantity_spec_interface { #else -template auto... Args> requires(!requires { QS._equation_; } || (requires { QS._equation_; } && (explicitly_convertible(Eq, QS._equation_)))) && (... && !QuantitySpec) -struct quantity_spec : quantity_spec { +struct quantity_spec : detail::quantity_spec_interface { #endif + using _base_type_ = quantity_spec; + static constexpr auto _parent_ = QS; static constexpr auto _equation_ = Eq; + static constexpr Dimension auto dimension = _parent_.dimension; static constexpr quantity_character character = detail::quantity_character_init(Eq.character); }; +namespace detail { + +template +struct derived_quantity_spec_impl : +#ifdef MP_UNITS_API_NO_CRTP + detail::quantity_spec_interface, +#else + detail::quantity_spec_interface>, +#endif + detail::expr_fractions { + using _base_type_ = derived_quantity_spec_impl; + using _base_ = detail::expr_fractions; + + static constexpr Dimension auto dimension = + detail::expr_map(_base_{}); + static constexpr quantity_character character = + detail::derived_quantity_character(typename _base_::_num_{}, typename _base_::_den_{}); +}; + +} // namespace detail /** * @brief A specification of a derived quantity @@ -421,22 +463,8 @@ struct quantity_spec : quantity_spec { * @note User should not instantiate this type! It is not exported from the C++ module. The library will * instantiate this type automatically based on the dimensional arithmetic equation provided by the user. */ -template -struct derived_quantity_spec : -#ifdef MP_UNITS_API_NO_CRTP - detail::quantity_spec_interface, -#else - detail::quantity_spec_interface>, -#endif - detail::expr_fractions { - using _base_ = detail::expr_fractions; - - static constexpr Dimension auto dimension = - detail::expr_map(_base_{}); - static constexpr quantity_character character = - detail::derived_quantity_character(typename _base_::_num_{}, typename _base_::_den_{}); -}; +template +struct derived_quantity_spec final : detail::derived_quantity_spec_impl {}; /** * @brief Quantity of dimension one @@ -446,6 +474,13 @@ struct derived_quantity_spec : */ MP_UNITS_EXPORT QUANTITY_SPEC(dimensionless, derived_quantity_spec<>{}); +namespace detail { + +template<> +struct is_dimensionless : std::true_type {}; + +} // namespace detail + /** * @brief Quantity kind specifier * @@ -454,7 +489,7 @@ MP_UNITS_EXPORT QUANTITY_SPEC(dimensionless, derived_quantity_spec<>{}); namespace detail { template -concept QuantitySpecWithNoSpecifiers = detail::NamedQuantitySpec || detail::IntermediateDerivedQuantitySpec; +concept QuantitySpecWithNoSpecifiers = detail::NamedQuantitySpec || detail::DerivedQuantitySpec; template [[nodiscard]] consteval QuantitySpec auto get_kind_tree_root(Q q); @@ -464,7 +499,8 @@ template #ifdef MP_UNITS_API_NO_CRTP template requires detail::QuantitySpecWithNoSpecifiers && (detail::get_kind_tree_root(Q{}) == Q{}) -struct kind_of_ : Q { +struct kind_of_ final : Q::_base_type_ { + using _base_type_ = kind_of_; static constexpr auto _quantity_spec_ = Q{}; }; #else @@ -476,7 +512,8 @@ template template requires(detail::get_kind_tree_root(Q{}) == Q{}) #endif -struct kind_of_ : quantity_spec, Q{}> { +struct kind_of_ final : quantity_spec, Q{}>::_base_type_ { + using _base_type_ = kind_of_; static constexpr auto _quantity_spec_ = Q{}; }; #endif @@ -487,9 +524,6 @@ inline constexpr kind_of_ kind_of; namespace detail { -template<> -struct is_dimensionless : std::true_type {}; - template [[nodiscard]] consteval QuantitySpec auto clone_kind_of(Q q) { @@ -556,7 +590,7 @@ template return dimensionless; else if constexpr (detail::ratio{Num, Den} == 1) return q; - else if constexpr (detail::IntermediateDerivedQuantitySpec) + else if constexpr (detail::DerivedQuantitySpec) return detail::clone_kind_of( detail::expr_pow( detail::remove_kind(q))); @@ -590,8 +624,6 @@ MP_UNITS_EXPORT_END namespace detail { -enum class specs_convertible_result : std::int8_t { no, cast, explicit_conversion, yes }; - template [[nodiscard]] consteval int get_complexity(Q); @@ -616,7 +648,7 @@ template template [[nodiscard]] consteval int get_complexity(Q) { - if constexpr (detail::IntermediateDerivedQuantitySpec) + if constexpr (detail::DerivedQuantitySpec) return get_complexity(typename Q::_num_{}) + get_complexity(typename Q::_den_{}); else if constexpr (requires { Q::_equation_; }) return 1 + get_complexity(Q::_equation_); @@ -659,6 +691,8 @@ template return true; } +enum class specs_convertible_result : std::int8_t { no, cast, explicit_conversion, yes }; + template struct explode_to_equation_result { Q equation; @@ -709,7 +743,7 @@ explode_result(Q) -> explode_result; #endif -template +template [[nodiscard]] consteval auto explode(Q q); template @@ -771,7 +805,7 @@ template return explode_result{dimensionless}; } -template +template [[nodiscard]] consteval auto explode(Q q) { constexpr auto c = get_complexity(Q{}); @@ -1304,7 +1338,7 @@ template return specs_convertible_result::yes; } -template +template [[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(From, To) { return are_ingredients_convertible(type_list_sort{}, @@ -1313,7 +1347,7 @@ template{}); } -template +template [[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(From, To) { return are_ingredients_convertible(type_list_sort{}, @@ -1321,7 +1355,7 @@ template type_list{}, type_list<>{}); } -template +template [[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(From, To) { return are_ingredients_convertible(type_list{}, type_list<>{}, @@ -1353,8 +1387,8 @@ template using enum specs_convertible_result; if constexpr (have_common_base(From{}, To{})) { - if constexpr (std::derived_from) return yes; - if constexpr (std::derived_from) return explicit_conversion; + if constexpr (is_child_of(From{}, To{})) return yes; + if constexpr (is_child_of(To{}, From{})) return explicit_conversion; if constexpr (get_kind(From{}) == get_kind(To{})) return cast; return no; } else if constexpr (get_kind(From{}) != get_kind(To{})) @@ -1385,9 +1419,9 @@ template return yes; else if constexpr (NamedQuantitySpec && NamedQuantitySpec) { return convertible_named(from, to); - } else if constexpr (IntermediateDerivedQuantitySpec && IntermediateDerivedQuantitySpec) { + } else if constexpr (DerivedQuantitySpec && DerivedQuantitySpec) { return are_ingredients_convertible(from, to); - } else if constexpr (IntermediateDerivedQuantitySpec) { + } else if constexpr (DerivedQuantitySpec) { auto res = explode(from); if constexpr (NamedQuantitySpec) return convertible_impl(res.quantity, to); @@ -1396,7 +1430,7 @@ template return min(eq.result, convertible_impl(res.quantity, eq.equation)); } else return are_ingredients_convertible(from, to); - } else if constexpr (IntermediateDerivedQuantitySpec) { + } else if constexpr (DerivedQuantitySpec) { auto res = explode(to); if constexpr (NamedQuantitySpec) return min(res.result, convertible_impl(from, res.quantity)); @@ -1473,7 +1507,7 @@ template return q; } else if constexpr (requires { Q::_parent_; }) { return get_kind_tree_root(Q::_parent_); - } else if constexpr (detail::IntermediateDerivedQuantitySpec) { + } else if constexpr (detail::DerivedQuantitySpec) { return detail::expr_map(q); } else { @@ -1511,11 +1545,11 @@ template else if constexpr (detail::NestedQuantityKindSpecOf) return detail::remove_kind(q2); else if constexpr ((detail::QuantityKindSpec && !detail::QuantityKindSpec) || - (detail::IntermediateDerivedQuantitySpec && detail::NamedQuantitySpec && + (detail::DerivedQuantitySpec && detail::NamedQuantitySpec && implicitly_convertible(Q1{}, Q2{}))) return q2; else if constexpr ((!detail::QuantityKindSpec && detail::QuantityKindSpec) || - (detail::NamedQuantitySpec && detail::IntermediateDerivedQuantitySpec && + (detail::NamedQuantitySpec && detail::DerivedQuantitySpec && implicitly_convertible(Q2{}, Q1{}))) return q1; else if constexpr (detail::have_common_base(Q1{}, Q2{})) 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 ab5b047d..dba816d0 100644 --- a/src/core/include/mp-units/framework/quantity_spec_concepts.h +++ b/src/core/include/mp-units/framework/quantity_spec_concepts.h @@ -64,25 +64,14 @@ template inline constexpr bool is_derived_from_specialization_of_quantity_spec = requires(T* t) { to_base_specialization_of_quantity_spec(t); }; -template -inline constexpr bool is_specialization_of_quantity_spec = false; - -#ifdef MP_UNITS_API_NO_CRTP -template -inline constexpr bool is_specialization_of_quantity_spec> = true; -#else -template -inline constexpr bool is_specialization_of_quantity_spec> = true; -#endif - /** * @brief Concept matching all named quantity specification types * * Satisfied by all types that derive from `quantity_spec`. */ template -concept NamedQuantitySpec = is_derived_from_specialization_of_quantity_spec && - (!is_specialization_of_quantity_spec)&&(!QuantityKindSpec); +concept NamedQuantitySpec = + is_derived_from_specialization_of_quantity_spec && std::is_final_v && (!QuantityKindSpec); template struct is_dimensionless : std::false_type {}; @@ -101,13 +90,12 @@ inline constexpr bool is_per_of_quantity_specs> = (... && (NamedQuantitySpec || is_dimensionless::value || is_power_of_quantity_spec)); template -concept IntermediateDerivedQuantitySpecExpr = - detail::NamedQuantitySpec || detail::is_dimensionless::value || detail::is_power_of_quantity_spec || - detail::is_per_of_quantity_specs; +concept DerivedQuantitySpecExpr = detail::NamedQuantitySpec || detail::is_dimensionless::value || + detail::is_power_of_quantity_spec || detail::is_per_of_quantity_specs; } // namespace detail -template +template struct derived_quantity_spec; namespace detail { @@ -121,7 +109,7 @@ namespace detail { * explicitly not supported here. */ template -concept IntermediateDerivedQuantitySpec = +concept DerivedQuantitySpec = is_specialization_of || (QuantityKindSpec && is_specialization_of, derived_quantity_spec>); @@ -130,20 +118,21 @@ concept IntermediateDerivedQuantitySpec = MP_UNITS_EXPORT template -concept QuantitySpec = - detail::NamedQuantitySpec || detail::IntermediateDerivedQuantitySpec || detail::QuantityKindSpec; +concept QuantitySpec = detail::NamedQuantitySpec || detail::DerivedQuantitySpec || detail::QuantityKindSpec; MP_UNITS_EXPORT template [[nodiscard]] consteval detail::QuantityKindSpec auto get_kind(Q q); namespace detail { -template -concept NestedQuantityKindSpecOf = - QuantitySpec && QuantitySpec && get_kind(From) != get_kind(To) && - std::derived_from>; +template +[[nodiscard]] consteval bool is_child_of(Child ch, Parent p); -} +template +concept NestedQuantityKindSpecOf = QuantitySpec && QuantitySpec && + get_kind(From) != get_kind(To) && is_child_of(To, get_kind(From)._quantity_spec_); + +} // namespace detail MP_UNITS_EXPORT template concept QuantitySpecOf = diff --git a/src/core/include/mp-units/framework/symbol_text.h b/src/core/include/mp-units/framework/symbol_text.h index e89aab96..a266cb24 100644 --- a/src/core/include/mp-units/framework/symbol_text.h +++ b/src/core/include/mp-units/framework/symbol_text.h @@ -31,6 +31,7 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE +#include #include // IWYU pragma: export #include #include diff --git a/src/core/include/mp-units/framework/system_reference.h b/src/core/include/mp-units/framework/system_reference.h index b4c202ec..5718cb1a 100644 --- a/src/core/include/mp-units/framework/system_reference.h +++ b/src/core/include/mp-units/framework/system_reference.h @@ -45,10 +45,10 @@ namespace mp_units { * @code{.cpp} * // hypothetical natural system of units for c=1 * - * inline constexpr struct second : named_unit<"s"> {} second; - * inline constexpr struct minute : named_unit<"min", mag<60> * second> {} minute; - * inline constexpr struct gram : named_unit<"g"> {} gram; - * inline constexpr struct kilogram : decltype(si::kilo) {} kilogram; + * inline constexpr struct second final : named_unit<"s"> {} second; + * inline constexpr struct minute final : named_unit<"min", mag<60> * second> {} minute; + * inline constexpr struct gram final : named_unit<"g"> {} gram; + * inline constexpr auto kilogram = si::kilo; * * inline constexpr struct time : system_reference {} time; * inline constexpr struct length : system_reference {} length; diff --git a/src/core/include/mp-units/framework/unit.h b/src/core/include/mp-units/framework/unit.h index 03dde4f0..f5c140a7 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -41,6 +41,7 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE +#include #include #include #include @@ -62,6 +63,13 @@ struct propagate_point_origin { static constexpr auto point_origin = U::point_origin; }; +template +struct scaled_unit_impl : detail::propagate_point_origin { + using _base_type_ = scaled_unit_impl; // exposition only + static constexpr MP_UNITS_CONSTRAINED_AUTO_WORKAROUND(Magnitude) auto mag = M; + static constexpr U reference_unit{}; +}; + } // namespace detail /** @@ -74,10 +82,7 @@ struct propagate_point_origin { * instantiate this type automatically based on the unit arithmetic equation provided by the user. */ template -struct scaled_unit : detail::propagate_point_origin { - static constexpr MP_UNITS_CONSTRAINED_AUTO_WORKAROUND(Magnitude) auto mag = M; - static constexpr U reference_unit{}; -}; +struct scaled_unit final : detail::scaled_unit_impl {}; namespace detail { @@ -100,12 +105,12 @@ inline constexpr bool is_specialization_of_scaled_unit> = true * For example: * * @code{.cpp} - * inline constexpr struct second : named_unit<"s", time> {} second; - * inline constexpr struct metre : named_unit<"m", length> {} metre; - * inline constexpr struct hertz : named_unit<"Hz", inverse(second)> {} hertz; - * inline constexpr struct newton : named_unit<"N", kilogram * metre / square(second)> {} newton; - * inline constexpr struct degree_Celsius : named_unit<{u8"°C", "`C"}, kelvin> {} degree_Celsius; - * inline constexpr struct minute : named_unit<"min", mag<60> * second> {} minute; + * inline constexpr struct second final : named_unit<"s", kind_of