diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index a667f22c..532abc63 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -15,6 +15,8 @@ - FPS system added (thanks [@mikeford3](https://github.com/mikeford3)) - `quantity_point` support added (thanks [@johelegp](https://github.com/johelegp)) - `ratio` changed to the NTTP kind + - `exp` and `Exp` renamed to `exponent` and `Exponent` + - Added support for `exp()` mathematical support - **0.5.0 May 17, 2020** - Major refactoring and rewrite of the library diff --git a/docs/_static/img/concepts.png b/docs/_static/img/concepts.png index 256bbd4a..fd9fd816 100644 Binary files a/docs/_static/img/concepts.png and b/docs/_static/img/concepts.png differ diff --git a/docs/_static/img/downcast_1.png b/docs/_static/img/downcast_1.png index d8eaa468..203d9039 100644 Binary files a/docs/_static/img/downcast_1.png and b/docs/_static/img/downcast_1.png differ diff --git a/docs/_static/img/downcast_2.png b/docs/_static/img/downcast_2.png index 8a37faab..94729025 100644 Binary files a/docs/_static/img/downcast_2.png and b/docs/_static/img/downcast_2.png differ diff --git a/docs/design/downcasting.rst b/docs/design/downcasting.rst index e5173482..37775459 100644 --- a/docs/design/downcasting.rst +++ b/docs/design/downcasting.rst @@ -14,18 +14,18 @@ The same can be observed during debugging of a source code that use template ali Let's assume that we want to provide a user friendly name for a derived dimension of capacitance quantity. Other libraries will do it in the following way:: - using dim_capacitance = detail::derived_dimension_base, - exp, - exp, - exp>; + using dim_capacitance = detail::derived_dimension_base, + exponent, + exponent, + exponent>; The above solution does provide a good developer's experience but a really poor one for the end user. If we will get a compilation error message containing `dim_capacitance` in most cases the compiler will print the following type instead of the alias:: - units::detail::derived_dimension_base, - units::exp, units::exp, - units::exp > + units::detail::derived_dimension_base, + units::exponent, units::exponent, units::exponent > You can notice that in case of **mp-units** even this long syntax was carefully selected to provide quite good user experience (some other units libraries produce a type that cannot easily @@ -48,7 +48,7 @@ to use inheritance: .. http://www.nomnoml.com - [derived_dimension_base>]<:-[dim_area] + [derived_dimension_base>]<:-[dim_area] This gives us a nice looking strong type when directly used by the user. However, we just got ourselves into problems. The library's framework does not know how to switch from a long @@ -79,9 +79,9 @@ The downcasting facility is provided by injecting two classes into our hierarchy .. http://www.nomnoml.com - [downcast_base>>]<:-[detail::derived_dimension_base>] - [detail::derived_dimension_base>]<:-[downcast_child>>] - [downcast_child>>]<:-[dim_area] + [downcast_base>>]<:-[detail::derived_dimension_base>] + [detail::derived_dimension_base>]<:-[downcast_child>>] + [downcast_child>>]<:-[dim_area] In the above example: diff --git a/docs/faq.rst b/docs/faq.rst index 5b1e8d64..12c8b5ec 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -97,14 +97,6 @@ Unfortunately, if `using-directives `_ function. In such a case the library's `time` function needs to be prefixed with at least one (or all) namespace names. -error: template argument 1 is invalid -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Again, usage of ``using namespace units`` -`using-directive `_ may result in -the collision between `units::exp` class template and C `exp `_ -function. In such a case the library's `exp` class template needs to be prefixed with `units` namespace name. - diff --git a/docs/framework/basic_concepts.rst b/docs/framework/basic_concepts.rst index 5406f90a..1ba4171b 100644 --- a/docs/framework/basic_concepts.rst +++ b/docs/framework/basic_concepts.rst @@ -13,9 +13,9 @@ The most important concepts in the library are `Unit`, `Dimension`, http://www.nomnoml.com [Dimension| - [base_dimension]<-[exp] - [derived_dimension]<-[exp] - [exp]<-[derived_dimension] + [base_dimension]<-[exponent] + [derived_dimension]<-[exponent] + [exponent]<-[derived_dimension] ] [Quantity| diff --git a/docs/framework/dimensions.rst b/docs/framework/dimensions.rst index 6185c939..b60c679c 100644 --- a/docs/framework/dimensions.rst +++ b/docs/framework/dimensions.rst @@ -147,9 +147,9 @@ The above dimensions can be defined in the library with the namespace si { struct dim_area : derived_dimension> {}; + exponent> {}; struct dim_speed : derived_dimension, exp> {}; + exponent, exponent> {}; } @@ -179,9 +179,9 @@ matter. Even if we define the above as: namespace si { struct dim_area : derived_dimension, exp> {}; + exponent, exponent> {}; struct dim_speed : derived_dimension, exp> {}; + exponent, exponent> {}; } diff --git a/docs/framework/units.rst b/docs/framework/units.rst index a10586f4..3faac996 100644 --- a/docs/framework/units.rst +++ b/docs/framework/units.rst @@ -120,17 +120,17 @@ will result in a different unnamed unit symbol: :emphasize-lines: 2-4, 6-8, 10-12 struct dim_momentum : derived_dimension, - exp, - exp> {}; // kg ⋅ m/s + exponent, + exponent, + exponent> {}; // kg ⋅ m/s struct dim_momentum : derived_dimension, - exp, - exp> {}; // m ⋅ kg/s + exponent, + exponent, + exponent> {}; // m ⋅ kg/s struct dim_momentum : derived_dimension, - exp, - exp> {}; // 1/s ⋅ m ⋅ kg + exponent, + exponent, + exponent> {}; // 1/s ⋅ m ⋅ kg where ``kilogram_metre_per_second`` is defined as:: @@ -143,8 +143,8 @@ However, the easiest way to define momentum is just to use the :emphasize-lines: 3 struct dim_momentum : derived_dimension, - exp> {}; // kg ⋅ m/s + exponent, + exponent> {}; // kg ⋅ m/s In such a case the library will do its magic and will automatically unpack a provided derived dimension to its base dimensions in order to @@ -161,8 +161,8 @@ of ``N/m``): :emphasize-lines: 2 struct dim_surface_tension : derived_dimension, - exp> {}; // N/m + exponent, + exponent> {}; // N/m If we defined the above in terms of base units we would end up with a ``kg/s²`` derived unit symbol. @@ -194,7 +194,7 @@ where `no_prefix` is a special tag type describing that the library should not allow to define a new prefixed unit that would use this unit as a reference ("kilohours" does not have much sense, right?). The `ratio` type used in the definition is really similar to ``std::ratio`` but it takes -an additional ``Exp`` template parameter that defines the exponent of the ratio. +an additional ``Exponent`` template parameter that defines the exponent of the ratio. Another important difference is the fact that the objects of that class are used as class NTTPs rather then a type template parameter kind. diff --git a/docs/reference/core/concepts.rst b/docs/reference/core/concepts.rst index 79e8367d..09fe5b60 100644 --- a/docs/reference/core/concepts.rst +++ b/docs/reference/core/concepts.rst @@ -27,7 +27,7 @@ Concepts .. concept:: template Exponent - A concept matching dimension's exponents. Satisfied by all instantiations of :class:`exp`. + A concept matching dimension's exponents. Satisfied by all instantiations of :class:`exponent`. .. concept:: template DerivedDimension diff --git a/docs/reference/core/types/dimensions.rst b/docs/reference/core/types/dimensions.rst index b7bfe170..2592ac4b 100644 --- a/docs/reference/core/types/dimensions.rst +++ b/docs/reference/core/types/dimensions.rst @@ -4,7 +4,7 @@ Dimensions .. doxygenstruct:: units::base_dimension :members: -.. doxygenstruct:: units::exp +.. doxygenstruct:: units::exponent :members: .. doxygenstruct:: units::derived_dimension diff --git a/docs/use_cases/extensions.rst b/docs/use_cases/extensions.rst index 219a049d..52ca9ffd 100644 --- a/docs/use_cases/extensions.rst +++ b/docs/use_cases/extensions.rst @@ -115,7 +115,7 @@ coherent unit:: // new derived dimensions struct dim_desk_rate : derived_dimension, exp> {}; + exponent, exponent> {}; // our unit of interest for a new derived dimension struct desk_per_hour : deduced_unit {}; @@ -164,7 +164,8 @@ With the above we can now define a new derived dimension:: struct person_per_square_metre : unit {}; struct dim_occupancy_rate : derived_dimension, exp> {}; + exponent, + exponent> {}; struct person_per_desk : deduced_unit {}; diff --git a/docs/use_cases/unknown_dimensions.rst b/docs/use_cases/unknown_dimensions.rst index 3456358e..c71ea45b 100644 --- a/docs/use_cases/unknown_dimensions.rst +++ b/docs/use_cases/unknown_dimensions.rst @@ -46,7 +46,7 @@ we forget to include a header file with the resulting dimension definition: constexpr auto result = 144q_km / 2q_h; static_assert(is_same_v, exp>>); + unknown_dimension, exponent>>); static_assert(is_same_v>); diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 3a2d15ee..afe0cd50 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -33,7 +33,6 @@ add_example(clcpp_response) add_example(conversion_factor) add_example(experimental_angle) add_example(foot_pound_second) -add_example(glide_computer) add_example(kalman_filter-alpha_beta_filter_example2) conan_check_testing(linear_algebra) @@ -51,6 +50,7 @@ if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") # TODO Those examples use Concepts terse syntax not yet supported by MSVC add_example(avg_speed) +add_example(glide_computer) add_example(hello_units) add_example(total_energy) diff --git a/example/alternative_namespaces/capacitor_time_curve.cpp b/example/alternative_namespaces/capacitor_time_curve.cpp index 8ff58195..e62a1408 100644 --- a/example/alternative_namespaces/capacitor_time_curve.cpp +++ b/example/alternative_namespaces/capacitor_time_curve.cpp @@ -23,8 +23,8 @@ #include #include #include +#include #include "./voltage.h" -#include #include using namespace units::experimental; @@ -41,7 +41,7 @@ int main() constexpr auto R = 4.7q_kR; for (auto t = 0q_ms; t <= 50q_ms; ++t) { - const auto Vt = V0 * std::exp(-t / (R * C)); + const auto Vt = V0 * units::exp(-t / (R * C)); std::cout << "at " << t << " voltage is "; diff --git a/example/capacitor_time_curve.cpp b/example/capacitor_time_curve.cpp index 04186e78..cdfa9fc1 100644 --- a/example/capacitor_time_curve.cpp +++ b/example/capacitor_time_curve.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include int main() @@ -41,7 +41,7 @@ int main() constexpr auto R = 4.7q_kR; for (auto t = 0q_ms; t <= 50q_ms; ++t) { - const Voltage AUTO Vt = V0 * std::exp(-t / (R * C)); + const Voltage AUTO Vt = V0 * units::exp(-t / (R * C)); std::cout << "at " << t << " voltage is "; diff --git a/example/glide_computer.cpp b/example/glide_computer.cpp index 771ce780..b35dacec 100644 --- a/example/glide_computer.cpp +++ b/example/glide_computer.cpp @@ -46,6 +46,7 @@ template requires Quantity || QuantityPoint class vector { public: + using value_type = Q; using magnitude_type = Q; static constexpr direction dir = D; @@ -74,6 +75,43 @@ public: return *this; } + template + [[nodiscard]] friend constexpr auto operator+(const vector& lhs, const vector& rhs) + requires requires { lhs.magnitude() + rhs.magnitude(); } + { + using ret_type = decltype(lhs.magnitude() + rhs.magnitude()); + return vector(lhs.magnitude() + rhs.magnitude()); + } + + template + [[nodiscard]] friend constexpr auto operator-(const vector& lhs, const vector& rhs) + requires requires { lhs.magnitude() - rhs.magnitude(); } + { + using ret_type = decltype(lhs.magnitude() - rhs.magnitude()); + return vector(lhs.magnitude() - rhs.magnitude()); + } + + template + [[nodiscard]] friend constexpr auto operator*(const vector& lhs, const V& value) + requires (Scalar || Dimensionless) && requires { lhs.magnitude() * value; } + { + return vector(lhs.magnitude() * value); + } + + template + [[nodiscard]] friend constexpr auto operator*(const V& value, const vector& rhs) + requires (Scalar || Dimensionless) && requires { value * rhs.magnitude(); } + { + return vector(value * rhs.magnitude()); + } + + template + [[nodiscard]] friend constexpr auto operator/(const vector& lhs, const vector& rhs) + requires requires { lhs.magnitude() / rhs.magnitude(); } + { + return lhs.magnitude() / rhs.magnitude(); + } + #if COMP_MSVC || COMP_GCC >= 10 template @@ -149,42 +187,6 @@ private: Q magnitude_{}; }; -template -[[nodiscard]] constexpr auto operator+(const vector& lhs, const vector& rhs) - requires requires { lhs.magnitude() + rhs.magnitude(); } -{ - using ret_type = decltype(lhs.magnitude() + rhs.magnitude()); - return vector(lhs.magnitude() + rhs.magnitude()); -} - -template -[[nodiscard]] constexpr auto operator-(const vector& lhs, const vector& rhs) - requires requires { lhs.magnitude() - rhs.magnitude(); } -{ - using ret_type = decltype(lhs.magnitude() - rhs.magnitude()); - return vector(lhs.magnitude() - rhs.magnitude()); -} - -template -[[nodiscard]] constexpr auto operator*(const vector& lhs, const V& value) - requires requires { lhs.magnitude() * value; } -{ - return vector(lhs.magnitude() * value); -} - -template -[[nodiscard]] constexpr auto operator*(const V& value, const vector& rhs) - requires requires { value * rhs.magnitude(); } -{ - return vector(value * rhs.magnitude()); -} - -template -[[nodiscard]] constexpr auto operator/(const vector& lhs, const vector& rhs) - requires requires { lhs.magnitude() / rhs.magnitude(); } -{ - return lhs.magnitude() / rhs.magnitude(); -} template inline constexpr bool is_vector = false; @@ -276,7 +278,7 @@ auto get_gliders() return gliders; } -constexpr double glide_ratio(const glider::polar_point& polar) +constexpr Dimensionless AUTO glide_ratio(const glider::polar_point& polar) { return polar.v / -polar.climb; } diff --git a/src/include/units/bits/base_units_ratio.h b/src/include/units/bits/base_units_ratio.h index c8aa2855..5d9cba92 100644 --- a/src/include/units/bits/base_units_ratio.h +++ b/src/include/units/bits/base_units_ratio.h @@ -23,7 +23,7 @@ #pragma once #include -#include +#include #include namespace units::detail { @@ -45,7 +45,7 @@ constexpr ratio exp_ratio() template constexpr ratio base_units_ratio(exp_list) { - return (exp_ratio() * ...); + return (exp_ratio() * ... * ratio(1)); } } // namespace units::detail diff --git a/src/include/units/bits/deduced_symbol_text.h b/src/include/units/bits/deduced_symbol_text.h index 38b071c0..b16d0f3b 100644 --- a/src/include/units/bits/deduced_symbol_text.h +++ b/src/include/units/bits/deduced_symbol_text.h @@ -77,7 +77,7 @@ constexpr auto exp_text() } template -inline constexpr int negative_exp_count = ((Es::num < 0 ? 1 : 0) + ...); +inline constexpr int negative_exp_count = ((Es::num < 0 ? 1 : 0) + ... + 0); template constexpr auto deduced_symbol_text(exp_list, std::index_sequence) diff --git a/src/include/units/bits/derived_dimension_base.h b/src/include/units/bits/derived_dimension_base.h index 71fdcc1b..74f8fa25 100644 --- a/src/include/units/bits/derived_dimension_base.h +++ b/src/include/units/bits/derived_dimension_base.h @@ -24,7 +24,7 @@ #include #include -#include +#include namespace units::detail { @@ -35,19 +35,18 @@ namespace units::detail { * quantities as a product of powers of factors corresponding to the base quantities, omitting any numerical factors. * A power of a factor is the factor raised to an exponent. * - * A derived dimension can be formed from multiple exponents (i.e. speed is represented as "exp, exp"). + * A derived dimension can be formed from multiple exponents (i.e. speed is represented as "exponent, exponent"). * It is also possible to form a derived dimension with only one exponent (i.e. frequency is represented as just - * "exp"). + * "exponent"). * * @note This class template is used by the library engine and should not be directly instantiated by the user. * - * @tparam E a first exponent of a derived dimension - * @tparam ERest zero or more following exponents of a derived dimension + * @tparam Es zero or more exponents of a derived dimension */ -template - requires (BaseDimension && ... && BaseDimension) -struct derived_dimension_base : downcast_base> { - using exponents = exp_list; +template + requires (BaseDimension && ...) +struct derived_dimension_base : downcast_base> { + using exponents = exp_list; }; template diff --git a/src/include/units/bits/dim_consolidate.h b/src/include/units/bits/dim_consolidate.h index aba1a706..fb3f11c5 100644 --- a/src/include/units/bits/dim_consolidate.h +++ b/src/include/units/bits/dim_consolidate.h @@ -23,7 +23,7 @@ #pragma once #include -#include +#include #include // TODO remove this dependency with #11 namespace units::detail { @@ -55,13 +55,13 @@ struct dim_consolidate> { }; template -struct dim_consolidate, exp, ERest...>> { +struct dim_consolidate, exponent, ERest...>> { // TODO: we have ration_add now, but dim_consolidate etc, now need to cope with our new ratio using r1 = std::ratio; using r2 = std::ratio; using r = std::ratio_add; using type = conditional>::type, - typename dim_consolidate, ERest...>>::type>; + typename dim_consolidate, ERest...>>::type>; }; } // namespace units::detail diff --git a/src/include/units/bits/dim_unpack.h b/src/include/units/bits/dim_unpack.h index fe9c6fd4..f9df5ee3 100644 --- a/src/include/units/bits/dim_unpack.h +++ b/src/include/units/bits/dim_unpack.h @@ -24,7 +24,7 @@ #include #include -#include +#include namespace units::detail { @@ -42,17 +42,17 @@ struct dim_unpack<> { }; template -struct dim_unpack, ERest...> { - using type = type_list_push_front::type, exp>; +struct dim_unpack, ERest...> { + using type = type_list_push_front::type, exponent>; }; template -struct dim_unpack, ERest...> { - using type = TYPENAME dim_unpack, Num, Den>, ERest...>::type; +struct dim_unpack, ERest...> { + using type = TYPENAME dim_unpack, Num, Den>, ERest...>::type; }; template -struct dim_unpack, Num, Den>, ERest...> { +struct dim_unpack, Num, Den>, ERest...> { using type = type_list_push_front::type, exp_multiply...>; }; diff --git a/src/include/units/bits/dimension_op.h b/src/include/units/bits/dimension_op.h index 325fcef1..a5fc0889 100644 --- a/src/include/units/bits/dimension_op.h +++ b/src/include/units/bits/dimension_op.h @@ -44,7 +44,7 @@ template struct equivalent_exp : std::false_type {}; template -struct equivalent_exp, exp> : equivalent_dim_impl {}; +struct equivalent_exp, exponent> : equivalent_dim_impl {}; template struct equivalent_derived_dim : std::false_type {}; @@ -68,11 +68,10 @@ inline constexpr bool equivalent_dim = detail::equivalent_dim_impl::valu * dimension. In such a case an `unknown_dimension` is created with a coherent unit of `unknown_coherent_unit` * and ratio(1). * - * @tparam E the list of exponents of ingredient dimensions - * @tparam ERest the list of exponents of ingredient dimensions + * @tparam Es the list of exponents of ingredient dimensions */ -template -struct unknown_dimension : derived_dimension, unknown_coherent_unit, E, ERest...> {}; +template +struct unknown_dimension : derived_dimension, unknown_coherent_unit, Es...> {}; namespace detail { @@ -113,11 +112,11 @@ struct dim_invert_impl; template struct dim_invert_impl { - using type = downcast_dimension>>; + using type = downcast_dimension>>; }; template -struct dim_invert_impl>> { +struct dim_invert_impl>> { using type = D; }; @@ -147,7 +146,7 @@ struct to_dimension> { }; template -struct to_dimension>> { +struct to_dimension>> { using type = D; }; @@ -168,12 +167,12 @@ struct dimension_multiply_impl; template struct dimension_multiply_impl { - using type = downcast_dimension>, derived_dimension_base>>>; + using type = downcast_dimension>, derived_dimension_base>>>; }; template struct dimension_multiply_impl { - using type = downcast_dimension>, typename D2::downcast_base_type>>; + using type = downcast_dimension>, typename D2::downcast_base_type>>; }; template @@ -202,11 +201,11 @@ struct dimension_sqrt_impl; template struct dimension_sqrt_impl { - using type = downcast_dimension>>; + using type = downcast_dimension>>; }; template -struct dimension_sqrt_impl>> { +struct dimension_sqrt_impl>> { using type = D; }; @@ -233,7 +232,7 @@ struct dimension_pow_impl; template struct dimension_pow_impl { - using type = downcast_dimension>>; + using type = downcast_dimension>>; }; template @@ -242,7 +241,7 @@ struct dimension_pow_impl { }; template -struct dimension_pow_impl>, N> { +struct dimension_pow_impl>, N> { using type = D; }; diff --git a/src/include/units/bits/to_string.h b/src/include/units/bits/to_string.h index 76625011..f2047f5b 100644 --- a/src/include/units/bits/to_string.h +++ b/src/include/units/bits/to_string.h @@ -36,28 +36,28 @@ template constexpr auto ratio_text() { if constexpr(R.num == 1 && R.den == 1 && R.exp != 0) { - return base_multiplier + superscript() + basic_fixed_string(" "); + return base_multiplier + superscript(); } else if constexpr(R.num != 1 || R.den != 1 || R.exp != 0) { auto txt = basic_fixed_string("[") + regular(); if constexpr(R.den == 1) { if constexpr(R.exp == 0) { - return txt + basic_fixed_string("] "); + return txt + basic_fixed_string("]"); } else { return txt + " " + base_multiplier + superscript() + - basic_fixed_string("] "); + basic_fixed_string("]"); } } else { if constexpr(R.exp == 0) { return txt + basic_fixed_string("/") + regular() + - basic_fixed_string("] "); + basic_fixed_string("]"); } else { return txt + basic_fixed_string("/") + regular() + " " + base_multiplier + superscript() + - basic_fixed_string("] "); + basic_fixed_string("]"); } } } @@ -66,7 +66,7 @@ constexpr auto ratio_text() } } -template +template constexpr auto prefix_or_ratio_text() { if constexpr(R.num == 1 && R.den == 1 && R.exp == 0) { @@ -84,12 +84,20 @@ constexpr auto prefix_or_ratio_text() } else { // print as a ratio of the coherent unit - return ratio_text(); + constexpr auto txt = ratio_text(); + if constexpr(SymbolLen > 0 && txt.standard().size() > 0) + return txt + basic_fixed_string(" "); + else + return txt; } } else { // print as a ratio of the coherent unit - return ratio_text(); + constexpr auto txt = ratio_text(); + if constexpr(SymbolLen > 0 && txt.standard().size() > 0) + return txt + basic_fixed_string(" "); + else + return txt; } } } @@ -97,8 +105,7 @@ constexpr auto prefix_or_ratio_text() template constexpr auto derived_dimension_unit_text(exp_list, std::index_sequence) { - constexpr auto neg_exp = negative_exp_count; - return (exp_text::symbol, neg_exp, Idxs>() + ...); + return (exp_text::symbol, negative_exp_count, Idxs>() + ... + basic_symbol_text(basic_fixed_string(""))); } template @@ -129,6 +136,11 @@ constexpr auto exp_list_with_named_units(exp_list) return type_list_join(); } +constexpr auto exp_list_with_named_units(exp_list<> empty) +{ + return empty; +} + template constexpr auto derived_dimension_unit_text() { @@ -150,15 +162,18 @@ constexpr auto unit_text() else { // print as a prefix or ratio of a coherent unit using coherent_unit = dimension_unit; - auto prefix_txt = prefix_or_ratio_text(); if constexpr(has_symbol) { // use predefined coherent unit symbol - return prefix_txt + coherent_unit::symbol; + constexpr auto symbol_text = coherent_unit::symbol; + constexpr auto prefix_txt = prefix_or_ratio_text(); + return prefix_txt + symbol_text; } else { // use derived dimension ingredients to create a unit symbol - return prefix_txt + derived_dimension_unit_text(); + constexpr auto symbol_text = derived_dimension_unit_text(); + constexpr auto prefix_txt = prefix_or_ratio_text(); + return prefix_txt + symbol_text; } } } diff --git a/src/include/units/concepts.h b/src/include/units/concepts.h index 549c8742..1ec4e064 100644 --- a/src/include/units/concepts.h +++ b/src/include/units/concepts.h @@ -128,23 +128,23 @@ concept BaseDimension = detail::is_derived_from_base_dimension; namespace detail { template -inline constexpr bool is_exp = false; +inline constexpr bool is_exponent = false; } // namespace detail /** * @brief A concept matching dimension's exponents. * - * Satisfied by all specializations of :class:`exp`. + * Satisfied by all specializations of :class:`exponent`. */ template -concept Exponent = detail::is_exp; +concept Exponent = detail::is_exponent; // DerivedDimension namespace detail { -template - requires (BaseDimension && ... && BaseDimension) +template + requires (BaseDimension && ...) struct derived_dimension_base; } // namespace detail diff --git a/src/include/units/data/bitrate.h b/src/include/units/data/bitrate.h index fa2068af..13a3078d 100644 --- a/src/include/units/data/bitrate.h +++ b/src/include/units/data/bitrate.h @@ -30,7 +30,7 @@ namespace units::data { struct bit_per_second : unit {}; -struct dim_bitrate : derived_dimension, exp> {}; +struct dim_bitrate : derived_dimension, exponent> {}; struct kibibit_per_second : deduced_unit {}; struct mebibit_per_second : deduced_unit {}; diff --git a/src/include/units/derived_dimension.h b/src/include/units/derived_dimension.h index e622b63f..4fb281bf 100644 --- a/src/include/units/derived_dimension.h +++ b/src/include/units/derived_dimension.h @@ -29,7 +29,7 @@ #include #include #include -#include +#include namespace units { @@ -75,12 +75,11 @@ using make_dimension = TYPENAME to_derived_dimension_base -struct derived_dimension : downcast_child> { - using recipe = exp_list; +template +struct derived_dimension : downcast_child> { + using recipe = exp_list; using coherent_unit = U; static constexpr ratio base_units_ratio = detail::base_units_ratio(typename derived_dimension::exponents()); }; diff --git a/src/include/units/dimensionless.h b/src/include/units/dimensionless.h new file mode 100644 index 00000000..5228c63b --- /dev/null +++ b/src/include/units/dimensionless.h @@ -0,0 +1,46 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include + +namespace units { + +struct unitless : named_unit {}; +struct percent : named_scaled_unit {}; + +/** + * @brief Dimension one + * + * Dimension for which all the exponents of the factors corresponding to the base + * dimensions are zero. Also commonly named as "dimensionless". + */ +struct dim_one : derived_dimension {}; + +template +concept Dimensionless = QuantityOf; + +template +using dimensionless = quantity; + +} // namespace units diff --git a/src/include/units/exp.h b/src/include/units/exponent.h similarity index 91% rename from src/include/units/exp.h rename to src/include/units/exponent.h index 61cfe0d7..1a8b6185 100644 --- a/src/include/units/exp.h +++ b/src/include/units/exponent.h @@ -35,17 +35,17 @@ namespace units { * @tparam Den denominator of the factor */ template -struct exp { +struct exponent { using dimension = Dim; static constexpr int num = Num; static constexpr int den = Den; }; -// is_exp +// is_exponent namespace detail { template -inline constexpr bool is_exp> = true; +inline constexpr bool is_exponent> = true; } // namespace detail @@ -58,7 +58,7 @@ struct exp_less : base_dimension_less -constexpr exp exp_invert_impl(exp); +constexpr exponent exp_invert_impl(exponent); } // namespace detail @@ -71,7 +71,7 @@ namespace detail { template struct exp_multiply_impl { static constexpr ratio r = ratio(E::num, E::den) * ratio(Num, Den); - using type = exp; + using type = exponent; }; } // namespace detail diff --git a/src/include/units/math.h b/src/include/units/math.h index 4ef1f982..f528c1c1 100644 --- a/src/include/units/math.h +++ b/src/include/units/math.h @@ -79,6 +79,19 @@ inline Quantity AUTO sqrt(const Q& q) noexcept return quantity(static_cast(std::sqrt(q.count()))); } +/** + * @brief Computes Euler's raised to the given power + * + * @param q Quantity being the base of the operation + * @return Quantity The value of the same quantity type + */ +template +inline quantity exp(const quantity& q) +{ + using coherent_unit = dimension_unit; + return quantity_cast(quantity(std::exp(quantity_cast(q).count()))); +} + /** * @brief Computes the absolute value of a quantity * @@ -86,7 +99,7 @@ inline Quantity AUTO sqrt(const Q& q) noexcept * @return Quantity The absolute value of a provided quantity */ template -constexpr Quantity AUTO abs(const Q& q) noexcept +inline Quantity AUTO abs(const Q& q) noexcept requires requires { std::abs(q.count()); } { return Q(std::abs(q.count())); diff --git a/src/include/units/physical/dimensions.h b/src/include/units/physical/dimensions.h index 40e13b0b..f23859f5 100644 --- a/src/include/units/physical/dimensions.h +++ b/src/include/units/physical/dimensions.h @@ -65,126 +65,126 @@ struct dim_angle : base_dimension<"A", U> {}; template A, DimensionOf T> -struct dim_angular_velocity : derived_dimension, exp> {}; +struct dim_angular_velocity : derived_dimension, exponent> {}; template T> -struct dim_frequency : derived_dimension> {}; +struct dim_frequency : derived_dimension> {}; template L> -struct dim_area : derived_dimension> {}; +struct dim_area : derived_dimension> {}; template L> -struct dim_volume : derived_dimension> {}; +struct dim_volume : derived_dimension> {}; template L, DimensionOf T> -struct dim_speed : derived_dimension, exp> {}; +struct dim_speed : derived_dimension, exponent> {}; template L, DimensionOf T> -struct dim_acceleration : derived_dimension, exp> {}; +struct dim_acceleration : derived_dimension, exponent> {}; template M, DimensionOf A> -struct dim_force : derived_dimension, exp> {}; +struct dim_force : derived_dimension, exponent> {}; template M, DimensionOf V> -struct dim_momentum : derived_dimension, exp> {}; +struct dim_momentum : derived_dimension, exponent> {}; template F, DimensionOf L> -struct dim_energy : derived_dimension, exp> {}; +struct dim_energy : derived_dimension, exponent> {}; template E, DimensionOf A> -struct dim_torque : derived_dimension, exp> {}; +struct dim_torque : derived_dimension, exponent> {}; template M, DimensionOf L> -struct dim_density : derived_dimension, exp> {}; +struct dim_density : derived_dimension, exponent> {}; template E, DimensionOf T> -struct dim_power : derived_dimension, exp> {}; +struct dim_power : derived_dimension, exponent> {}; template P, DimensionOf C> -struct dim_voltage : derived_dimension, exp> {}; +struct dim_voltage : derived_dimension, exponent> {}; template V, DimensionOf C> -struct dim_resistance : derived_dimension, exp> {}; +struct dim_resistance : derived_dimension, exponent> {}; template T, DimensionOf C> -struct dim_electric_charge : derived_dimension, exp> {}; +struct dim_electric_charge : derived_dimension, exponent> {}; template C, DimensionOf V> -struct dim_capacitance : derived_dimension, exp> {}; +struct dim_capacitance : derived_dimension, exponent> {}; template F, DimensionOf L> -struct dim_surface_tension : derived_dimension, exp> {}; +struct dim_surface_tension : derived_dimension, exponent> {}; template F, DimensionOf A> -struct dim_pressure : derived_dimension, exp> {}; +struct dim_pressure : derived_dimension, exponent> {}; template V, DimensionOf T, DimensionOf L> -struct dim_magnetic_induction : derived_dimension, exp, exp> {}; +struct dim_magnetic_induction : derived_dimension, exponent, exponent> {}; template B, DimensionOf A> -struct dim_magnetic_flux : derived_dimension, exp> {}; +struct dim_magnetic_flux : derived_dimension, exponent> {}; template F, DimensionOf I> -struct dim_inductance : derived_dimension, exp> {}; +struct dim_inductance : derived_dimension, exponent> {}; template R> -struct dim_conductance : derived_dimension> {}; +struct dim_conductance : derived_dimension> {}; // TODO Add when downcasting issue is solved // template T> -// struct dim_radioactivity : derived_dimension> {}; +// struct dim_radioactivity : derived_dimension> {}; template T, DimensionOf M> -struct dim_catalytic_activity : derived_dimension, exp> {}; +struct dim_catalytic_activity : derived_dimension, exponent> {}; template E, DimensionOf M> -struct dim_absorbed_dose : derived_dimension, exp> {}; +struct dim_absorbed_dose : derived_dimension, exponent> {}; template I, DimensionOf L> -struct dim_current_density : derived_dimension, exp> {}; +struct dim_current_density : derived_dimension, exponent> {}; template M, DimensionOf L> -struct dim_concentration : derived_dimension, exp> {}; +struct dim_concentration : derived_dimension, exponent> {}; template I, DimensionOf L> -struct dim_luminance : derived_dimension, exp> {}; +struct dim_luminance : derived_dimension, exponent> {}; template P, DimensionOf T> -struct dim_dynamic_viscosity : derived_dimension, exp> {}; +struct dim_dynamic_viscosity : derived_dimension, exponent> {}; template E, DimensionOf T> -struct dim_heat_capacity : derived_dimension, exp> {}; +struct dim_heat_capacity : derived_dimension, exponent> {}; template C, DimensionOf M> -struct dim_specific_heat_capacity : derived_dimension, exp> {}; +struct dim_specific_heat_capacity : derived_dimension, exponent> {}; template C, DimensionOf M> -struct dim_molar_heat_capacity : derived_dimension, exp> {}; +struct dim_molar_heat_capacity : derived_dimension, exponent> {}; template P, DimensionOf L, DimensionOf T> -struct dim_thermal_conductivity : derived_dimension, exp, exp> {}; +struct dim_thermal_conductivity : derived_dimension, exponent, exponent> {}; // TODO Add when downcasting issue is solved // template E, DimensionOf L> -// struct dim_energy_density : derived_dimension, exp> {}; +// struct dim_energy_density : derived_dimension, exponent> {}; template V, DimensionOf L> -struct dim_electric_field_strength : derived_dimension, exp> {}; +struct dim_electric_field_strength : derived_dimension, exponent> {}; template Q, DimensionOf L> -struct dim_charge_density : derived_dimension, exp> {}; +struct dim_charge_density : derived_dimension, exponent> {}; template Q, DimensionOf L> -struct dim_surface_charge_density : derived_dimension, exp> {}; +struct dim_surface_charge_density : derived_dimension, exponent> {}; template C, DimensionOf L> -struct dim_permittivity : derived_dimension, exp> {}; +struct dim_permittivity : derived_dimension, exponent> {}; template H, DimensionOf L> -struct dim_permeability : derived_dimension, exp> {}; +struct dim_permeability : derived_dimension, exponent> {}; template E, DimensionOf M> -struct dim_molar_energy : derived_dimension, exp> {}; +struct dim_molar_energy : derived_dimension, exponent> {}; template concept Length = QuantityOf; diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index e9b5fa38..c7336543 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #if COMP_MSVC || COMP_GCC >= 10 @@ -76,7 +77,7 @@ public: template requires detail::safe_convertible - constexpr explicit quantity(const Value& v) : value_{static_cast(v)} {} + constexpr explicit(!(std::is_same_v && std::is_same_v)) quantity(const Value& v) : value_{static_cast(v)} {} template requires equivalent_dim && @@ -220,6 +221,123 @@ public: // Hidden Friends // Below friend functions are to be found via argument-dependent lookup only + + [[nodiscard]] friend constexpr quantity operator+(const quantity& lhs, const quantity& rhs) + requires std::regular_invocable, Rep, Rep> + { + return quantity(lhs.count() + rhs.count()); + } + + template + [[nodiscard]] friend constexpr Quantity AUTO operator+(const quantity& lhs, const quantity& rhs) + requires std::regular_invocable, Rep, Rep2> + { + using common_rep = decltype(lhs.count() + rhs.count()); + using ret = common_quantity, common_rep>; + return ret(ret(lhs).count() + ret(rhs).count()); + } + + [[nodiscard]] friend constexpr quantity operator-(const quantity& lhs, const quantity& rhs) + requires std::regular_invocable, Rep, Rep> + { + return quantity(lhs.count() - rhs.count()); + } + + template + [[nodiscard]] friend constexpr Quantity AUTO operator-(const quantity& lhs, const quantity& rhs) + requires std::regular_invocable, Rep, Rep2> + { + using common_rep = decltype(lhs.count() - rhs.count()); + using ret = common_quantity, common_rep>; + return ret(ret(lhs).count() - ret(rhs).count()); + } + + template + [[nodiscard]] friend constexpr Quantity AUTO operator*(const quantity& q, const Value& v) + requires std::regular_invocable, Rep, Value> + { + using common_rep = decltype(q.count() * v); + using ret = quantity; + return ret(q.count() * v); + } + + template + [[nodiscard]] friend constexpr Quantity AUTO operator*(const Value& v, const quantity& q) + requires std::regular_invocable, Value, Rep> + { + return q * v; + } + + template + [[nodiscard]] friend constexpr Quantity AUTO operator*(const quantity& lhs, const quantity& rhs) + requires std::regular_invocable, Rep, Rep2> + { + using dim = dimension_multiply; + using ret_unit = downcast_unit::ratio) * (U2::ratio / dimension_unit::ratio) * dimension_unit::ratio>; + using common_rep = decltype(lhs.count() * rhs.count()); + using ret = quantity; + return ret(lhs.count() * rhs.count()); + } + + template + [[nodiscard]] friend constexpr Quantity AUTO operator/(const Value& v, const quantity& q) + requires std::regular_invocable, Value, Rep> + { + Expects(q.count() != 0); + + using dim = dim_invert; + using ret_unit = downcast_unit; + using common_rep = decltype(v / q.count()); + using ret = quantity; + return ret(v / q.count()); + } + + template + [[nodiscard]] friend constexpr Quantity AUTO operator/(const quantity& q, const Value& v) + requires std::regular_invocable, Rep, Value> + { + Expects(v != Value{0}); + + using common_rep = decltype(q.count() / v); + using ret = quantity; + return ret(q.count() / v); + } + + template + [[nodiscard]] friend constexpr Quantity AUTO operator/(const quantity& lhs, const quantity& rhs) + requires std::regular_invocable, Rep, Rep2> + { + Expects(rhs.count() != 0); + + using common_rep = decltype(lhs.count() / rhs.count()); + using dim = dimension_divide; + using ret_unit = downcast_unit::ratio) / (U2::ratio / dimension_unit::ratio) * dimension_unit::ratio>; + using ret = quantity; + return ret(lhs.count() / rhs.count()); + } + + template + [[nodiscard]] friend constexpr Quantity AUTO operator%(const quantity& q, const Value& v) + requires (!treat_as_floating_point) && + (!treat_as_floating_point) && + std::regular_invocable, Rep, Value> + { + using common_rep = decltype(q.count() % v); + using ret = quantity; + return ret(q.count() % v); + } + + template + [[nodiscard]] friend constexpr Quantity AUTO operator%(const quantity& lhs, const quantity& rhs) + requires (!treat_as_floating_point) && + (!treat_as_floating_point) && + std::regular_invocable, Rep, Rep2> + { + using common_rep = decltype(lhs.count() % rhs.count()); + using ret = common_quantity, common_rep>; + return ret(ret(lhs).count() % ret(rhs).count()); + } + #if COMP_MSVC || COMP_GCC >= 10 template @@ -301,136 +419,6 @@ public: } }; -template -[[nodiscard]] constexpr Quantity AUTO operator+(const quantity& lhs, const quantity& rhs) - requires std::regular_invocable, Rep1, Rep2> -{ - using common_rep = decltype(lhs.count() + rhs.count()); - using ret = common_quantity, quantity, common_rep>; - return ret(ret(lhs).count() + ret(rhs).count()); -} - -template -[[nodiscard]] constexpr Quantity AUTO operator-(const quantity& lhs, const quantity& rhs) - requires std::regular_invocable, Rep1, Rep2> -{ - using common_rep = decltype(lhs.count() - rhs.count()); - using ret = common_quantity, quantity, common_rep>; - return ret(ret(lhs).count() - ret(rhs).count()); -} - -template -[[nodiscard]] constexpr Quantity AUTO operator*(const quantity& q, const Value& v) - requires std::regular_invocable, Rep, Value> -{ - using common_rep = decltype(q.count() * v); - using ret = quantity; - return ret(q.count() * v); -} - -template -[[nodiscard]] constexpr Quantity AUTO operator*(const Value& v, const quantity& q) - requires std::regular_invocable, Value, Rep> -{ - return q * v; -} - -template -[[nodiscard]] constexpr Scalar AUTO operator*(const quantity& lhs, const quantity& rhs) - requires std::regular_invocable, Rep1, Rep2> && - equivalent_dim> -{ - using common_rep = decltype(lhs.count() * rhs.count()); - const ratio r = U1::ratio * U2::ratio; - if constexpr (treat_as_floating_point) { - return lhs.count() * rhs.count() * static_cast(r.num * detail::fpow10(r.exp)) / static_cast(r.den); - } else { - return lhs.count() * rhs.count() * static_cast(r.num * detail::ipow10(r.exp)) / static_cast(r.den); - } -} - -template -[[nodiscard]] constexpr Quantity AUTO operator*(const quantity& lhs, const quantity& rhs) - requires std::regular_invocable, Rep1, Rep2> -{ - using dim = dimension_multiply; - using unit = downcast_unit::ratio) * (U2::ratio / dimension_unit::ratio) * dimension_unit::ratio>; - using common_rep = decltype(lhs.count() * rhs.count()); - using ret = quantity; - return ret(lhs.count() * rhs.count()); -} - -template -[[nodiscard]] constexpr Quantity AUTO operator/(const Value& v, const quantity& q) - requires std::regular_invocable, Value, Rep> -{ - Expects(q.count() != 0); - - using dim = dim_invert; - using unit = downcast_unit; - using common_rep = decltype(v / q.count()); - using ret = quantity; - return ret(v / q.count()); -} - -template -[[nodiscard]] constexpr Quantity AUTO operator/(const quantity& q, const Value& v) - requires std::regular_invocable, Rep, Value> -{ - Expects(v != Value{0}); - - using common_rep = decltype(q.count() / v); - using ret = quantity; - return ret(q.count() / v); -} - -template -[[nodiscard]] constexpr Scalar AUTO operator/(const quantity& lhs, const quantity& rhs) - requires std::regular_invocable, Rep1, Rep2> && - equivalent_dim -{ - Expects(rhs.count() != 0); - - using common_rep = decltype(lhs.count() / rhs.count()); - using cq = common_quantity, quantity, common_rep>; - return cq(lhs).count() / cq(rhs).count(); -} - -template -[[nodiscard]] constexpr Quantity AUTO operator/(const quantity& lhs, const quantity& rhs) - requires std::regular_invocable, Rep1, Rep2> -{ - Expects(rhs.count() != 0); - - using common_rep = decltype(lhs.count() / rhs.count()); - using dim = dimension_divide; - using unit = downcast_unit::ratio) / (U2::ratio / dimension_unit::ratio) * dimension_unit::ratio>; - using ret = quantity; - return ret(lhs.count() / rhs.count()); -} - -template -[[nodiscard]] constexpr Quantity AUTO operator%(const quantity& q, const Value& v) - requires (!treat_as_floating_point) && - (!treat_as_floating_point) && - std::regular_invocable, Rep, Value> -{ - using common_rep = decltype(q.count() % v); - using ret = quantity; - return ret(q.count() % v); -} - -template -[[nodiscard]] constexpr Quantity AUTO operator%(const quantity& lhs, const quantity& rhs) - requires (!treat_as_floating_point) && - (!treat_as_floating_point) && - std::regular_invocable, Rep1, Rep2> -{ - using common_rep = decltype(lhs.count() % rhs.count()); - using ret = common_quantity, quantity, common_rep>; - return ret(ret(lhs).count() % ret(rhs).count()); -} - namespace detail { template diff --git a/src/include/units/quantity_cast.h b/src/include/units/quantity_cast.h index f9e23f4d..2f51af6b 100644 --- a/src/include/units/quantity_cast.h +++ b/src/include/units/quantity_cast.h @@ -27,8 +27,6 @@ #include #include #include -#include -#include #include #ifdef _MSC_VER @@ -38,6 +36,12 @@ namespace units { +template U, Scalar Rep> +class quantity; + +template U, Scalar Rep> +class quantity_point; + namespace detail { template @@ -284,8 +288,8 @@ struct quantity_cast_impl { template constexpr ratio cast_ratio(const Q1& from, const Q2& to) { - using FromU = Q1::unit; - using ToU = Q2::unit; + using FromU = TYPENAME Q1::unit; + using ToU = TYPENAME Q2::unit; if constexpr(same_unit_reference::value) { return FromU::ratio / ToU::ratio; } diff --git a/test/metabench/make_dimension/concepts_all.cpp.erb b/test/metabench/make_dimension/concepts_all.cpp.erb index b37b1dc7..cb987f93 100644 --- a/test/metabench/make_dimension/concepts_all.cpp.erb +++ b/test/metabench/make_dimension/concepts_all.cpp.erb @@ -8,7 +8,7 @@ struct test<%= k %> { #if defined(METABENCH) using dim = units::make_dimension_t<<%= - xs = ((1)..(n)).map { |j| "units::exp" } + xs = ((1)..(n)).map { |j| "units::exponent" } rng = Random.new(k) xs.shuffle(random: rng).join(', ') %>>; diff --git a/test/metabench/make_dimension/concepts_iface.cpp.erb b/test/metabench/make_dimension/concepts_iface.cpp.erb index 0d8cff6b..2e63f485 100644 --- a/test/metabench/make_dimension/concepts_iface.cpp.erb +++ b/test/metabench/make_dimension/concepts_iface.cpp.erb @@ -8,7 +8,7 @@ struct test<%= k %> { #if defined(METABENCH) using dim = units::make_dimension_t<<%= - xs = ((1)..(n)).map { |j| "units::exp" } + xs = ((1)..(n)).map { |j| "units::exponent" } rng = Random.new(k) xs.shuffle(random: rng).join(', ') %>>; diff --git a/test/metabench/make_dimension/dimension_concepts_all.h b/test/metabench/make_dimension/dimension_concepts_all.h index 8278f228..e23162e9 100644 --- a/test/metabench/make_dimension/dimension_concepts_all.h +++ b/test/metabench/make_dimension/dimension_concepts_all.h @@ -60,26 +60,26 @@ namespace units { struct base_dimension_less : std::bool_constant { }; - // exp + // exponent template - struct exp { + struct exponent { static constexpr const base_dimension& dimension = BaseDimension; static constexpr std::intmax_t num = Num; static constexpr std::intmax_t den = Den; }; - // is_exp + // is_exponent namespace detail { template - inline constexpr bool is_exp = false; + inline constexpr bool is_exponent = false; template - inline constexpr bool is_exp> = true; + inline constexpr bool is_exponent> = true; } // namespace detail template - concept Exponent = detail::is_exp; + concept Exponent = detail::is_exponent; // exp_dim_id_less @@ -93,8 +93,8 @@ namespace units { struct exp_invert; template - struct exp_invert> { - using type = exp; + struct exp_invert> { + using type = exponent; }; template @@ -161,12 +161,12 @@ namespace units { }; template - struct dim_consolidate, exp, ERest...>> { + struct dim_consolidate, exponent, ERest...>> { using r1 = std::ratio; using r2 = std::ratio; using r = std::ratio_add; using type = conditional>, - dim_consolidate_t, ERest...>>>; + dim_consolidate_t, ERest...>>>; }; } // namespace detail diff --git a/test/metabench/make_dimension/dimension_concepts_iface.h b/test/metabench/make_dimension/dimension_concepts_iface.h index 069c7458..c7cc4dac 100644 --- a/test/metabench/make_dimension/dimension_concepts_iface.h +++ b/test/metabench/make_dimension/dimension_concepts_iface.h @@ -60,26 +60,26 @@ namespace units { struct base_dimension_less : std::bool_constant { }; - // exp + // exponent template - struct exp { + struct exponent { static constexpr const base_dimension& dimension = BaseDimension; static constexpr int num = Num; static constexpr int den = Den; }; - // is_exp + // is_exponent namespace detail { template - inline constexpr bool is_exp = false; + inline constexpr bool is_exponent = false; template - inline constexpr bool is_exp> = true; + inline constexpr bool is_exponent> = true; } // namespace detail template - concept Exponent = detail::is_exp; + concept Exponent = detail::is_exponent; // exp_dim_id_less @@ -93,8 +93,8 @@ namespace units { struct exp_invert; template - struct exp_invert> { - using type = exp; + struct exp_invert> { + using type = exponent; }; template @@ -161,12 +161,12 @@ namespace units { }; template - struct dim_consolidate, exp, ERest...>> { + struct dim_consolidate, exponent, ERest...>> { using r1 = std::ratio; using r2 = std::ratio; using r = std::ratio_add; using type = conditional>, - dim_consolidate_t, ERest...>>>; + dim_consolidate_t, ERest...>>>; }; } // namespace detail diff --git a/test/metabench/make_dimension/dimension_no_concepts.h b/test/metabench/make_dimension/dimension_no_concepts.h index 569a4ba1..da073eed 100644 --- a/test/metabench/make_dimension/dimension_no_concepts.h +++ b/test/metabench/make_dimension/dimension_no_concepts.h @@ -60,10 +60,10 @@ namespace units { struct base_dimension_less : std::bool_constant { }; - // exp + // exponent template - struct exp { + struct exponent { static constexpr const base_dimension& dimension = BaseDimension; static constexpr std::intmax_t num = Num; static constexpr std::intmax_t den = Den; @@ -81,8 +81,8 @@ namespace units { struct exp_invert; template - struct exp_invert> { - using type = exp; + struct exp_invert> { + using type = exponent; }; template @@ -132,12 +132,12 @@ namespace units { }; template - struct dim_consolidate, exp, ERest...>> { + struct dim_consolidate, exponent, ERest...>> { using r1 = std::ratio; using r2 = std::ratio; using r = std::ratio_add; using type = conditional>, - dim_consolidate_t, ERest...>>>; + dim_consolidate_t, ERest...>>>; }; } // namespace detail diff --git a/test/metabench/make_dimension/no_concepts.cpp.erb b/test/metabench/make_dimension/no_concepts.cpp.erb index 68b98188..4f010f0d 100644 --- a/test/metabench/make_dimension/no_concepts.cpp.erb +++ b/test/metabench/make_dimension/no_concepts.cpp.erb @@ -8,7 +8,7 @@ struct test<%= k %> { #if defined(METABENCH) using dim = units::make_dimension_t<<%= - xs = ((1)..(n)).map { |j| "units::exp" } + xs = ((1)..(n)).map { |j| "units::exponent" } rng = Random.new(k) xs.shuffle(random: rng).join(', ') %>>; diff --git a/test/unit_test/runtime/fmt_test.cpp b/test/unit_test/runtime/fmt_test.cpp index 131105e6..f03cc87c 100644 --- a/test/unit_test/runtime/fmt_test.cpp +++ b/test/unit_test/runtime/fmt_test.cpp @@ -421,6 +421,72 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") } } + SECTION("dimensionless quantity") + { + SECTION("unitless with ratio == 1") + { + const auto q = 4q_m / 2q_m; + os << q; + + SECTION("iostream") + { + CHECK(os.str() == "2"); + } + + SECTION("fmt with default format {} on a quantity") + { + CHECK(fmt::format("{}", q) == os.str()); + } + + SECTION("fmt with format {:%Q %q} on a quantity") + { + CHECK(fmt::format("{:%Q %q}", q) == "2 "); + } + } + + SECTION("unitless with ratio.exp != 0") + { + const auto q = 4q_km / 2q_m; + os << q; + + SECTION("iostream") + { + CHECK(os.str() == "2 × 10³"); + } + + SECTION("fmt with default format {} on a quantity") + { + CHECK(fmt::format("{}", q) == os.str()); + } + + SECTION("fmt with format {:%Q %q} on a quantity") + { + CHECK(fmt::format("{:%Q %q}", q) == "2 × 10³"); + } + } + + SECTION("percents") + { + const auto q = quantity_cast(15.q_m / 100.q_m); + os << q; + + SECTION("iostream") + { + CHECK(os.str() == "15 %"); + } + + SECTION("fmt with default format {} on a quantity") + { + CHECK(fmt::format("{}", q) == os.str()); + } + + SECTION("fmt with format {:%Q %q} on a quantity") + { + CHECK(fmt::format("{:%Q %q}", q) == os.str()); + } + } + } + SECTION("quantity with an unkown dimension") { SECTION("unit::ratio::num == 1 && unit::ratio::den == 1") @@ -573,7 +639,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") } } - SECTION("exp::num == 1 && exp::den == 1") + SECTION("exponent::num == 1 && exponent::den == 1") { const auto q = 4q_m * 2q_s; os << q; @@ -594,7 +660,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") } } - SECTION("exp::num == 2 && exp::den == 1 for positive exponent") + SECTION("exponent::num == 2 && exponent::den == 1 for positive exponent") { const auto q = 4q_m * 2q_s * 2q_s; os << q; @@ -615,7 +681,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") } } - SECTION("exp::num == 2 && exp::den == 1 for negative exponent (first dimension)") + SECTION("exponent::num == 2 && exponent::den == 1 for negative exponent (first dimension)") { const auto q = 8q_s / 2q_m / 2q_m; os << q; @@ -636,7 +702,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") } } - SECTION("exp::num == 2 && exp::den == 1 for negative exponent (not first dimension)") + SECTION("exponent::num == 2 && exponent::den == 1 for negative exponent (not first dimension)") { const auto q = 8q_m / 2q_kg / 2q_kg; os << q; diff --git a/test/unit_test/runtime/math_test.cpp b/test/unit_test/runtime/math_test.cpp index fc33ad3e..6b0c0243 100644 --- a/test/unit_test/runtime/math_test.cpp +++ b/test/unit_test/runtime/math_test.cpp @@ -63,10 +63,12 @@ TEST_CASE("absolute functions on quantity returns the absolute value", "[math][a REQUIRE(abs(-1q_m) == 1q_m); } +#ifndef COMP_MSVC SECTION ("floating-point representation") { REQUIRE(abs(-1.q_m) == 1q_m); } +#endif } SECTION ("'abs()' on a positive quantity returns the abs") @@ -76,10 +78,12 @@ TEST_CASE("absolute functions on quantity returns the absolute value", "[math][a REQUIRE(abs(1q_m) == 1q_m); } +#ifndef COMP_MSVC SECTION ("floating-point representation") { REQUIRE(abs(1.q_m) == 1q_m); } +#endif } } diff --git a/test/unit_test/static/custom_unit_test.cpp b/test/unit_test/static/custom_unit_test.cpp index 5f773cb2..07ebc27f 100644 --- a/test/unit_test/static/custom_unit_test.cpp +++ b/test/unit_test/static/custom_unit_test.cpp @@ -33,14 +33,14 @@ using namespace units::physical::si; // power spectral density struct sq_volt_per_hertz : unit {}; -struct dim_power_spectral_density : derived_dimension, units::exp> {}; +struct dim_power_spectral_density : derived_dimension, units::exponent> {}; template using power_spectral_density = quantity; // amplitude spectral density struct volt_per_sqrt_hertz : unit {}; -struct dim_amplitude_spectral_density : derived_dimension, units::exp> {}; +struct dim_amplitude_spectral_density : derived_dimension, units::exponent> {}; template using amplitude_spectral_density = quantity; @@ -60,7 +60,7 @@ static_assert(is_same_v( namespace { struct kilogram_per_second : unit {}; -struct dim_mass_rate : derived_dimension, units::exp> {}; +struct dim_mass_rate : derived_dimension, units::exponent> {}; struct kilogram_per_hour : deduced_unit {}; constexpr auto a = 1q_kg / 1q_h; static_assert(is_same_v); diff --git a/test/unit_test/static/dimension_op_test.cpp b/test/unit_test/static/dimension_op_test.cpp index dff74f2c..94a27c0a 100644 --- a/test/unit_test/static/dimension_op_test.cpp +++ b/test/unit_test/static/dimension_op_test.cpp @@ -39,8 +39,8 @@ struct d3 : base_dimension<"d3", u3> {}; // exp_invert -static_assert(is_same_v>, units::exp>); -static_assert(is_same_v>, units::exp>); +static_assert(is_same_v>, units::exponent>); +static_assert(is_same_v>, units::exponent>); // dim_unpack @@ -54,66 +54,66 @@ template using derived_dim = detail::derived_dimension_base; static_assert(is_same_v, exp_list<>>); -static_assert(is_same_v>, exp_list>>); -static_assert(is_same_v, units::exp>, exp_list, units::exp>>); -using dim1 = derived_dim>; -using dim2 = derived_dim, units::exp>; -static_assert(is_same_v, units::exp>, exp_list, units::exp>>); -static_assert(is_same_v, units::exp, units::exp>, - exp_list, units::exp, units::exp, units::exp>>); +static_assert(is_same_v>, exp_list>>); +static_assert(is_same_v, units::exponent>, exp_list, units::exponent>>); +using dim1 = derived_dim>; +using dim2 = derived_dim, units::exponent>; +static_assert(is_same_v, units::exponent>, exp_list, units::exponent>>); +static_assert(is_same_v, units::exponent, units::exponent>, + exp_list, units::exponent, units::exponent, units::exponent>>); // dim_invert -static_assert(is_same_v>>, d0>); -static_assert(is_same_v>>, unknown_dimension>>); +static_assert(is_same_v>>, d0>); +static_assert(is_same_v>>, unknown_dimension>>); static_assert( - is_same_v, units::exp>>, unknown_dimension, units::exp>>); + is_same_v, units::exponent>>, unknown_dimension, units::exponent>>); // make_dimension template using make_dimension = detail::make_dimension; -static_assert(is_same_v>, derived_dim>>); -static_assert(is_same_v, units::exp>, derived_dim, units::exp>>); -static_assert(is_same_v, units::exp>, derived_dim, units::exp>>); -static_assert(is_same_v, units::exp>, derived_dim>>); -static_assert(is_same_v, units::exp>, derived_dim>>); -static_assert(is_same_v, units::exp>, derived_dim>>); -static_assert(is_same_v, units::exp>, derived_dim>>); +static_assert(is_same_v>, derived_dim>>); +static_assert(is_same_v, units::exponent>, derived_dim, units::exponent>>); +static_assert(is_same_v, units::exponent>, derived_dim, units::exponent>>); +static_assert(is_same_v, units::exponent>, derived_dim>>); +static_assert(is_same_v, units::exponent>, derived_dim>>); +static_assert(is_same_v, units::exponent>, derived_dim>>); +static_assert(is_same_v, units::exponent>, derived_dim>>); -static_assert(is_same_v, units::exp, units::exp, units::exp>, - derived_dim, units::exp>>); -static_assert(is_same_v, units::exp, units::exp, units::exp>, - derived_dim, units::exp>>); +static_assert(is_same_v, units::exponent, units::exponent, units::exponent>, + derived_dim, units::exponent>>); +static_assert(is_same_v, units::exponent, units::exponent, units::exponent>, + derived_dim, units::exponent>>); -static_assert(is_same_v, units::exp, units::exp>, derived_dim>>); -static_assert(is_same_v, units::exp, units::exp>, derived_dim>>); -static_assert(is_same_v, units::exp, units::exp>, derived_dim>>); +static_assert(is_same_v, units::exponent, units::exponent>, derived_dim>>); +static_assert(is_same_v, units::exponent, units::exponent>, derived_dim>>); +static_assert(is_same_v, units::exponent, units::exponent>, derived_dim>>); // dimension_multiply -static_assert(is_same_v>, derived_dim>>, - unknown_dimension, units::exp>>); +static_assert(is_same_v>, derived_dim>>, + unknown_dimension, units::exponent>>); static_assert( - is_same_v>, d1>, unknown_dimension, units::exp>>); + is_same_v>, d1>, unknown_dimension, units::exponent>>); static_assert( - is_same_v>>, unknown_dimension, units::exp>>); -static_assert(is_same_v, unknown_dimension, units::exp>>); + is_same_v>>, unknown_dimension, units::exponent>>); +static_assert(is_same_v, unknown_dimension, units::exponent>>); static_assert(is_same_v< - dimension_multiply, units::exp, units::exp>, derived_dim>>, - unknown_dimension, units::exp, units::exp, units::exp>>); + dimension_multiply, units::exponent, units::exponent>, derived_dim>>, + unknown_dimension, units::exponent, units::exponent, units::exponent>>); static_assert(is_same_v< - dimension_multiply, units::exp, units::exp>, derived_dim>>, - unknown_dimension, units::exp, units::exp>>); + dimension_multiply, units::exponent, units::exponent>, derived_dim>>, + unknown_dimension, units::exponent, units::exponent>>); static_assert(is_same_v< - dimension_multiply, units::exp, units::exp>, derived_dim>>, - unknown_dimension, units::exp>>); -static_assert(is_same_v>, derived_dim>>, d0>); + dimension_multiply, units::exponent, units::exponent>, derived_dim>>, + unknown_dimension, units::exponent>>); +static_assert(is_same_v>, derived_dim>>, d0>); // dimension_divide -static_assert(is_same_v>, derived_dim>>, - unknown_dimension, units::exp>>); -static_assert(is_same_v>, unknown_dimension>>, d0>); +static_assert(is_same_v>, derived_dim>>, + unknown_dimension, units::exponent>>); +static_assert(is_same_v>, unknown_dimension>>, d0>); } // namespace diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index eaf4832a..164bf533 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -161,21 +161,21 @@ static_assert( static_assert( is_same_v() * physical::si::time()), length, int>>); static_assert(is_same_v() * physical::si::time()), - quantity, units::exp>, scaled_unit>>); + quantity, units::exponent>, scaled_unit>>); static_assert(is_same_v()), frequency>); static_assert(is_same_v()), frequency, int>>); static_assert(is_same_v()), physical::si::time>); static_assert(is_same_v()), - quantity>, scaled_unit>>); + quantity>, scaled_unit>>); static_assert(is_same_v() / 1.0), length>); -static_assert(is_same_v() / length()), double>); -static_assert(is_same_v() / length()), double>); +static_assert(is_same_v() / length()), dimensionless>); +static_assert(is_same_v() / length()), dimensionless, double>>); static_assert( is_same_v() / physical::si::time()), speed>); static_assert( is_same_v() / physical::si::time()), speed>>); static_assert(is_same_v() / length()), - quantity, units::exp>, scaled_unit>>); + quantity, units::exponent>, scaled_unit>>); static_assert(is_same_v() % short(1)), length>); static_assert(is_same_v() % length(1)), length>); @@ -186,14 +186,27 @@ static_assert((1q_km - 1q_m).count() == 999); static_assert((2q_m * 2).count() == 4); static_assert((3 * 3q_m).count() == 9); static_assert((4q_m / 2).count() == 2); -static_assert(4q_m / 2q_m == 2); -static_assert(4q_km / 2000q_m == 2); +static_assert((4q_km / 2q_m).count() == 2); +static_assert((4000q_m / 2q_m).count() == 2000); static_assert((7q_m % 2).count() == 1); static_assert((7q_m % 2q_m).count() == 1); static_assert((7q_km % 2000q_m).count() == 1000); static_assert((10q_km2 * 10q_km2) / 50q_km2 == 2q_km2); +constexpr auto q1 = 10q_km / 5q_m; +static_assert(std::is_same_v, std::int64_t>>); +static_assert(q1.count() == 2); + +constexpr dimensionless q2 = q1; +static_assert(q2.count() == 2000); + +static_assert(quantity_cast(q1).count() == 2000); + +constexpr auto q3 = 10q_s * 2q_kHz; +static_assert(std::is_same_v, std::int64_t>>); +static_assert(q3.count() == 20); + // comparators static_assert(2q_m + 1q_m == 3q_m); @@ -264,6 +277,37 @@ static_assert(quantity_cast(2q_km).count() == 2000); static_assert(quantity_cast(2000q_m).count() == 2); static_assert(quantity_cast(1.23q_m).count() == 1); +// dimensionless + +static_assert(std::is_convertible_v>); +static_assert(std::is_convertible_v>); +static_assert(!std::is_convertible_v>); +static_assert(std::is_convertible_v>); + +static_assert(!std::is_convertible_v>>); +static_assert(std::is_constructible_v>, double>); + +static_assert(dimensionless(1.23) + dimensionless(1.23) == dimensionless(2.46)); +static_assert(dimensionless(1.23) + dimensionless(1.23) == 2.46); +static_assert(dimensionless(1.23) + 1.23 == 2.46); +static_assert(1.23 + dimensionless(1.23) == 2.46); +static_assert(dimensionless(1) + 1 == 2); +static_assert(dimensionless(1) + 1 == 2); + +template +concept invalid_dimensionless_operation = requires() +{ + !requires(dimensionless d) { d + 1.23; }; + !requires(dimensionless d) { 1.23 + d; }; + !requires(dimensionless, Rep> d) { 1 + d; }; + !requires(dimensionless, Rep> d) { d + 1; }; +}; +static_assert(invalid_dimensionless_operation); + +static_assert(quantity_cast(50.q_m / 100.q_m).count() == 50); +static_assert(50.q_m / 100.q_m == dimensionless(50)); + + // time static_assert(1q_h == 3600q_s); diff --git a/test/unit_test/static/si_test.cpp b/test/unit_test/static/si_test.cpp index a1b339f0..ae4a00e3 100644 --- a/test/unit_test/static/si_test.cpp +++ b/test/unit_test/static/si_test.cpp @@ -40,7 +40,9 @@ static_assert(1q_hm == 100q_m); static_assert(1q_au == 149'597'870'700q_m); static_assert(1q_km + 1q_m == 1001q_m); static_assert(10q_km / 5q_km == 2); -static_assert(100q_mm / 5q_cm == 2); +static_assert(10q_km / 5q_km < 3); +static_assert(100q_mm / 5q_cm == dimensionless>(20)); +static_assert(100q_mm / 5q_cm == dimensionless(2)); static_assert(10q_km / 2 == 5q_km); static_assert(millimetre::symbol == "mm"); @@ -102,7 +104,8 @@ static_assert(120 / 1q_min == 2q_Hz); static_assert(1000 / 1q_s == 1q_kHz); static_assert(1 / 1q_ms == 1q_kHz); static_assert(3.2q_GHz == 3'200'000'000q_Hz); -static_assert(10q_Hz * 1q_min == 600); +static_assert(10q_Hz * 1q_min == dimensionless>(10)); +static_assert(10q_Hz * 1q_min == dimensionless(600)); static_assert(2 / 1q_Hz == 2q_s); // force diff --git a/test/unit_test/static/type_list_test.cpp b/test/unit_test/static/type_list_test.cpp index d1afb67e..6f8cf8d4 100644 --- a/test/unit_test/static/type_list_test.cpp +++ b/test/unit_test/static/type_list_test.cpp @@ -98,20 +98,20 @@ struct d0 : base_dimension<"d0", u0> {}; struct u1 : named_unit {}; struct d1 : base_dimension<"d1", u1> {}; -static_assert(is_same_v>, type_list>, exp_less>, - type_list, units::exp>>); -static_assert(is_same_v>, type_list>, exp_less>, - type_list, units::exp>>); +static_assert(is_same_v>, type_list>, exp_less>, + type_list, units::exponent>>); +static_assert(is_same_v>, type_list>, exp_less>, + type_list, units::exponent>>); // type_list_sort template using exp_sort = type_list_sort; -static_assert(is_same_v>>, exp_list>>); +static_assert(is_same_v>>, exp_list>>); static_assert( - is_same_v, units::exp>>, exp_list, units::exp>>); + is_same_v, units::exponent>>, exp_list, units::exponent>>); static_assert( - is_same_v, units::exp>>, exp_list, units::exp>>); + is_same_v, units::exponent>>, exp_list, units::exponent>>); } // namespace diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index b0c90abf..43d1d473 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -46,7 +46,7 @@ static_assert([](P) { return !requires { typename prefixed_unit {}; -struct dim_speed : derived_dimension, units::exp> {}; +struct dim_speed : derived_dimension, units::exponent> {}; struct kilometre_per_hour : deduced_unit {}; static_assert(is_same_v>, metre>);