From b5ade4b1f6986c795e2e5267be7f24b6c494a664 Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Tue, 19 Apr 2022 19:26:03 +0200 Subject: [PATCH 01/18] improve documentation. --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3f258a6..77900ea 100644 --- a/README.md +++ b/README.md @@ -119,14 +119,14 @@ TU comes with its own test suite. It does not rely on any external testing tool. The following instruction assumes that you do an out of source build in a directory under the repository root. ```bat -cmake .. -G -cmake --build . --config +> cmake .. -G +> cmake --build . --config ``` Run the test suite -``` -ctest -V +```bat +> ctest -V ``` The test suite test TU for both float and double as underlying datatype. @@ -153,13 +153,13 @@ The intrinsic data type used by TU is defined in the preprocessor macro `TU_TYPE ### Namespaces The main namespace of TU is `tu`. -Functionality inside `tu` that is located in the namespace `internal` is not public and should only be used implicitly by public classes and methods. +Functionality inside `tu` that is located in the namespace `internal` is not public and should only be used implicitly through public classes and methods. ### Classes and structs #### s, m, kg, A, K, mol, cd -These are the base units with floating point template arguments that determins the power of the base unit. +These are the base units with floating point template arguments that determines the power of the base unit. The base units are used to build `Coherent_unit`s The definition of each base unit looks as follows where the unit is denoted `X`. @@ -488,8 +488,7 @@ Note that `unop` operates on the `base_value` on a unit. In the case of `degree` * metre_cubed * metre_squared -### Non coherent units - +### Non-coherent units #### Time From c5cb9cd9707e37721a679cd95320875d64ca8e98 Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Wed, 20 Apr 2022 19:32:52 +0200 Subject: [PATCH 02/18] Improve documentation --- README.md | 49 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 77900ea..d8ca5cf 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ The test suite test TU for both float and double as underlying datatype. The aim of TU is to be -* compliant to definitions and guides of official bodies. For SI units, TU aims for compliance with the definitions issued by Bureau International des Poids et Mesures (BIPM). See [link to bimp.org](https://www.bipm.org/documents/20126/41483022/SI-Brochure-9.pdf) for details. +* compliant to definitions and guides of official bodies. For SI units, TU aims for compliance with the definitions issued by Bureau International des Poids et Mesures (BIPM). See [bimp.org](https://www.bipm.org/documents/20126/41483022/SI-Brochure-9.pdf) for details. * (type)safe * easy to use * light weight @@ -146,30 +146,64 @@ TU is released under the MIT license. https://mit-license.org/ ## Detailed description +### Spelling + +TU uses official spelling of units. Therefore TU uses `litre` and `metre` and not *liter* and *meter*. + ### Types The intrinsic data type used by TU is defined in the preprocessor macro `TU_TYPE`. `TU_TYPE` can be `float` or `double`. All values and floating point template argumets will have the type defined by `TU_TYPE`. ### Namespaces + The main namespace of TU is `tu`. Functionality inside `tu` that is located in the namespace `internal` is not public and should only be used implicitly through public classes and methods. ### Classes and structs -#### s, m, kg, A, K, mol, cd +The TU unit system is built on five structs: `Base_unit`, `Coherent_unit`, `Non_coherent_unit`, `Unit` and the enum struct `prefix`. + +The illustration below shows how the different structs are used to create other structs. Structs at lower level uses structs on higher level in their construction. + +``` +prefix Base_unit +| | +| Coherent_unit +| | | +| | Non_coherent_unit +| | | +| | Non_coherent_unit +| | | +|--------Unit +``` + +With words the above would be written: -These are the base units with floating point template arguments that determines the power of the base unit. -The base units are used to build `Coherent_unit`s +* `Coherent_unit` is built from `Base_unit`(s) +* `Non_coherent_unit` is built from a `Coherent_unit` or another `Non_coherent_unit` +* `Unit` is built from a `prefix` and a `Coherent_unit` or a `Non_coherent_unit`. -The definition of each base unit looks as follows where the unit is denoted `X`. +The main entity that a typical user of TU will interact with is the `Unit` struct. When extending the unit system, interaction with other structs is required. At some occasions interaction with `Coherent_unit`s is necessary. + +#### Base_unit + +Base units are the smallest building blocks in the types system. These define powers of the seven basic units `s`, `m`, `kg`, `A`, `K`, `mol` and `cd`. Base units are used to build up `Coherent_unit`s. + +The definition of base units are done through inheritance of the `Base_unit` struct and looks as follows where the base unit is denoted `X`. ```c++ template struct X : internal::Base_unit

{}; ``` +The definition of `s` (second) to some power `p` then looks as + +```c++ +template +struct s : internal::Base_unit

{}; +``` -The base unit `per_second` can be declared through +and a base unit `per_second` can be declared through ```c++ s<(TU_TYPE)-1.0f>; @@ -177,7 +211,7 @@ s<(TU_TYPE)-1.0f>; #### Coherent_unit -The `Coherent_unit` struct represents a unit that is a multiple of all base units: s, m, kg, A, K, mol and cd. +The `Coherent_unit` struct represents a unit that is a multiple of all base units: s, m, kg, A, K, mol and cd with individual powers. A specific coherent unit should be defined by inheriting from a `Coherent_unit` The specific coherent unit `newton` is defined as @@ -195,6 +229,7 @@ Note that computations using seconds should use the `Coherent_unit` `second` and ```c++ struct second: Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; ``` + All base units are defined as `Coherent_unit`s in similar fashion. #### Non_coherent_unit From 17eeb550bbdbd75cd722fe542b42e9eaa450e13f Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Thu, 21 Apr 2022 18:59:58 +0200 Subject: [PATCH 03/18] Embed link to license. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d8ca5cf..57956db 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ The aim of TU is to be ## License -TU is released under the MIT license. https://mit-license.org/ +TU is released under the [MIT](https://mit-license.org/) license. ## Detailed description From bf4612e5dc9df43df58db7139c790a064b0e3e3d Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Thu, 21 Apr 2022 20:32:22 +0200 Subject: [PATCH 04/18] Added scalar coherent unit to facilitate binary operations between scalar and units. --- CHANGELOG.md | 16 ++++++++++++++++ test/test.cpp | 17 +++++++++++++++++ typesafe_units/include/tu/typesafe_units.h | 2 ++ 3 files changed, 35 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d94e3d6..5ff6597 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,23 @@ # Changelog + All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] + +### Added + +- New coherent unit `scalar` that can be used in binary operations with units. +- New constructor `Coherent_unit(TU_TYPE)` so that a coherent unit can be created with value. + +### Fixed + +- Ambiguities and typos in documentation. + +## [0.1.0] - 2022-04-19 + +### Added + +- Core functionality of **TU** diff --git a/test/test.cpp b/test/test.cpp index 74b50ae..a17d6d4 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -531,6 +531,23 @@ int main() { } ); + Test<"scalar binary operations">( + [](T &t) { + + Unit mult_res = Unit((TU_TYPE)2.0) * scalar((TU_TYPE)100.0); + Unit div_res = Unit((TU_TYPE)400.0) / scalar((TU_TYPE)2.0); + + t.assert_true(mult_res.value == (TU_TYPE)200.0, __LINE__); + t.assert_true(div_res.value == (TU_TYPE)200.0, __LINE__); + + Unit add_res = Unit((TU_TYPE)2.0) + scalar((TU_TYPE)1.0); + Unit sub_res = Unit((TU_TYPE)2.0) - scalar((TU_TYPE)1.0); + + t.assert_true(add_res.value == (TU_TYPE)3.0, __LINE__); + t.assert_true(add_res.value == (TU_TYPE)3.0, __LINE__); + } + ); + static_assert(tu::hour::base_multiplier == 3600.0f); return Test_stats::fail; } \ No newline at end of file diff --git a/typesafe_units/include/tu/typesafe_units.h b/typesafe_units/include/tu/typesafe_units.h index a795d6d..f4e75ed 100644 --- a/typesafe_units/include/tu/typesafe_units.h +++ b/typesafe_units/include/tu/typesafe_units.h @@ -242,6 +242,7 @@ template { Coherent_unit() = default; Coherent_unit(const internal::Coherent_unit_base& cb) : internal::Coherent_unit_base(cb) {} + Coherent_unit(TU_TYPE v) : internal::Coherent_unit_base(v){} }; namespace internal { @@ -477,6 +478,7 @@ struct candela: Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0> // // Dervived units with special names // +struct scalar : Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; struct hertz : Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; struct becquerel: Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; struct ohm: Coherent_unit, m<(TU_TYPE)2.0>, kg<(TU_TYPE)1.0>, A<(TU_TYPE)-2.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; From a7194c96525a8de55c77f9c11da784cb733fe28d Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Fri, 22 Apr 2022 15:36:02 +0200 Subject: [PATCH 05/18] Fix typo in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 57956db..276e08c 100644 --- a/README.md +++ b/README.md @@ -346,7 +346,7 @@ std::cout << tu::convert_to(m).value << std::endl; // prin ``` ### Operators -#### + - +#### + - TU supports the binary operators `+` and `-` (addition and subtraction) on units. Conversions are handled under the hood of TU. @@ -464,7 +464,7 @@ is equivalent to pow<0.5>(unit). ``` -Not that since TU uses floating point powers, it is not guaranteed that applying first `pow<2.0>` and the `sqrt` on a unit would yield the exact unit back. +Note that since TU uses floating point powers, it is not guaranteed that applying first `pow<2.0>` and the `sqrt` on a unit would yield the exact unit back. #### unop From 8a9c72a43293c23c650acdfd3cef05d7ba9fecea Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Wed, 28 Feb 2024 14:22:54 +0100 Subject: [PATCH 06/18] Change definition of PI: Use std definition Introduce quecto, ronto, ronna and quetta as prefixes as these are now a part of the SI standard. --- typesafe_units/include/tu/typesafe_units.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/typesafe_units/include/tu/typesafe_units.h b/typesafe_units/include/tu/typesafe_units.h index f4e75ed..57e0ca5 100644 --- a/typesafe_units/include/tu/typesafe_units.h +++ b/typesafe_units/include/tu/typesafe_units.h @@ -12,15 +12,18 @@ #include #include #include +#include namespace tu { -constexpr TU_TYPE PI = (TU_TYPE)3.1415926535897932384626433832795028841971693993751; +constexpr TU_TYPE PI = std::numbers::pi_v; // // Prefixes used to define units. // enum struct prefix { + quecto = -30, + ronto = -27, yocto = -24, zepto = -21, atto = -18, @@ -42,9 +45,10 @@ enum struct prefix { exa = 18, zetta = 21, yotta = 24, + ronna = 27, + quetta = 30, }; - namespace internal { // // Returns compile time calculation of 10^exp. From 4a8f319c0ee9e41584b2ac37b95ef0bb92420aa4 Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Wed, 28 Feb 2024 14:24:20 +0100 Subject: [PATCH 07/18] Fix typos in comments --- typesafe_units/include/tu/typesafe_units.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/typesafe_units/include/tu/typesafe_units.h b/typesafe_units/include/tu/typesafe_units.h index 57e0ca5..5a68803 100644 --- a/typesafe_units/include/tu/typesafe_units.h +++ b/typesafe_units/include/tu/typesafe_units.h @@ -277,7 +277,7 @@ struct Non_coherent_unit : Parent_unit { // Express one unit with prefix in a different unit. // Example: // Unit m(1.0f); -// std::cout << tu::convert_to(m).value << std::endl; // prints 60000.0 +// std::cout << tu::convert_to(m).value << std::endl; // prints 60000.0 // template convert_to(const Unit& from) no // Unit is the intended public unit class. // Prefix is an enum class intrinsically converted to the exponent of the prefix. // Example: -// Unit s = 3.0; -// +// Unit s = 3.0; +// template requires std::derived_from struct Unit : U::Base { From e39198db07969c2c5da0ee766d89b98117f6aa9c Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Sat, 2 Mar 2024 18:40:28 +0100 Subject: [PATCH 08/18] Make compile with std::ratio instead of TU_TYPE --- typesafe_units/include/tu/typesafe_units.h | 297 +++++++++++++-------- 1 file changed, 181 insertions(+), 116 deletions(-) diff --git a/typesafe_units/include/tu/typesafe_units.h b/typesafe_units/include/tu/typesafe_units.h index 5a68803..8084b98 100644 --- a/typesafe_units/include/tu/typesafe_units.h +++ b/typesafe_units/include/tu/typesafe_units.h @@ -13,11 +13,65 @@ #include #include #include +#include namespace tu { constexpr TU_TYPE PI = std::numbers::pi_v; +template +using r = std::ratio; + +//namespace internal { +template +using is_ratio = std::is_same, typename R::type>; + +template +inline constexpr bool is_ratio_v = is_ratio::value; + +template +constexpr bool are_ratios() { + if constexpr (sizeof...(R) == 0) + return is_ratio_v; + else + return is_ratio_v && are_ratios; +} + +template +requires (are_ratios()) +constexpr auto ratio_op(A a, B b) { + return op()(a,b); +} +//} + +template +requires (!std::is_integral_v && is_ratio_v) +constexpr F fraction(R) { + return static_cast(R::num) / static_cast(R::den); +} + +struct Plus { + template + constexpr auto operator() (A, B) -> std::ratio_add::type{ + return {}; + } +}; + +struct Minus { + template + constexpr auto operator() (A, B) -> std::ratio_subtract::type{ + return {}; + } +}; + +struct Multiply { + template + constexpr auto operator() (A, B) -> std::ratio_multiply::type{ + return {}; + } +}; + + // // Prefixes used to define units. // @@ -74,15 +128,16 @@ constexpr TU_TYPE pow10() noexcept { // Convenience struct to wrap a TU_TYPE representing an exponent in a template argument. // This makes it possibel to deduce the exponent argument from a function parameter. // -template +template struct powexp { constexpr powexp() noexcept {}; - static constexpr TU_TYPE exp = e; + //static constexpr TU_TYPE exp = e; }; -template +template +requires (are_ratios()) constexpr bool are_args_zero() noexcept { - if constexpr (U_first != (TU_TYPE)0.0) { + if constexpr (U_first::num != 0) { return false; } else if constexpr (sizeof...(U_args) > 0) { @@ -127,7 +182,7 @@ struct Unit_fundament{ // Coherent_unit_base<-2, 1, 1, 0, 0, 0, 0> represents the coherent SI unit // Newton (kg * m / s^2). // -template +template struct Coherent_unit_base : Unit_fundament { using Base = Coherent_unit_base; constexpr Coherent_unit_base() noexcept = default; @@ -157,52 +212,60 @@ struct Coherent_unit_base : Unit_fundament { // derived explicit units access to the template argument in terms of // the constexpr int `power`. // -template +template +requires (is_ratio_v

) struct Base_unit { - static constexpr TU_TYPE power = p; + using power = p; }; } // namespace internal // // Struct representation of base unit s (second) with power p // -template +template +requires (is_ratio_v

) struct s : internal::Base_unit

{}; // // Struct representation of base unit m (metre) with power p // -template +template +requires (is_ratio_v

) struct m : internal::Base_unit

{}; // // Struct representation of base unit kg (kilogram) with power p // -template +template +requires (is_ratio_v

) struct kg : internal::Base_unit

{}; // // Struct representation of base unit A (ampere) with power p // -template +template +requires (is_ratio_v

) struct A : internal::Base_unit

{}; // // Struct representation of base unit K (kelvin) with power p // -template +template +requires (is_ratio_v

) struct K : internal::Base_unit

{}; // // Struct representation of base unit mol (mole) with power p // -template +template +requires (is_ratio_v

) struct mol : internal::Base_unit

{}; // // Struct representation of base unit cd (candela) with power p // -template +template +requires (is_ratio_v

) struct cd : internal::Base_unit

{}; // @@ -210,25 +273,25 @@ struct cd : internal::Base_unit

{}; // of coherent units. // template -concept Second_power = std::is_same, Ty>::value; +concept Second_power = std::is_same, Ty>::value; template -concept Metre_power = std::is_same, Ty>::value; +concept Metre_power = std::is_same, Ty>::value; template -concept Kilogram_power = std::is_same, Ty>::value; +concept Kilogram_power = std::is_same, Ty>::value; template -concept Ampere_power = std::is_same, Ty>::value; +concept Ampere_power = std::is_same, Ty>::value; template -concept Kelvin_power = std::is_same, Ty>::value; +concept Kelvin_power = std::is_same, Ty>::value; template -concept Mole_power = std::is_same, Ty>::value; +concept Mole_power = std::is_same, Ty>::value; template -concept Candela_power = std::is_same, Ty>::value; +concept Candela_power = std::is_same, Ty>::value; // // Struct that represents a coherent unit. @@ -243,10 +306,10 @@ template -struct Coherent_unit: internal::Coherent_unit_base { +struct Coherent_unit: internal::Coherent_unit_base { Coherent_unit() = default; - Coherent_unit(const internal::Coherent_unit_base& cb) : internal::Coherent_unit_base(cb) {} - Coherent_unit(TU_TYPE v) : internal::Coherent_unit_base(v){} + Coherent_unit(const internal::Coherent_unit_base& cb) : internal::Coherent_unit_base(cb) {} + Coherent_unit(TU_TYPE v) : internal::Coherent_unit_base(v){} }; namespace internal { @@ -255,7 +318,7 @@ namespace internal { // Quantities of base are assumed to be in the intended order. // Dimenisons are deduced from base. // -template +template constexpr auto create_coherent_unit(const Coherent_unit_base& cb) noexcept { return Coherent_unit, m, kg, A, K, mol, cd>(cb); } @@ -310,84 +373,86 @@ struct Unit : U::Base { // // Define binary operations +, -, *, and / for units. // -template typename T> +template typename T> +requires (are_ratios()) auto operator + (T l, T r) noexcept { return internal::create_coherent_unit(T(l.base_value + r.base_value)); } -template typename T> +template typename T> +requires (are_ratios()) auto operator - (T l, T r) noexcept { return internal::create_coherent_unit(T(l.base_value - r.base_value)); } namespace internal { -template typename L, - TU_TYPE rf, - TU_TYPE... r_args, - template typename R, - TU_TYPE... lr_args, - template typename L_op_R, +template typename L, + typename rf, + typename... r_args, + template typename R, + typename... lr_args, + template typename L_op_R, typename Op> -requires (sizeof...(l_args) == sizeof...(r_args)) +requires (sizeof...(l_args) == sizeof...(r_args) && are_ratios && are_ratios) constexpr auto binary_op_args(L, R, L_op_R, Op op) noexcept { if constexpr (sizeof...(l_args) == 0 && sizeof...(r_args) == 0) { - return create_coherent_unit(L_op_R()); + return create_coherent_unit(L_op_R()); } else { - return binary_op_args(L(), R< r_args...>(), L_op_R(), op); + return binary_op_args(L(), R< r_args...>(), L_op_R(), op); } } } // namespace internal -template typename L, - template typename R> -requires (sizeof...(L_args) == sizeof...(R_args)) +template typename L, + template typename R> +requires (sizeof...(L_args) == sizeof...(R_args) && are_ratios() && are_ratios()) auto operator * (L l, R r) noexcept -> decltype(internal::binary_op_args(L(), R(), L<>(), - std::plus())) { + Plus())) { return {l.base_value * r.base_value}; } -template typename L, - template typename R> -requires (sizeof...(L_args) == sizeof...(R_args)) +template typename L, + template typename R> +requires (sizeof...(L_args) == sizeof...(R_args) && are_ratios() && are_ratios()) auto operator / (L l, R r) noexcept -> decltype(internal::binary_op_args(L(), R(), L<>(), - std::minus())) { + Minus())) { return {l.base_value / r.base_value}; } namespace internal { // -// Apply a binary operation Op recusively to every template argument of U and a number n. -// Given U and the number n, the returned type of the operation is U +// Apply a binary operation Op recusively to every template argument of U and a ratio r. +// Given U and the ratio r, the returned type of the operation is U // -template typename U, - TU_TYPE... U_op_args, - template typename U_op, - TU_TYPE n, - template typename Num, +template typename U, + typename... U_op_args, + template typename U_op, + typename r, + template typename Rat, typename Op> -constexpr auto binary_op_args_num(U, [[maybe_unused]] Num N, U_op, Op op) noexcept { +constexpr auto binary_op_args_num(U, [[maybe_unused]] Rat R, U_op, Op op) noexcept { if constexpr (sizeof...(U_args) == 0) { - return internal::create_coherent_unit(U_op()); + return internal::create_coherent_unit(U_op()); } else { - return binary_op_args_num(U(), N, U_op(), op); + return binary_op_args_num(U(), R, U_op(), op); } } @@ -396,16 +461,16 @@ constexpr auto binary_op_args_num(U, [[maybe_unused]] Num // Use the `binary_op_args_num` template functions to perform pow(U). // Binary operation is std::multiplies. // -template typename U> +template typename U> requires std::derived_from, Unit_fundament> auto pow(U u) noexcept -> decltype(binary_op_args_num(U(), powexp(), U<>(), - std::multiplies())) { - return {std::pow(u.base_value, exp)}; + Multiply())) { + return {std::pow(u.base_value, fraction(exp()))}; } } //namespace internal @@ -413,7 +478,7 @@ auto pow(U u) noexcept -> decltype(binary_op_args_num(U(Unit) returning the underlying "coherent" unit U::Base // -template requires std::derived_from @@ -428,17 +493,17 @@ template requires std::derived_from auto sqrt(Unit u) noexcept { - return pow<(TU_TYPE)0.5>(u); + return pow>(u); } // // sqrt for struct Coherent_unit<> or similar. // -template typename U> +template typename U> requires std::derived_from, internal::Unit_fundament> auto sqrt(U u) noexcept { - return pow<(TU_TYPE)0.5>(u); + return pow>(u); } // @@ -471,47 +536,47 @@ auto unop(const U& u){ // // Explicit definitions of coherent units. // -struct second : Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct metre : Coherent_unit, m<(TU_TYPE)1.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct kilogram: Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)1.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct ampere: Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)1.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct kelvin: Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)1.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct mole: Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)1.0>, cd<(TU_TYPE)0.0>>{}; -struct candela: Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)1.0>>{}; - -// -// Dervived units with special names -// -struct scalar : Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct hertz : Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct becquerel: Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct ohm: Coherent_unit, m<(TU_TYPE)2.0>, kg<(TU_TYPE)1.0>, A<(TU_TYPE)-2.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct siemens: Coherent_unit, m<(TU_TYPE)-2.0>, kg<(TU_TYPE)-1.0>, A<(TU_TYPE)2.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct farad: Coherent_unit, m<(TU_TYPE)-2.0>, kg<(TU_TYPE)-1.0>, A<(TU_TYPE)2.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct lumen: Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)1.0>>{}; -struct weber: Coherent_unit, m<(TU_TYPE)2.0>, kg<(TU_TYPE)1.0>, A<(TU_TYPE)-1.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct gray: Coherent_unit, m<(TU_TYPE)2.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct sievert: Coherent_unit, m<(TU_TYPE)2.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct watt: Coherent_unit, m<(TU_TYPE)2.0>, kg<(TU_TYPE)1.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct newton: Coherent_unit, m<(TU_TYPE)1.0>, kg<(TU_TYPE)1.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct lux: Coherent_unit, m<(TU_TYPE)-2.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)1.0>>{}; -struct radian: Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct joule: Coherent_unit, m<(TU_TYPE)2.0>, kg<(TU_TYPE)1.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct steradian: Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct katal: Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)1.0>, cd<(TU_TYPE)0.0>>{}; -struct pascal: Coherent_unit, m<(TU_TYPE)-1.0>, kg<(TU_TYPE)1.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct coulomb: Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)1.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct henry: Coherent_unit, m<(TU_TYPE)2.0>, kg<(TU_TYPE)1.0>, A<(TU_TYPE)-2.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct tesla: Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)1.0>, A<(TU_TYPE)-1.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct volt : Coherent_unit, m<(TU_TYPE)2.0>, kg<(TU_TYPE)1.0>, A<(TU_TYPE)-1.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; +struct second : Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct metre : Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct kilogram: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct ampere: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct kelvin: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct mole: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct candela: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; + +// +// Derived units with special names +// +struct scalar : Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct hertz : Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct becquerel: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct ohm: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct siemens: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct farad: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct lumen: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct weber: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct gray: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct sievert: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct watt: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct newton: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct lux: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct radian: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct joule: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct steradian: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct katal: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct pascal: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct coulomb: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct henry: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct tesla: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct volt : Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; // // Derived coherent units // -struct metre_per_second : Coherent_unit, m<(TU_TYPE)1.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct second_squared : Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct metre_cubed: Coherent_unit, m<(TU_TYPE)3.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; -struct metre_squared: Coherent_unit, m<(TU_TYPE)2.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; +struct metre_per_second : Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct second_squared : Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct metre_cubed: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +struct metre_squared: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; // // Define non coherent units From cf5f30652eefab335d8c2058b79455113eb8a989 Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Mon, 11 Mar 2024 20:51:04 +0100 Subject: [PATCH 09/18] Make tests work with new implementation based on std::ratio --- test/test.cpp | 170 +++++++++------- typesafe_units/include/tu/typesafe_units.h | 218 ++++++++++----------- 2 files changed, 204 insertions(+), 184 deletions(-) diff --git a/test/test.cpp b/test/test.cpp index a17d6d4..e9005e2 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -169,18 +169,26 @@ int main() { } ); + Test<"is_ratio">( + [](T &t) { + t.assert_true(is_ratio>::value, __LINE__); + t.assert_false(is_ratio::value, __LINE__); + t.assert_true(is_ratio_v>, __LINE__); + t.assert_false(is_ratio_v, __LINE__); + } + ); + Test<"Coherent_unit_base">( [](T &t) { TU_TYPE val = 3.5; - - auto c1 = Coherent_unit_base<(TU_TYPE)1.0, (TU_TYPE)2.0>(val); + auto c1 = Coherent_unit_base, std::ratio<2>>(val); t.template assert>(val, c1.base_value, __LINE__); - auto c2 = Coherent_unit_base<(TU_TYPE)1.0, (TU_TYPE)2.0>(c1); + auto c2 = Coherent_unit_base, std::ratio<2>>(c1); t.template assert>(val, c2.base_value , __LINE__); Unit f(val); - Coherent_unit_base<(TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)1.0, (TU_TYPE)0.0, (TU_TYPE)0.0> c3 = Coherent_unit_base(f); + Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>, std::ratio<0>, std::ratio<0>> c3 = Coherent_unit_base(f); t.template assert>((val * 1.0e-3f - (TU_TYPE)32.0)/1.8f + (TU_TYPE)273.15, c3.base_value , __LINE__); } ); @@ -212,9 +220,9 @@ int main() { Test<"create_coherent_unit">( [](T &t){ - Coherent_unit_base<(TU_TYPE)1.0, (TU_TYPE)2.0, (TU_TYPE)3.0, (TU_TYPE)4.0, (TU_TYPE)5.0, (TU_TYPE)6.0, (TU_TYPE)7.0> cub; + Coherent_unit_base, std::ratio<2>, std::ratio<3>, std::ratio<4>, std::ratio<5>, std::ratio<6>, std::ratio<7>> cub; auto cu = internal::create_coherent_unit(cub); - t.assert_true(std::is_same, m<(TU_TYPE)2.0>, kg<(TU_TYPE)3.0>, A<(TU_TYPE)4.0>, K<(TU_TYPE)5.0>, mol<(TU_TYPE)6.0>, cd<(TU_TYPE)7.0>>>::value, __LINE__); + t.assert_true(std::is_same>, m>, kg>, A>, K>, mol>, cd>>>::value, __LINE__); } ); @@ -224,6 +232,9 @@ int main() { Unit s(value); t.template assert>(s.value, value, __LINE__); + //SUnit ss(value); + //t.template assert>(ss.value, value, __LINE__); + Unit s2 = value; t.template assert>(s2.value, value, __LINE__); @@ -246,11 +257,11 @@ int main() { Test<"is_scalar">( [](T &t){ TU_TYPE val = 0.0; - auto not_scalar = Coherent_unit_base<(TU_TYPE)1.0, (TU_TYPE)2.0>(val); - auto not_scalar2 = Coherent_unit_base<(TU_TYPE)0.0, (TU_TYPE)2.0>(val); - auto not_scalar3 = Coherent_unit_base<(TU_TYPE)1.0>(val); - auto scalar = Coherent_unit_base<(TU_TYPE)0.0, (TU_TYPE)0.0>(val); - auto scalar2 = Coherent_unit_base<(TU_TYPE)0.0>(val); + auto not_scalar = Coherent_unit_base, std::ratio<2>>(val); + auto not_scalar2 = Coherent_unit_base, std::ratio<2>>(val); + auto not_scalar3 = Coherent_unit_base>(val); + auto scalar = Coherent_unit_base, std::ratio<0>>(val); + auto scalar2 = Coherent_unit_base>(val); t.assert_false(not_scalar.is_scalar(), __LINE__); t.assert_false(not_scalar2.is_scalar(), __LINE__); @@ -289,8 +300,8 @@ int main() { auto value2 = 20000.0f; //Unit s1(value1); //Unit s2(value2); - Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>> s1(value1); - Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>> s2(value2); + Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> s1(value1); + Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> s2(value2); t.assert_true(s1 < s2, __LINE__); t.assert_false(s1 >= s2, __LINE__); @@ -315,7 +326,7 @@ int main() { Unit s1(value1); Unit s2(value2); - Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>> s12 = s1 + s2; + Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> s12 = s1 + s2; t.template assert>((TU_TYPE)30.0e-3f, s12.base_value, __LINE__); } ); @@ -327,7 +338,7 @@ int main() { Unit s1(value1); Unit s2(value2); - Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>> s12 = s1 - s2; + Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> s12 = s1 - s2; t.template assert>(-(TU_TYPE)10.0e-3f, s12.base_value, __LINE__); auto s3 = s12 + s12; @@ -340,7 +351,7 @@ int main() { TU_TYPE value2 = 20.0; Unit s1(value1); Unit a1(value2); - Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)1.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>> sa = s1 * a1; + Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> sa = s1 * a1; t.template assert>(sa.base_value, value1 * value2 * (TU_TYPE)1.0e-6f, __LINE__); } ); @@ -352,7 +363,7 @@ int main() { Unit s1(value1); Unit a1(value2); - Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)-1.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>> sa = s1 / a1; + Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> sa = s1 / a1; t.template assert>(sa.base_value, value1 / value2, __LINE__); } ); @@ -361,16 +372,16 @@ int main() { [](T &t){ TU_TYPE value1 = 10.0; TU_TYPE value2 = 20.0; - Coherent_unit_base<(TU_TYPE)1.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0> s1(value1); - Coherent_unit_base<(TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)1.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0> a1(value2); + Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>> s1(value1); + Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<1>, std::ratio<0>, std::ratio<0>, std::ratio<0>> a1(value2); - Coherent_unit_base<(TU_TYPE)1.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)1.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0> sa = s1 * a1; + Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<1>, std::ratio<0>, std::ratio<0>, std::ratio<0>> sa = s1 * a1; t.template assert>(sa.base_value, value1 * value2, __LINE__); auto s2 = internal::create_coherent_unit(s1); auto a2 = internal::create_coherent_unit(a1); - Coherent_unit_base<(TU_TYPE)1.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)1.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0> sa2 = s2 * a2; + Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<1>, std::ratio<0>, std::ratio<0>, std::ratio<0>> sa2 = s2 * a2; t.template assert>(sa2.base_value, value1 * value2, __LINE__); } ); @@ -379,66 +390,106 @@ int main() { [](T &t){ auto value1 = (TU_TYPE)10.0f; auto value2 = (TU_TYPE)20.0f; - Coherent_unit_base<(TU_TYPE)1.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0> s1(value1); - Coherent_unit_base<(TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)1.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0> a1(value2); + Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>> s1(value1); + Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<1>, std::ratio<0>, std::ratio<0>, std::ratio<0>> a1(value2); - Coherent_unit_base<(TU_TYPE)1.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)-1.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0> sa = s1 / a1; + Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<-1>, std::ratio<0>, std::ratio<0>, std::ratio<0>> sa = s1 / a1; t.template assert>(sa.base_value, value1 / value2, __LINE__); auto s2 = internal::create_coherent_unit(s1); auto a2 = internal::create_coherent_unit(a1); - Coherent_unit_base<(TU_TYPE)1.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)-1.0, (TU_TYPE)0.0, (TU_TYPE)0.0, (TU_TYPE)0.0> sa2 = s2 / a2; + Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<-1>, std::ratio<0>, std::ratio<0>, std::ratio<0>> sa2 = s2 / a2; t.template assert>(sa2.base_value, value1 / value2, __LINE__); } ); + Test<"Coherent_unit and Coherent_unit_base binary operator: +">( + [](T &t){ + auto value1 = (TU_TYPE)10.0f; + auto value2 = (TU_TYPE)20.0f; + Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>> s1(value1); + Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>> a1(value2); + + Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>> sa = s1 + a1; + t.template assert>(sa.base_value, value1 + value2, __LINE__); + + auto s2 = internal::create_coherent_unit(s1); + auto a2 = internal::create_coherent_unit(a1); + + Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> sa2 = s2 + a2; + t.template assert>(sa2.base_value, value1 + value2, __LINE__); + } + ); + + Test<"Coherent_unit and Coherent_unit_base binary operator: -">( + [](T &t){ + auto value1 = (TU_TYPE)10.0f; + auto value2 = (TU_TYPE)20.0f; + Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>> s1(value1); + Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>> a1(value2); + + Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>> sa = s1 - a1; + t.template assert>(sa.base_value, value1 - value2, __LINE__); + + auto s2 = internal::create_coherent_unit(s1); + auto a2 = internal::create_coherent_unit(a1); + + Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> sa2 = s2 - a2; + t.template assert>(sa2.base_value, value1 - value2, __LINE__); + } + ); + Test<"binary_op_args">( [](T ){ { - Coherent_unit_base<(TU_TYPE)1.0, (TU_TYPE)2.0, (TU_TYPE)3.0, (TU_TYPE)4.0, (TU_TYPE)5.0, (TU_TYPE)6.0, (TU_TYPE)7.0> l(2); - Coherent_unit_base<(TU_TYPE)6.0, (TU_TYPE)5.0, (TU_TYPE)4.0, (TU_TYPE)3.0, (TU_TYPE)2.0, (TU_TYPE)1.0, (TU_TYPE)0.0> r(3); + Coherent_unit_base, std::ratio<2>, std::ratio<3>, std::ratio<4>, std::ratio<5>, std::ratio<6>, std::ratio<7>> l(2); + Coherent_unit_base, std::ratio<5>, std::ratio<4>, std::ratio<3>, std::ratio<2>, std::ratio<1>, std::ratio<0>> r(3); Coherent_unit_base<> lr; - Coherent_unit_base<(TU_TYPE)7.0, (TU_TYPE)7.0, (TU_TYPE)7.0, (TU_TYPE)7.0, (TU_TYPE)7.0, (TU_TYPE)7.0, (TU_TYPE)7.0> l_plus_r = binary_op_args(l, r, lr, std::plus()); - Coherent_unit, m<(TU_TYPE)7.0>, kg<(TU_TYPE)7.0>, A<(TU_TYPE)7.0>, K<(TU_TYPE)7.0>, mol<(TU_TYPE)7.0>, cd<(TU_TYPE)7.0>> l_plus_r_c = binary_op_args(l, r, lr, std::plus()); + Coherent_unit_base, std::ratio<7>, std::ratio<7>, std::ratio<7>, std::ratio<7>, std::ratio<7>, std::ratio<7>> l_plus_r = binary_op_args(l, r, lr, Plus()); + Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> l_plus_r_c = binary_op_args(l, r, lr, Plus()); } } ); Test<"binary_op_args_num">( [](T ) { - Coherent_unit_base<(TU_TYPE)1.0, (TU_TYPE)2.0, (TU_TYPE)3.0, (TU_TYPE)4.0, (TU_TYPE)5.0, (TU_TYPE)6.0, (TU_TYPE)7.0> l; + Coherent_unit_base, std::ratio<2>, std::ratio<3>, std::ratio<4>, std::ratio<5>, std::ratio<6>, std::ratio<7>> l; Coherent_unit_base<> empty; - Coherent_unit_base<(TU_TYPE)2.0, (TU_TYPE)4.0, (TU_TYPE)6.0, (TU_TYPE)8.0, (TU_TYPE)10.0, (TU_TYPE)12.0, (TU_TYPE)14.0> r = binary_op_args_num(l, powexp<(TU_TYPE)2.0>(), empty, std::multiplies()); - Coherent_unit, m<(TU_TYPE)4.0>, kg<(TU_TYPE)6.0>, A<(TU_TYPE)8.0>, K<(TU_TYPE)10.0>, mol<(TU_TYPE)12.0>, cd<(TU_TYPE)14.0>> r2 = binary_op_args_num(l, powexp<(TU_TYPE)2.0>(), empty, std::multiplies()); + Coherent_unit_base, std::ratio<4>, std::ratio<6>, std::ratio<8>, std::ratio<10>, std::ratio<12>, std::ratio<14>> r = binary_op_args_num(l, std::ratio<2>(), empty, Multiply()); + Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> r2 = binary_op_args_num(l, std::ratio<2>(), empty, Multiply()); } ); Test<"pow Coherent_unit_base">( [](T &t) { TU_TYPE value = 3.0; - Coherent_unit_base<(TU_TYPE)1.0, (TU_TYPE)2.0, (TU_TYPE)3.0, (TU_TYPE)4.0, (TU_TYPE)5.0, (TU_TYPE)6.0, (TU_TYPE)7.0> r(value); - Coherent_unit_base<(TU_TYPE)2.0, (TU_TYPE)4.0, (TU_TYPE)6.0, (TU_TYPE)8.0, (TU_TYPE)10.0, (TU_TYPE)12.0, (TU_TYPE)14.0> l = pow<(TU_TYPE)2.0>(r); + Coherent_unit_base, std::ratio<2>, std::ratio<3>, std::ratio<4>, std::ratio<5>, std::ratio<6>, std::ratio<7>> r(value); + Coherent_unit_base, std::ratio<4>, std::ratio<6>, std::ratio<8>, std::ratio<10>, std::ratio<12>, std::ratio<14>> l = pow>(r); t.template assert>((TU_TYPE)pow(value, (TU_TYPE)2.0f), l.base_value, __LINE__); - Coherent_unit, m<(TU_TYPE)4.0>, kg<(TU_TYPE)6.0>, A<(TU_TYPE)8.0>, K<(TU_TYPE)10.0>, mol<(TU_TYPE)12.0>, cd<(TU_TYPE)14.0>> a = pow<(TU_TYPE)2.0>(r); + Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> a = pow>(r); } ); - Test<"pow Unit">( + Test<"pow Unit">( [](T &t) { TU_TYPE value1 = (TU_TYPE)20.0f; + Unit h1(value1); Unit s1(value1); - Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>> l = pow<(TU_TYPE)2.0>(s1); - t.template assert>(l.base_value, (TU_TYPE)pow(value1, (TU_TYPE)2.0) * (TU_TYPE)1.0e-6f, __LINE__); + Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> l1 = pow>(s1); + t.template assert>(l1.base_value, (TU_TYPE)pow(value1, (TU_TYPE)2.0) * (TU_TYPE)1.0e-6f, __LINE__); + + Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> l2 = pow>(h1); + t.template assert>(l2.base_value, (TU_TYPE)pow(value1, (TU_TYPE)2.0) * (TU_TYPE)1.0e-6f, __LINE__); } ); Test<"sqrt Coherent_unit_base">( [](T &t) { TU_TYPE value = 4.0; - Coherent_unit_base<(TU_TYPE)2.0, (TU_TYPE)4.0, (TU_TYPE)6.0, (TU_TYPE)8.0, (TU_TYPE)10.0, (TU_TYPE)12.0, (TU_TYPE)14.0> r(value); - Coherent_unit_base<(TU_TYPE)1.0, (TU_TYPE)2.0, (TU_TYPE)3.0, (TU_TYPE)4.0, (TU_TYPE)5.0, (TU_TYPE)6.0, (TU_TYPE)7.0> l = sqrt(r); + Coherent_unit_base, std::ratio<4>, std::ratio<6>, std::ratio<8>, std::ratio<10>, std::ratio<12>, std::ratio<14>> r(value); + Coherent_unit_base, std::ratio<2>, std::ratio<3>, std::ratio<4>, std::ratio<5>, std::ratio<6>, std::ratio<7>> l = sqrt(r); t.template assert>(std::sqrt(value), l.base_value, __LINE__); } ); @@ -447,7 +498,7 @@ int main() { [](T &t) { TU_TYPE value1 = 20.0; Unit s1(value1); - Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>> l = sqrt(s1); + Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> l = sqrt(s1); t.template assert>(l.base_value, std::sqrt(value1) * std::pow((TU_TYPE)1e-3, (TU_TYPE)0.5), __LINE__); } ); @@ -455,12 +506,12 @@ int main() { Test<"unop Coherent_unit_base">( [](T &t) { TU_TYPE val = 0.0; - auto scalar2 = Coherent_unit_base<(TU_TYPE)0.0>((TU_TYPE)tu::PI/2.0); - auto scalar = Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>(); + auto scalar2 = Coherent_unit_base>((TU_TYPE)tu::PI/2.0); + auto scalar = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>(); t.template assert>(unop(scalar).base_value, (TU_TYPE)0.0, __LINE__); t.template assert>(unop(scalar2).base_value, (TU_TYPE)1.0, __LINE__); - Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>> scalar3 = unop(scalar); + Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> scalar3 = unop(scalar); // lambda is globally defined to compile with gcc auto new_scalar_2 = unop(scalar); @@ -478,7 +529,7 @@ int main() { Unit new_scalar_unit = unop(scalar_unit); - Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>> scalar3 = unop(scalar_unit); + Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> scalar3 = unop(scalar_unit); t.template assert>(unop(scalar_unit).base_value, (TU_TYPE)1.0, __LINE__); t.template assert>(unop(scalar_unit2).base_value, (TU_TYPE)0.0, __LINE__); @@ -504,30 +555,15 @@ int main() { } ); - Test<"powexp">( - [](T) { - static_assert(powexp<(TU_TYPE)-2.0>::exp == -2); - static_assert(powexp<(TU_TYPE)-1.0>::exp == -1); - static_assert(powexp<(TU_TYPE)0.0>::exp == 0); - static_assert(powexp<(TU_TYPE)1.0>::exp == 1); - static_assert(powexp<(TU_TYPE)2.0>::exp == 2); - static_assert(powexp<(TU_TYPE)-2.0>::exp != 1); - static_assert(powexp<(TU_TYPE)-1.0>::exp != 1); - static_assert(powexp<(TU_TYPE)0.0>::exp != 1); - static_assert(powexp<(TU_TYPE)1.0>::exp != 0); - static_assert(powexp<(TU_TYPE)2.0>::exp != 1); - } - ); - Test<"Coherent units definition">( [](T) { - static_assert(std::is_base_of, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>, second>::value); - static_assert(std::is_base_of, m<(TU_TYPE)1.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>, metre>::value); - static_assert(std::is_base_of, m<(TU_TYPE)0.0>, kg<(TU_TYPE)1.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>, kilogram>::value); - static_assert(std::is_base_of, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)1.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>, ampere>::value); - static_assert(std::is_base_of, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)1.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>, kelvin>::value); - static_assert(std::is_base_of, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)1.0>, cd<(TU_TYPE)0.0>>, mole>::value); - static_assert(std::is_base_of, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)1.0>>, candela >::value); + static_assert(std::is_base_of>, m>, kg>, A>, K>, mol>, cd>>, second>::value); + static_assert(std::is_base_of>, m>, kg>, A>, K>, mol>, cd>>, metre>::value); + static_assert(std::is_base_of>, m>, kg>, A>, K>, mol>, cd>>, kilogram>::value); + static_assert(std::is_base_of>, m>, kg>, A>, K>, mol>, cd>>, ampere>::value); + static_assert(std::is_base_of>, m>, kg>, A>, K>, mol>, cd>>, kelvin>::value); + static_assert(std::is_base_of>, m>, kg>, A>, K>, mol>, cd>>, mole>::value); + static_assert(std::is_base_of>, m>, kg>, A>, K>, mol>, cd>>, candela >::value); } ); diff --git a/typesafe_units/include/tu/typesafe_units.h b/typesafe_units/include/tu/typesafe_units.h index 8084b98..e512557 100644 --- a/typesafe_units/include/tu/typesafe_units.h +++ b/typesafe_units/include/tu/typesafe_units.h @@ -19,58 +19,49 @@ namespace tu { constexpr TU_TYPE PI = std::numbers::pi_v; -template -using r = std::ratio; - //namespace internal { -template -using is_ratio = std::is_same, typename R::type>; - -template -inline constexpr bool is_ratio_v = is_ratio::value; - -template -constexpr bool are_ratios() { - if constexpr (sizeof...(R) == 0) - return is_ratio_v; - else - return is_ratio_v && are_ratios; -} -template -requires (are_ratios()) -constexpr auto ratio_op(A a, B b) { - return op()(a,b); -} +template +struct is_ratio : std::false_type {}; + +template +struct is_ratio> : std::true_type {}; + +template +inline constexpr bool is_ratio_v = is_ratio::value; + +template +concept Ratio = is_ratio_v; + //} -template -requires (!std::is_integral_v && is_ratio_v) -constexpr F fraction(R) { - return static_cast(R::num) / static_cast(R::den); +template +constexpr TU_TYPE fraction(R) { + return static_cast(R::num) / static_cast(R::den); } struct Plus { - template + template constexpr auto operator() (A, B) -> std::ratio_add::type{ return {}; } }; struct Minus { - template + template constexpr auto operator() (A, B) -> std::ratio_subtract::type{ return {}; } }; struct Multiply { - template + template constexpr auto operator() (A, B) -> std::ratio_multiply::type{ return {}; } }; +//} // namespace internal // // Prefixes used to define units. @@ -124,18 +115,7 @@ constexpr TU_TYPE pow10() noexcept { } } -// -// Convenience struct to wrap a TU_TYPE representing an exponent in a template argument. -// This makes it possibel to deduce the exponent argument from a function parameter. -// -template -struct powexp { - constexpr powexp() noexcept {}; - //static constexpr TU_TYPE exp = e; -}; - -template -requires (are_ratios()) +template constexpr bool are_args_zero() noexcept { if constexpr (U_first::num != 0) { return false; @@ -154,17 +134,17 @@ constexpr bool are_args_zero() noexcept { // template arguments to derive from it. Empty base optimization ensures that // this construction does not come with any memory overhead. // -struct Unit_fundament{ +struct Unit_fundament { auto operator <=> (const Unit_fundament& other) const noexcept = default; }; // // Base struct for coherent units. -// The variadic TU_TYPE arguments simplifies binary operations of units. +// The variadic std::ratio arguments simplifies binary operations of units. // Direct use of this struct should be avoided in application code since it is -// not explicit what quantity each template argument represent. +// not explicit what quantity each template argument represents. // -// Template arguments represents power (p) of SI quantities in the following +// Template arguments represents rational power (p) of SI quantities in the following // order: // //

) +template struct Base_unit { using power = p; }; } // namespace internal // -// Struct representation of base unit s (second) with power p +// Struct representation of base unit s (second) with rational power p // -template -requires (is_ratio_v

) +template struct s : internal::Base_unit

{}; // -// Struct representation of base unit m (metre) with power p +// Struct representation of base unit m (metre) with rational power p // -template -requires (is_ratio_v

) +template struct m : internal::Base_unit

{}; // -// Struct representation of base unit kg (kilogram) with power p +// Struct representation of base unit kg (kilogram) with rational power p // -template -requires (is_ratio_v

) +template struct kg : internal::Base_unit

{}; // -// Struct representation of base unit A (ampere) with power p +// Struct representation of base unit A (ampere) with rational power p // -template -requires (is_ratio_v

) +template struct A : internal::Base_unit

{}; // -// Struct representation of base unit K (kelvin) with power p +// Struct representation of base unit K (kelvin) with rational power p // -template -requires (is_ratio_v

) +template struct K : internal::Base_unit

{}; // -// Struct representation of base unit mol (mole) with power p +// Struct representation of base unit mol (mole) with rational power p // -template -requires (is_ratio_v

) +template struct mol : internal::Base_unit

{}; // -// Struct representation of base unit cd (candela) with power p +// Struct representation of base unit cd (candela) with rational power p // -template -requires (is_ratio_v

) +template struct cd : internal::Base_unit

{}; // @@ -318,7 +305,7 @@ namespace internal { // Quantities of base are assumed to be in the intended order. // Dimenisons are deduced from base. // -template +template constexpr auto create_coherent_unit(const Coherent_unit_base& cb) noexcept { return Coherent_unit, m, kg, A, K, mol, cd>(cb); } @@ -329,7 +316,7 @@ constexpr auto create_coherent_unit(const Coherent_unit_base -requires (std::derived_from && multiplier != (TU_TYPE)0.0) +requires (std::derived_from && (multiplier != (TU_TYPE)1.0 || adder != (TU_TYPE)0.0)) struct Non_coherent_unit : Parent_unit { static constexpr TU_TYPE base_multiplier = Parent_unit::base_multiplier * multiplier; static constexpr TU_TYPE base_adder = Parent_unit::base_adder + adder * multiplier; @@ -373,31 +360,29 @@ struct Unit : U::Base { // // Define binary operations +, -, *, and / for units. // -template typename T> -requires (are_ratios()) +template typename T> auto operator + (T l, T r) noexcept { return internal::create_coherent_unit(T(l.base_value + r.base_value)); } -template typename T> -requires (are_ratios()) +template typename T> auto operator - (T l, T r) noexcept { return internal::create_coherent_unit(T(l.base_value - r.base_value)); } namespace internal { -template typename L, - typename rf, - typename... r_args, + Ratio rf, + Ratio... r_args, template typename R, - typename... lr_args, + Ratio... lr_args, template typename L_op_R, typename Op> -requires (sizeof...(l_args) == sizeof...(r_args) && are_ratios && are_ratios) +requires (sizeof...(l_args) == sizeof...(r_args)) constexpr auto binary_op_args(L, R, L_op_R, Op op) noexcept { if constexpr (sizeof...(l_args) == 0 && sizeof...(r_args) == 0) { return create_coherent_unit(L_op_R()); @@ -407,13 +392,13 @@ constexpr auto binary_op_args(L, R, L_op_R typename L, - template typename R> -requires (sizeof...(L_args) == sizeof...(R_args) && are_ratios() && are_ratios()) +template typename L, + template typename R> +requires (sizeof...(L_args) == sizeof...(R_args)) auto operator * (L l, R r) noexcept -> decltype(internal::binary_op_args(L(), R(), L<>(), @@ -421,13 +406,13 @@ auto operator * (L l, R r) noexcept -> d return {l.base_value * r.base_value}; } -template typename L, - template typename R> -requires (sizeof...(L_args) == sizeof...(R_args) && are_ratios() && are_ratios()) +template typename L, + template typename R> +requires (sizeof...(L_args) == sizeof...(R_args)) auto operator / (L l, R r) noexcept -> decltype(internal::binary_op_args(L(), R(), L<>(), @@ -445,40 +430,39 @@ template typename U, typename... U_op_args, template typename U_op, - typename r, - template typename Rat, + Ratio R, typename Op> -constexpr auto binary_op_args_num(U, [[maybe_unused]] Rat R, U_op, Op op) noexcept { +constexpr auto binary_op_args_num(U, [[maybe_unused]] R r, U_op, Op op) noexcept { if constexpr (sizeof...(U_args) == 0) { - return internal::create_coherent_unit(U_op()); + return internal::create_coherent_unit(U_op()); } else { - return binary_op_args_num(U(), R, U_op(), op); + return binary_op_args_num(U(), r, U_op(), op); } } // -// Use the `binary_op_args_num` template functions to perform pow(U). -// Binary operation is std::multiplies. +// Use the `binary_op_args_num` template functions to perform pow>(U...>). +// Binary operation is Multiply. // -template typename U> requires std::derived_from, Unit_fundament> auto pow(U u) noexcept -> decltype(binary_op_args_num(U(), - powexp(), - U<>(), - Multiply())) { - return {std::pow(u.base_value, fraction(exp()))}; + exp(), + U<>(), + Multiply())) { + return {std::pow(u.base_value, fraction(exp()))}; } } //namespace internal // -// Template function for pow(Unit) returning the underlying "coherent" unit U::Base +// Template function for pow(Unit) returning the underlying "coherent" unit U::Base // -template requires std::derived_from @@ -511,9 +495,9 @@ auto sqrt(U u) noexcept { // unop is a template function that applies any unary function that takes a TU_TYPE // and returns a TU_TYPE to the underlying value of the unit if it is a scalar unit e.g // radian or steradian. The function returns a scalar Coherent_unit initialized with -// the value of the performed operation. This makes it possible to operate with any unary -// function (subjected to the restrictions above) from the standard library on a Unit or -// Coherent_unit. unop can take both unary functions and lambda expressions as +// the resulting value of the performed operation. This makes it possible to operate with +// any unary function (subjected to the restrictions above) from the standard library on a +// Unit or Coherent_unit. unop can take both unary functions and lambda expressions as // template parameter. // // Example: From 129c3d51e1540c668f48d42b6d6218547c5612b6 Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Wed, 13 Mar 2024 09:05:42 +0100 Subject: [PATCH 10/18] Simplify templates to make compile in Linux --- test/test.cpp | 2 - typesafe_units/include/tu/typesafe_units.h | 114 +++++++-------------- 2 files changed, 39 insertions(+), 77 deletions(-) diff --git a/test/test.cpp b/test/test.cpp index e9005e2..96a8af8 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -340,8 +340,6 @@ int main() { Coherent_unit>, m>, kg>, A>, K>, mol>, cd>> s12 = s1 - s2; t.template assert>(-(TU_TYPE)10.0e-3f, s12.base_value, __LINE__); - - auto s3 = s12 + s12; } ); diff --git a/typesafe_units/include/tu/typesafe_units.h b/typesafe_units/include/tu/typesafe_units.h index e512557..15b7007 100644 --- a/typesafe_units/include/tu/typesafe_units.h +++ b/typesafe_units/include/tu/typesafe_units.h @@ -360,34 +360,29 @@ struct Unit : U::Base { // // Define binary operations +, -, *, and / for units. // -template typename T> -auto operator + (T l, T r) noexcept { - return internal::create_coherent_unit(T(l.base_value + r.base_value)); +template +auto operator + (internal::Coherent_unit_base l, internal::Coherent_unit_base r) noexcept { + return internal::create_coherent_unit(internal::Coherent_unit_base(l.base_value + r.base_value)); } -template typename T> -auto operator - (T l, T r) noexcept { - return internal::create_coherent_unit(T(l.base_value - r.base_value)); +template +auto operator - (internal::Coherent_unit_base l, internal::Coherent_unit_base r) noexcept { + return internal::create_coherent_unit(internal::Coherent_unit_base(l.base_value - r.base_value)); } namespace internal { template typename L, Ratio rf, Ratio... r_args, - template typename R, Ratio... lr_args, - template typename L_op_R, typename Op> requires (sizeof...(l_args) == sizeof...(r_args)) -constexpr auto binary_op_args(L, R, L_op_R, Op op) noexcept { +constexpr auto binary_op_args(internal::Coherent_unit_base, internal::Coherent_unit_base, internal::Coherent_unit_base, Op op) noexcept { if constexpr (sizeof...(l_args) == 0 && sizeof...(r_args) == 0) { - return create_coherent_unit(L_op_R()); + return create_coherent_unit(internal::Coherent_unit_base()); } else { - return binary_op_args(L(), R< r_args...>(), L_op_R(), op); + return binary_op_args(internal::Coherent_unit_base(), internal::Coherent_unit_base(), internal::Coherent_unit_base(), op); } } } // namespace internal @@ -395,28 +390,26 @@ constexpr auto binary_op_args(L, R, L_op_R typename L, - template typename R> + Ratio... R_args> requires (sizeof...(L_args) == sizeof...(R_args)) -auto operator * (L l, R r) noexcept -> decltype(internal::binary_op_args(L(), - R(), - L<>(), - Plus())) { +auto operator * (internal::Coherent_unit_base l, + internal::Coherent_unit_base r) noexcept -> decltype(internal::binary_op_args(internal::Coherent_unit_base(), + internal::Coherent_unit_base(), + internal::Coherent_unit_base<>(), + Plus())) { return {l.base_value * r.base_value}; } template typename L, - template typename R> + Ratio... R_args> requires (sizeof...(L_args) == sizeof...(R_args)) -auto operator / (L l, R r) noexcept -> decltype(internal::binary_op_args(L(), - R(), - L<>(), - Minus())) { +auto operator / (internal::Coherent_unit_base l, + internal::Coherent_unit_base r) noexcept -> decltype(internal::binary_op_args(internal::Coherent_unit_base(), + internal::Coherent_unit_base(), + internal::Coherent_unit_base<>(), + Minus())) { return {l.base_value / r.base_value}; } @@ -425,68 +418,39 @@ namespace internal { // Apply a binary operation Op recusively to every template argument of U and a ratio r. // Given U and the ratio r, the returned type of the operation is U // -template typename U, - typename... U_op_args, - template typename U_op, +template -constexpr auto binary_op_args_num(U, [[maybe_unused]] R r, U_op, Op op) noexcept { +constexpr auto binary_op_args_num(internal::Coherent_unit_base, [[maybe_unused]] R r, internal::Coherent_unit_base, Op op) noexcept { if constexpr (sizeof...(U_args) == 0) { - return internal::create_coherent_unit(U_op()); + return internal::create_coherent_unit(internal::Coherent_unit_base()); } else { - return binary_op_args_num(U(), r, U_op(), op); + return binary_op_args_num(internal::Coherent_unit_base(), r, internal::Coherent_unit_base(), op); } } - +} //namespace internal // // Use the `binary_op_args_num` template functions to perform pow>(U...>). // Binary operation is Multiply. // template typename U> -requires std::derived_from, Unit_fundament> -auto pow(U u) noexcept -> decltype(binary_op_args_num(U(), - exp(), - U<>(), - Multiply())) { + Ratio U_first, + Ratio... U_args> +auto pow(internal::Coherent_unit_base u) noexcept -> decltype(binary_op_args_num(internal::Coherent_unit_base(), + exp(), + internal::Coherent_unit_base<>(), + Multiply())) { return {std::pow(u.base_value, fraction(exp()))}; } -} //namespace internal - - -// -// Template function for pow(Unit) returning the underlying "coherent" unit U::Base -// -template -requires std::derived_from -auto pow(Unit u) noexcept { - return pow(static_cast::Base&>(u)); -} - -// -// sqrt for struct Unit. -// -template -requires std::derived_from -auto sqrt(Unit u) noexcept { - return pow>(u); -} // -// sqrt for struct Coherent_unit<> or similar. +// sqrt for struct Unit and Coherent_unit<> or similar. // -template typename U> -requires std::derived_from, internal::Unit_fundament> -auto sqrt(U u) noexcept { +template +auto sqrt(internal::Coherent_unit_base u) noexcept { return pow>(u); } @@ -506,13 +470,13 @@ auto sqrt(U u) noexcept { using Unary_op_func = TU_TYPE(*)(TU_TYPE); template -requires (std::derived_from && Unit::is_scalar()) +requires (Unit::is_scalar()) auto unop(const Unit& u){ return internal::create_coherent_unit(typename U::Base(op(u.base_value))); } template -requires (std::derived_from && U::is_scalar()) +requires (U::is_scalar()) auto unop(const U& u){ return U(op(u.base_value)); } From 64c28c603e866c654ba81c55ed6c2c0360611455 Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Wed, 13 Mar 2024 13:12:51 +0100 Subject: [PATCH 11/18] Move some functionality into "internal" namespace --- typesafe_units/include/tu/typesafe_units.h | 48 +++++++++++----------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/typesafe_units/include/tu/typesafe_units.h b/typesafe_units/include/tu/typesafe_units.h index 15b7007..b05f445 100644 --- a/typesafe_units/include/tu/typesafe_units.h +++ b/typesafe_units/include/tu/typesafe_units.h @@ -19,7 +19,7 @@ namespace tu { constexpr TU_TYPE PI = std::numbers::pi_v; -//namespace internal { +namespace internal { template struct is_ratio : std::false_type {}; @@ -61,7 +61,7 @@ struct Multiply { } }; -//} // namespace internal +} // namespace internal // // Prefixes used to define units. @@ -216,43 +216,43 @@ struct Base_unit { // // Struct representation of base unit s (second) with rational power p // -template +template struct s : internal::Base_unit

{}; // // Struct representation of base unit m (metre) with rational power p // -template +template struct m : internal::Base_unit

{}; // // Struct representation of base unit kg (kilogram) with rational power p // -template +template struct kg : internal::Base_unit

{}; // // Struct representation of base unit A (ampere) with rational power p // -template +template struct A : internal::Base_unit

{}; // // Struct representation of base unit K (kelvin) with rational power p // -template +template struct K : internal::Base_unit

{}; // // Struct representation of base unit mol (mole) with rational power p // -template +template struct mol : internal::Base_unit

{}; // // Struct representation of base unit cd (candela) with rational power p // -template +template struct cd : internal::Base_unit

{}; // @@ -387,29 +387,29 @@ constexpr auto binary_op_args(internal::Coherent_unit_base, inter } } // namespace internal -template +template requires (sizeof...(L_args) == sizeof...(R_args)) auto operator * (internal::Coherent_unit_base l, internal::Coherent_unit_base r) noexcept -> decltype(internal::binary_op_args(internal::Coherent_unit_base(), internal::Coherent_unit_base(), internal::Coherent_unit_base<>(), - Plus())) { + internal::Plus())) { return {l.base_value * r.base_value}; } -template +template requires (sizeof...(L_args) == sizeof...(R_args)) auto operator / (internal::Coherent_unit_base l, internal::Coherent_unit_base r) noexcept -> decltype(internal::binary_op_args(internal::Coherent_unit_base(), internal::Coherent_unit_base(), internal::Coherent_unit_base<>(), - Minus())) { + internal::Minus())) { return {l.base_value / r.base_value}; } @@ -436,14 +436,14 @@ constexpr auto binary_op_args_num(internal::Coherent_unit_base>(U...>). // Binary operation is Multiply. // -template +template auto pow(internal::Coherent_unit_base u) noexcept -> decltype(binary_op_args_num(internal::Coherent_unit_base(), exp(), internal::Coherent_unit_base<>(), - Multiply())) { - return {std::pow(u.base_value, fraction(exp()))}; + internal::Multiply())) { + return {std::pow(u.base_value, internal::fraction(exp()))}; } // From bafdaaf9086703c42c948417aea8ec2b74b10e4b Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Thu, 14 Mar 2024 14:11:07 +0100 Subject: [PATCH 12/18] Define units in terms of "using" and not through inheritance --- typesafe_units/include/tu/typesafe_units.h | 135 ++++++++------------- 1 file changed, 49 insertions(+), 86 deletions(-) diff --git a/typesafe_units/include/tu/typesafe_units.h b/typesafe_units/include/tu/typesafe_units.h index b05f445..5541780 100644 --- a/typesafe_units/include/tu/typesafe_units.h +++ b/typesafe_units/include/tu/typesafe_units.h @@ -64,7 +64,7 @@ struct Multiply { } // namespace internal // -// Prefixes used to define units. +// Prefixes used to define Units. // enum struct prefix { quecto = -30, @@ -484,47 +484,47 @@ auto unop(const U& u){ // // Explicit definitions of coherent units. // -struct second : Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct metre : Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct kilogram: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct ampere: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct kelvin: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct mole: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct candela: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +using second = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using metre = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using kilogram = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using ampere = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using kelvin = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using mole = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using candela = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; // // Derived units with special names // -struct scalar : Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct hertz : Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct becquerel: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct ohm: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct siemens: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct farad: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct lumen: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct weber: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct gray: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct sievert: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct watt: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct newton: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct lux: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct radian: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct joule: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct steradian: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct katal: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct pascal: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct coulomb: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct henry: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct tesla: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct volt : Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +using scalar = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using hertz = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using becquerel = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using ohm = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using siemens = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using farad = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using lumen = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using weber = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using gray = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using sievert = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using watt = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using newton = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using lux = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using radian = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using joule = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using steradian = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using katal = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using pascal = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using coulomb = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using henry = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using tesla = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using volt = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; // // Derived coherent units // -struct metre_per_second : Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct second_squared : Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct metre_cubed: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; -struct metre_squared: Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>{}; +using metre_per_second = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using second_squared = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using metre_cubed = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; +using metre_squared = Coherent_unit>, m>, kg>, A>, K>, mol>, cd>>; // // Define non coherent units @@ -534,91 +534,54 @@ struct metre_squared: Coherent_unit>, m>, kg { - using Non_coherent_unit<(TU_TYPE)60.0, (TU_TYPE)0.0, second>::Base; -}; - -struct hour : Non_coherent_unit<(TU_TYPE)60.0, (TU_TYPE)0.0, minute> { - using Non_coherent_unit<(TU_TYPE)60.0, (TU_TYPE)0.0, minute>::Base; -}; - -struct day : Non_coherent_unit<(TU_TYPE)24.0, (TU_TYPE)0.0, hour> { - using Non_coherent_unit<(TU_TYPE)24.0, (TU_TYPE)0.0, hour>::Base; -}; +using minute = Non_coherent_unit<(TU_TYPE)60.0, (TU_TYPE)0.0, second>; +using hour = Non_coherent_unit<(TU_TYPE)60.0, (TU_TYPE)0.0, minute>; +using day = Non_coherent_unit<(TU_TYPE)24.0, (TU_TYPE)0.0, hour>; // // Temperature // -struct degree_Celsius : Non_coherent_unit<(TU_TYPE)1.0, (TU_TYPE)273.15, kelvin> { - using Non_coherent_unit<(TU_TYPE)1.0, (TU_TYPE)273.15, kelvin>::Base; -}; +using degree_Celsius = Non_coherent_unit<(TU_TYPE)1.0, (TU_TYPE)273.15, kelvin>; // // Mass // -struct gram : Non_coherent_unit<(TU_TYPE)0.001, (TU_TYPE)0.0, kilogram> { - using Non_coherent_unit<(TU_TYPE)0.001, (TU_TYPE)0.0, kilogram>::Base; -}; - -struct tonne : Non_coherent_unit<(TU_TYPE)1000.0, (TU_TYPE)0.0, kilogram> { - using Non_coherent_unit<(TU_TYPE)1000.0, (TU_TYPE)0.0, kilogram>::Base; -}; - -struct dalton : Non_coherent_unit<(TU_TYPE)1.66053904020e-27, (TU_TYPE)0.0, kilogram> { - using Non_coherent_unit<(TU_TYPE)1.66053904020e-27, (TU_TYPE)0.0, kilogram>::Base; -}; - -struct unified_atomic_mass_unit : Non_coherent_unit<(TU_TYPE)1.66053904020e-27, (TU_TYPE)0.0, kilogram> { - using Non_coherent_unit<(TU_TYPE)1.66053904020e-27, (TU_TYPE)0.0, kilogram>::Base; -}; +using gram = Non_coherent_unit<(TU_TYPE)0.001, (TU_TYPE)0.0, kilogram>; +using tonne = Non_coherent_unit<(TU_TYPE)1000.0, (TU_TYPE)0.0, kilogram>; +using dalton = Non_coherent_unit<(TU_TYPE)1.66053904020e-27, (TU_TYPE)0.0, kilogram>; +using unified_atomic_mass_unit = Non_coherent_unit<(TU_TYPE)1.66053904020e-27, (TU_TYPE)0.0, kilogram>; // // Energy // -struct electronvolt : Non_coherent_unit<(TU_TYPE)1.602176634e-19, (TU_TYPE)0.0, joule> { - using Non_coherent_unit<(TU_TYPE)1.602176634e-19, (TU_TYPE)0.0, joule>::Base; -}; +using electronvolt = Non_coherent_unit<(TU_TYPE)1.602176634e-19, (TU_TYPE)0.0, joule>; // // Volume // -struct litre : Non_coherent_unit<(TU_TYPE)0.001, (TU_TYPE)0.0, metre_cubed> { - using Non_coherent_unit<(TU_TYPE)0.001, (TU_TYPE)0.0, metre_cubed>::Base; -}; +using litre = Non_coherent_unit<(TU_TYPE)0.001, (TU_TYPE)0.0, metre_cubed>; // // Plane- and phase angel // -struct degree : Non_coherent_unit<(TU_TYPE)(PI/180.0), (TU_TYPE)0.0, radian> { - using Non_coherent_unit<(TU_TYPE)(PI/180.0), (TU_TYPE)0.0, radian>::Base; -}; - -struct arc_minute : Non_coherent_unit<(TU_TYPE)(1/60.0), (TU_TYPE)0.0, degree> { - using Non_coherent_unit<(TU_TYPE)(1/60.0), (TU_TYPE)0.0, degree>::Base; -}; - -struct arc_second : Non_coherent_unit<(TU_TYPE)(1/60.0), (TU_TYPE)0.0, arc_minute> { - using Non_coherent_unit<(TU_TYPE)(1/60.0), (TU_TYPE)0.0, arc_minute>::Base; -}; +using degree = Non_coherent_unit<(TU_TYPE)(PI/180.0), (TU_TYPE)0.0, radian>; +using arc_minute = Non_coherent_unit<(TU_TYPE)(1/60.0), (TU_TYPE)0.0, degree>; +using arc_second = Non_coherent_unit<(TU_TYPE)(1/60.0), (TU_TYPE)0.0, arc_minute>; // // Area // -struct hectare : Non_coherent_unit<(TU_TYPE)(10000.0), (TU_TYPE)0.0, metre_squared> { - using Non_coherent_unit<(TU_TYPE)(10000.0), (TU_TYPE)0.0, metre_squared>::Base; -}; +using hectare = Non_coherent_unit<(TU_TYPE)(10000.0), (TU_TYPE)0.0, metre_squared>; // // Length // -struct astronomical_unit : Non_coherent_unit<(TU_TYPE)149597870700.0, (TU_TYPE)0.0, metre> { - using Non_coherent_unit<(TU_TYPE)149597870700.0, (TU_TYPE)0.0, metre>::Base; -}; +using astronomical_unit = Non_coherent_unit<(TU_TYPE)149597870700.0, (TU_TYPE)0.0, metre>; } // namespace tu \ No newline at end of file From f34589030758df4a4bd664520283b4913f72569c Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Thu, 14 Mar 2024 19:36:00 +0100 Subject: [PATCH 13/18] Update readme.md --- README.md | 108 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 276e08c..e7140c0 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,10 @@ Unit c = s * a; std::cout << c.value << std::endl; // prints 5e-08 ``` -If you need a non-SI unit you define it by declaring a simple struct. This is how you would define the unit -`degree_Fahrenheit`: +If you need a non-SI unit you define it by declaring a Non_coherent_unit. This is how you would define the unit `degree_Fahrenheit`: ```c++ - struct degree_Fahrenheit : Non_coherent_unit<1.0f / 1.8f, -32.0f, degree_Celsius> { - using Non_coherent_unit<1.0f / 1.8f, -32.0f, degree_Celsius>::Base; - }; +using degree_Fahrenheit = Non_coherent_unit<1.0f / 1.8f, -32.0f, degree_Celsius>; ``` You can use the new unit like any other unit already defined in TU: @@ -71,9 +68,7 @@ target_compile_definitions(my_target PRIVATE TU_TYPE=double) ## Requirements -TU requires a c++20 compliant compiler. Specifically TU utilizes float non-type template arguments. - -For the test suite that comes with TU to work, your system needs to have support for ANSI escape sequences since the output uses colours. This should work on fairly recent Windows 10 system, linux and macOS. It might be a problem on Windows 7 though. If you find that this is a showstopper for you please let us know. If enough people run TU on systems that does not have support for ANSI escape sequences, we will remove it. +TU requires a c++20 compliant compiler. For the test suite that comes with TU to work, your system needs to have support for ANSI escape sequences since the output uses colours. This should work on fairly recent Windows 10 system, linux and macOS. It might be a problem on Windows 7 though. If you find that this is a showstopper for you please let us know. If enough people run TU on systems that does not have support for ANSI escape sequences, we will remove it. ## Tested compilers @@ -153,7 +148,7 @@ TU uses official spelling of units. Therefore TU uses `litre` and `metre` and no ### Types The intrinsic data type used by TU is defined in the preprocessor macro `TU_TYPE`. -`TU_TYPE` can be `float` or `double`. All values and floating point template argumets will have the type defined by `TU_TYPE`. +`TU_TYPE` can be `float` or `double`. All values will have the type defined by `TU_TYPE`. ### Namespaces @@ -193,20 +188,22 @@ Base units are the smallest building blocks in the types system. These define po The definition of base units are done through inheritance of the `Base_unit` struct and looks as follows where the base unit is denoted `X`. ```c++ -template +template struct X : internal::Base_unit

{}; ``` The definition of `s` (second) to some power `p` then looks as ```c++ -template +template struct s : internal::Base_unit

{}; ``` +where Ratio is of type std:ratio + and a base unit `per_second` can be declared through ```c++ -s<(TU_TYPE)-1.0f>; +s; ``` #### Coherent_unit @@ -217,7 +214,13 @@ A specific coherent unit should be defined by inheriting from a `Coherent_unit` The specific coherent unit `newton` is defined as ```c++ -struct newton: Coherent_unit, m<(TU_TYPE)1.0>, kg<(TU_TYPE)1.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; +using newton = Coherent_unit + ,m + ,kg + ,A + ,K + ,mol + ,cd>; ``` i.e. it has the unit `kg m / s^2` @@ -227,7 +230,13 @@ Note that computations using seconds should use the `Coherent_unit` `second` and `second` is defined as ```c++ -struct second: Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>{}; +using second = Coherent_unit + ,m + ,kg + ,A + ,K + ,mol + ,cd>; ``` All base units are defined as `Coherent_unit`s in similar fashion. @@ -236,31 +245,23 @@ All base units are defined as `Coherent_unit`s in similar fashion. A `Non_coherent_unit` is a unit that is scaled or shifted relative to a base unit. The value of the `Non_coherent_unit` is related to the value of the base unit through `v = a * b + c` where `v` is the value of the `Non_coherent_unit`, `b` is the value of the base unit. `a` and `c` are the scaling and shift respectively. -Example of `Non_coherent_unit`s are `minute`, `hour` and `degree_Celcius`. +Examples of `Non_coherent_unit`s are `minute`, `hour` and `degree_Celcius`. A `Non_coherent_unit` is a templated struct that has the scaling factor, the shift and the base unit as template parameters. `minute` and `hour` are defined by ```c++ -struct minute : Non_coherent_unit<(TU_TYPE)60.0, (TU_TYPE)0.0, second> { - using Non_coherent_unit<(TU_TYPE)60.0, (TU_TYPE)0.0, second>::Base; -}; +using minute = Non_coherent_unit<(TU_TYPE)60.0, (TU_TYPE)0.0, second>; -struct hour : Non_coherent_unit<(TU_TYPE)60.0, (TU_TYPE)0.0, minute> { - using Non_coherent_unit<(TU_TYPE)60.0, (TU_TYPE)0.0, minute>::Base; -}; +struct hour = Non_coherent_unit<(TU_TYPE)60.0, (TU_TYPE)0.0, minute>; ``` `degree_Celsius` is defined by ```c++ -struct degree_Celsius : Non_coherent_unit<(TU_TYPE)1.0, (TU_TYPE)273.15, kelvin> { - using Non_coherent_unit<(TU_TYPE)1.0, (TU_TYPE)273.15, kelvin>::Base; -}; +struct degree_Celsius = Non_coherent_unit<(TU_TYPE)1.0, (TU_TYPE)273.15, kelvin>; ``` -The `using` statement is of internal concern only. If new `Non_coherent_unit`s are created, just follow the pattern. - #### Unit The `Unit` is the intended public unit type. @@ -298,6 +299,8 @@ std::cout << ms.value << " " << mi.value << std::endl; // prints 5.0 8.3333e-5 The following prefixes are defined and can be used when creating `Unit`s. +* quecto = 10-30 +* ronto = 10-27 * yocto = 10-24 * zepto = 10-21 * atto = 10-18 @@ -319,6 +322,8 @@ The following prefixes are defined and can be used when creating `Unit`s. * exa = 1018 * zetta = 1021 * yotta = 1024 +* ronna = 1027 +* quetta = 1030 ### Functions @@ -368,14 +373,20 @@ std::cout << cu.base_value << std::endl; // prints 3900.0 This is because TU does not know what `Unit` to construct from the operation. TU falls back on the fundamental `Coherent_unit`s and `cu` will be of type ```c++ -Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>; +Coherent_unit + ,m + ,kg + ,A + ,K + ,mol + ,cd>; ``` Note also that `Coherent_unit` does not have a `value` member but only a `base_value` If we would like a specific `Unit` representation of the operation, we have to explicitly state the `Unit` as in the first example and the result of the operation will be used to construct the desired `Unit`. -Applying the `+` and `-` operators on `Unit`s that don't have the same underlying `Coherent_unit` will result in compilation failure e.g. it is not possible to add to variables of type `newton` and `second`. +Applying the `+` and `-` operators on `Unit`s that don't have the same underlying `Coherent_unit` will result in compilation failure e.g. it is not possible to add two variables of type `newton` and `second`. #### \* / @@ -400,7 +411,13 @@ std::cout << cu.base_value << std::endl; // prints 5e-08 This is because TU does not know what `Unit` to construct from the operation. TU falls back on the fundamental `Coherent_units` and `cu` will be of type ```c++ -Coherent_unit, m<(TU_TYPE)0.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)1.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>; +Coherent_unit + ,m + ,kg + ,A + ,K + ,mol + ,cd>; ``` Note also that `Coherent_unit` does not have a `value` member but only a `base_value` @@ -426,21 +443,27 @@ TU implements a `pow` operator for units. ```c++ Unit me(5.0f); -auto ch = pow<2.0f>(me); +auto ch = pow>(me); std::cout << ch.base_value << std::endl; // prints 2.5 * 10^-5 ``` `ch` will be of type ```c++ -Coherent_unit, m<(TU_TYPE)2.0>, kg<(TU_TYPE)0.0>, A<(TU_TYPE)0.0>, K<(TU_TYPE)0.0>, mol<(TU_TYPE)0.0>, cd<(TU_TYPE)0.0>>; +Coherent_unit + ,m + ,kg + ,A + ,K + ,mol + ,cd>; ``` To construct a `Unit` directly we could do ```c++ Unit me(5.0f); -Unit m2 = pow<2.0f>(me); +Unit m2 = pow>(me); std::cout << m2.value << std::endl; // prints 2.5 * 10^-2 ``` @@ -449,7 +472,7 @@ Note that `Unit` means 10-3m2 To the unit (mm)2 is equivalent to `Unit` -Note that the power is not restricted to integers. +Note that the power is restricted to std::ratio. #### sqrt @@ -461,21 +484,28 @@ sqrt(unit). is equivalent to ```c++ -pow<0.5>(unit). +pow>(unit). ``` -Note that since TU uses floating point powers, it is not guaranteed that applying first `pow<2.0>` and the `sqrt` on a unit would yield the exact unit back. - #### unop TU supports unary operations on scalar units i.e. units where all basic unit powers are `0`. Examples of scalar units is `radian` and `degree`. `unop` is a template function that applies any unary function that takes a TU_TYPE -and returns a TU_TYPE to the underlying value of the unit if it is a scalar unit. The function returns a scalar Coherent_unit initialized with the value of the performed operation. This makes it possible to operate with any unary function (subjected to the restrictions above) from the standard library on a Unit or Coherent_unit. unop can take both unary functions and lambda expressions as template parameter. +and returns a TU_TYPE to the underlying **base_value** of the unit if it is a scalar unit. The function returns a scalar Coherent_unit initialized with the value of the performed operation. This makes it possible to operate with any unary function (subjected to the restrictions above) from the standard library on a Unit or Coherent_unit. unop can take both unary functions and lambda expressions as template parameter. ```c++ -Unit angle(90); -std::cout << unop(angle).base_value; // prints 1 +Unit angle_d(90); +std::cout << unop(angle_d).base_value; // prints 1 + +Unit angle_r(PI); +std::cout << unop(angle_r).base_value; // prints 1 + +constexpr auto lambda = [](TU_TYPE v) { + return v + (TU_TYPE)1.0; +}; + +std::cout << unop(angle_d).base_value; // prints 2.5708 i.e. PI/2.0 + 1.0 ``` Note that `unop` operates on the `base_value` on a unit. In the case of `degree` the base unit is `radian` (90 degrees == pi/2 radians) and the `std::sin` function yields the correct result. From 263e8a2511bada9fe8dbd61bead010ef72a678e5 Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Fri, 15 Mar 2024 17:25:04 +0100 Subject: [PATCH 14/18] Remove unnecessary constructor --- typesafe_units/include/tu/typesafe_units.h | 1 - 1 file changed, 1 deletion(-) diff --git a/typesafe_units/include/tu/typesafe_units.h b/typesafe_units/include/tu/typesafe_units.h index 5541780..9914205 100644 --- a/typesafe_units/include/tu/typesafe_units.h +++ b/typesafe_units/include/tu/typesafe_units.h @@ -182,7 +182,6 @@ struct Coherent_unit_base : Unit_fundament { using Base = Coherent_unit_base; constexpr Coherent_unit_base() noexcept = default; Coherent_unit_base(TU_TYPE v) noexcept : base_value(v){} - Coherent_unit_base(const Coherent_unit_base& u) noexcept : base_value(u.base_value) {} template Date: Fri, 15 Mar 2024 17:25:31 +0100 Subject: [PATCH 15/18] Tidy up tests --- test/test.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/test.cpp b/test/test.cpp index 96a8af8..9bb3093 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -187,9 +187,12 @@ int main() { auto c2 = Coherent_unit_base, std::ratio<2>>(c1); t.template assert>(val, c2.base_value , __LINE__); + auto c3 = Coherent_unit_base, std::ratio<2>>(std::move(c2)); + t.template assert>(val, c3.base_value , __LINE__); + Unit f(val); - Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>, std::ratio<0>, std::ratio<0>> c3 = Coherent_unit_base(f); - t.template assert>((val * 1.0e-3f - (TU_TYPE)32.0)/1.8f + (TU_TYPE)273.15, c3.base_value , __LINE__); + Coherent_unit_base, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>, std::ratio<0>, std::ratio<0>> c4 = Coherent_unit_base(f); + t.template assert>((val * 1.0e-3f - (TU_TYPE)32.0)/1.8f + (TU_TYPE)273.15, c4.base_value , __LINE__); } ); @@ -232,9 +235,6 @@ int main() { Unit s(value); t.template assert>(s.value, value, __LINE__); - //SUnit ss(value); - //t.template assert>(ss.value, value, __LINE__); - Unit s2 = value; t.template assert>(s2.value, value, __LINE__); @@ -580,8 +580,8 @@ int main() { t.assert_true(add_res.value == (TU_TYPE)3.0, __LINE__); t.assert_true(add_res.value == (TU_TYPE)3.0, __LINE__); } + ); - static_assert(tu::hour::base_multiplier == 3600.0f); return Test_stats::fail; } \ No newline at end of file From 2d0c3a5e3f6c6881e3e641c89bae89ed3c66d73f Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Fri, 15 Mar 2024 20:16:35 +0100 Subject: [PATCH 16/18] Updated changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ff6597..3130507 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Powers are now rational numbers and not floating point values. + ### Added - New coherent unit `scalar` that can be used in binary operations with units. - New constructor `Coherent_unit(TU_TYPE)` so that a coherent unit can be created with value. +- New prefixes `quecto`, `ronto`, `ronna`, `quetta`. ### Fixed From 3ed902d37687bbdd59c1ae8a7544dbedcb09062f Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Fri, 15 Mar 2024 20:37:28 +0100 Subject: [PATCH 17/18] Change project version to 0.2.0 --- CMakeLists.txt | 2 +- TUConfigVersion.cmake | 53 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b2d2071..d6228ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.12) -project(TU VERSION 0.1.0 LANGUAGES CXX) +project(TU VERSION 0.2.0 LANGUAGES CXX) enable_testing() add_subdirectory(typesafe_units) diff --git a/TUConfigVersion.cmake b/TUConfigVersion.cmake index abaed05..044f151 100644 --- a/TUConfigVersion.cmake +++ b/TUConfigVersion.cmake @@ -9,16 +9,52 @@ # The variable CVF_VERSION must be set before calling configure_file(). -set(PACKAGE_VERSION "0.1.0") +if (PACKAGE_FIND_VERSION_RANGE) + message(AUTHOR_WARNING + "`find_package()` specify a version range but the version strategy " + "(ExactVersion) of the module `${PACKAGE_FIND_NAME}` is incompatible " + "with this request. Only the lower endpoint of the range will be used.") +endif() + +set(PACKAGE_VERSION "0.2.0") + +if("0.2.0" MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)") # strip the tweak version + set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}") + set(CVF_VERSION_MINOR "${CMAKE_MATCH_2}") + set(CVF_VERSION_PATCH "${CMAKE_MATCH_3}") -if("0.1.0" MATCHES "^([0-9]+\\.[0-9]+\\.[0-9]+)\\.") # strip the tweak version - set(CVF_VERSION_NO_TWEAK "${CMAKE_MATCH_1}") + if(NOT CVF_VERSION_MAJOR VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" CVF_VERSION_MAJOR "${CVF_VERSION_MAJOR}") + endif() + if(NOT CVF_VERSION_MINOR VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" CVF_VERSION_MINOR "${CVF_VERSION_MINOR}") + endif() + if(NOT CVF_VERSION_PATCH VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" CVF_VERSION_PATCH "${CVF_VERSION_PATCH}") + endif() + + set(CVF_VERSION_NO_TWEAK "${CVF_VERSION_MAJOR}.${CVF_VERSION_MINOR}.${CVF_VERSION_PATCH}") else() - set(CVF_VERSION_NO_TWEAK "0.1.0") + set(CVF_VERSION_NO_TWEAK "0.2.0") endif() -if(PACKAGE_FIND_VERSION MATCHES "^([0-9]+\\.[0-9]+\\.[0-9]+)\\.") # strip the tweak version - set(REQUESTED_VERSION_NO_TWEAK "${CMAKE_MATCH_1}") +if(PACKAGE_FIND_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)") # strip the tweak version + set(REQUESTED_VERSION_MAJOR "${CMAKE_MATCH_1}") + set(REQUESTED_VERSION_MINOR "${CMAKE_MATCH_2}") + set(REQUESTED_VERSION_PATCH "${CMAKE_MATCH_3}") + + if(NOT REQUESTED_VERSION_MAJOR VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" REQUESTED_VERSION_MAJOR "${REQUESTED_VERSION_MAJOR}") + endif() + if(NOT REQUESTED_VERSION_MINOR VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" REQUESTED_VERSION_MINOR "${REQUESTED_VERSION_MINOR}") + endif() + if(NOT REQUESTED_VERSION_PATCH VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" REQUESTED_VERSION_PATCH "${REQUESTED_VERSION_PATCH}") + endif() + + set(REQUESTED_VERSION_NO_TWEAK + "${REQUESTED_VERSION_MAJOR}.${REQUESTED_VERSION_MINOR}.${REQUESTED_VERSION_PATCH}") else() set(REQUESTED_VERSION_NO_TWEAK "${PACKAGE_FIND_VERSION}") endif() @@ -34,11 +70,6 @@ if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) endif() -# if the installed project requested no architecture check, don't perform the check -if("FALSE") - return() -endif() - # if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it: if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "") return() From c0e77ff13c9cdc95076c3d1b5171615274fdf042 Mon Sep 17 00:00:00 2001 From: Klas Marcks Date: Sat, 16 Mar 2024 07:18:43 +0100 Subject: [PATCH 18/18] Updated release date for 0.2.0 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3130507..38cf61d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.2.0] - 2024-03-16 ### Changed