From b3aa40d95c95e4ced5247bfe73333773be40d7bb Mon Sep 17 00:00:00 2001 From: Nic Holthaus Date: Fri, 1 Mar 2024 12:00:02 -0500 Subject: [PATCH 01/48] - fix dependent typename warning - upgrade cmake min version to 3.16 --- CMakeLists.txt | 2 +- include/units/core.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 77af1f61..6c1bc6b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9 FATAL_ERROR) +cmake_minimum_required(VERSION 3.16 FATAL_ERROR) PROJECT(units VERSION 3.0.0 LANGUAGES CXX) diff --git a/include/units/core.h b/include/units/core.h index c1cdaed0..b9e7dfd6 100644 --- a/include/units/core.h +++ b/include/units/core.h @@ -1805,7 +1805,7 @@ namespace units normal_convert(static_cast(value) / static_cast(pow(detail::PI_VAL, -PiRatio::num / PiRatio::den)))); } // non-constexpr pi in numerator. This case (only) isn't actually constexpr. - else if constexpr ((PiRatio::num / PiRatio::den) < 1 && (PiRatio::num / PiRatio::den) > -1) + else if constexpr ((PiRatio::num / PiRatio::template den) < 1 && (PiRatio::num / PiRatio::den) > -1) { return static_cast(normal_convert( static_cast(value) * static_cast(std::pow(detail::PI_VAL, PiRatio::num / PiRatio::den)))); From 66f4c70ee5d1555d2885b25693aa020f0b21ad6b Mon Sep 17 00:00:00 2001 From: Nic Holthaus Date: Fri, 6 Dec 2024 17:33:50 -0500 Subject: [PATCH 02/48] changes: - update version to 3.1.0 - Supports CTAD finally! So you can delcare `meters` instead of `meters` - Update to C++20 for CTAD support - Replace SFINAE with concepts - fix warning on gcc-13 --- CMakeLists.txt | 4 +- include/units.h | 2 +- include/units/core.h | 161 ++++++++++++++++++++++++++----------------- unitTests/main.cpp | 69 ++++++++++++------- 4 files changed, 145 insertions(+), 91 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c1bc6b0..8c7440b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16 FATAL_ERROR) -PROJECT(units VERSION 3.0.0 LANGUAGES CXX) +PROJECT(units VERSION 3.1.0 LANGUAGES CXX) # check if this is the main project set(MAIN_PROJECT OFF) @@ -14,7 +14,7 @@ OPTION(UNITS_BUILD_DOCS "Build the documentation" OFF) OPTION(UNITS_DISABLE_IOSTREAM "Disables (cout) support for embedded applications" OFF) option(UNITS_CODE_COVERAGE "Generate coveralls code coverage data" OFF) -SET(CMAKE_CXX_STANDARD 17) +SET(CMAKE_CXX_STANDARD 20) # header-only library target. To use this project as a subdirectory, # add the following to your code: diff --git a/include/units.h b/include/units.h index a310d650..d292d693 100644 --- a/include/units.h +++ b/include/units.h @@ -91,7 +91,7 @@ namespace units { namespace detail { - struct PI : conversion_factor, dimensionless_unit, std::ratio<1>> + struct PI : conversion_factor, dimensionless_, std::ratio<1>> { }; } // namespace detail diff --git a/include/units/core.h b/include/units/core.h index b9e7dfd6..f32c95b8 100644 --- a/include/units/core.h +++ b/include/units/core.h @@ -66,6 +66,8 @@ #include #include +#include "core.h" + #if !defined(UNIT_LIB_DISABLE_IOSTREAM) #include #include @@ -196,10 +198,9 @@ namespace units * commas to be easily expanded. All the variadic 'arguments' should together * comprise the unit definition. */ -#define UNIT_ADD_SCALED_UNIT_DEFINITION(unitName, scale, /*conversionFactor*/...) \ - template \ +#define UNIT_ADD_SCALED_UNIT_DEFINITION(unitName, scale, /*conversionFactor*/...) \ + template \ using unitName = ::units::unit, Underlying, scale>; - /** * @def UNIT_ADD_NAME(namespaceName,namePlural,abbreviation) * @brief Macro for generating constexpr names/abbreviations for units. @@ -1791,24 +1792,25 @@ namespace units else if constexpr (!std::is_same_v, PiRatio> && std::is_same_v, Translation>) { using CommonUnderlying = std::common_type_t; + constexpr long double PiRatioValue = PiRatio::num / PiRatio::den; // constexpr pi in numerator - if constexpr (PiRatio::num / PiRatio::den >= 1 && PiRatio::num % PiRatio::den == 0) + if constexpr (PiRatioValue >= 1 && PiRatio::num % PiRatio::den == 0) { return static_cast( - normal_convert(static_cast(value) * static_cast(pow(detail::PI_VAL, PiRatio::num / PiRatio::den)))); + normal_convert(static_cast(value) * static_cast(pow(detail::PI_VAL, PiRatioValue)))); } // constexpr pi in denominator - else if constexpr (PiRatio::num / PiRatio::den <= -1 && PiRatio::num % PiRatio::den == 0) + else if constexpr (PiRatioValue <= -1 && PiRatio::num % PiRatio::den == 0) { return static_cast( - normal_convert(static_cast(value) / static_cast(pow(detail::PI_VAL, -PiRatio::num / PiRatio::den)))); + normal_convert(static_cast(value) / static_cast(pow(detail::PI_VAL, -PiRatioValue)))); } // non-constexpr pi in numerator. This case (only) isn't actually constexpr. - else if constexpr ((PiRatio::num / PiRatio::template den) < 1 && (PiRatio::num / PiRatio::den) > -1) + else if constexpr (PiRatioValue < 1 && PiRatio::num / PiRatioValue > -1) { return static_cast(normal_convert( - static_cast(value) * static_cast(std::pow(detail::PI_VAL, PiRatio::num / PiRatio::den)))); + static_cast(value) * static_cast(std::pow(detail::PI_VAL, PiRatioValue)))); } } // Translation required, no pi variable @@ -2166,9 +2168,9 @@ namespace units * @details constructs a new unit with `value`. * @param[in] value unit magnitude. */ - template::value && detail::is_losslessly_convertible, int> = 0> - inline explicit constexpr unit(const Ty value) noexcept : linearized_value(NumericalScale::linearize(static_cast(value))) + template + requires (!traits::is_dimensionless_unit::value && detail::is_losslessly_convertible) + explicit constexpr unit(Ty value) noexcept : linearized_value(NumericalScale::linearize(static_cast(value))) { } @@ -2177,8 +2179,9 @@ namespace units * @details constructs a new unit with `value`. * @param[in] value linearized unit magnitude. */ - template, int> = 0> - inline explicit constexpr unit(const Ty value, linearized_value_t) noexcept : linearized_value(value) + template + requires detail::is_losslessly_convertible + explicit constexpr unit(Ty value, linearized_value_t) noexcept : linearized_value(value) { } @@ -2187,9 +2190,9 @@ namespace units * @details enable implicit conversions from T types ONLY for linear dimensionless units * @param[in] value value of the unit */ - template::value && detail::is_losslessly_convertible, int> = 0> - inline constexpr unit(const Ty value) noexcept : linearized_value(NumericalScale::linearize(static_cast(value))) + template + requires traits::is_dimensionless_unit::value && detail::is_losslessly_convertible + constexpr unit(Ty value) noexcept : linearized_value(NumericalScale::linearize(static_cast(value))) { } @@ -2198,9 +2201,9 @@ namespace units * @details enable implicit conversions from std::chrono::duration types ONLY for time units * @param[in] value value of the unit */ - template && detail::is_losslessly_convertible, int> = 0> - inline constexpr unit(const std::chrono::duration& value) noexcept + template + requires detail::is_time_conversion_factor && detail::is_losslessly_convertible + constexpr unit(const std::chrono::duration& value) noexcept : linearized_value(units::convert(units::unit, Rep>(value.count())).linearized_value) { } @@ -2210,9 +2213,9 @@ namespace units * @details performs implicit unit conversions if required. * @param[in] rhs unit to copy. */ - template, unit>, int> = 0> - inline constexpr unit(const unit& rhs) noexcept : linearized_value(units::convert(rhs).linearized_value) + template + requires detail::is_losslessly_convertible_unit, unit> + constexpr unit(const unit& rhs) noexcept : linearized_value(units::convert(rhs).linearized_value) { } @@ -2228,8 +2231,9 @@ namespace units * @details performs implicit conversions from built-in types ONLY for dimensionless units * @param[in] rhs value to copy. */ - template::value>> - inline constexpr unit& operator=(const underlying_type& rhs) noexcept + template + requires traits::is_dimensionless_unit::value + constexpr unit& operator=(const underlying_type& rhs) noexcept { unit, units::dimension::dimensionless>, underlying_type, linear_scale> dimensionlessRhs(rhs); linearized_value = units::convert(dimensionlessRhs).linearized_value; @@ -2243,7 +2247,7 @@ namespace units * @returns true IFF the value of `this` is less than the value of `rhs` */ template - inline constexpr bool operator<(const unit& rhs) const noexcept + constexpr bool operator<(const unit& rhs) const noexcept { using CommonUnit = std::common_type_t>; return (CommonUnit(*this).linearized_value < CommonUnit(rhs).linearized_value); @@ -2256,7 +2260,7 @@ namespace units * @returns true IFF the value of `this` is less than or equal to the value of `rhs` */ template - inline constexpr bool operator<=(const unit& rhs) const noexcept + constexpr bool operator<=(const unit& rhs) const noexcept { using CommonUnit = std::common_type_t>; return (CommonUnit(*this).linearized_value <= CommonUnit(rhs).linearized_value); @@ -2269,7 +2273,7 @@ namespace units * @returns true IFF the value of `this` is greater than the value of `rhs` */ template - inline constexpr bool operator>(const unit& rhs) const noexcept + constexpr bool operator>(const unit& rhs) const noexcept { using CommonUnit = std::common_type_t>; return (CommonUnit(*this).linearized_value > CommonUnit(rhs).linearized_value); @@ -2282,7 +2286,7 @@ namespace units * @returns true IFF the value of `this` is greater than or equal to the value of `rhs` */ template - inline constexpr bool operator>=(const unit& rhs) const noexcept + constexpr bool operator>=(const unit& rhs) const noexcept { using CommonUnit = std::common_type_t>; return (CommonUnit(*this).linearized_value >= CommonUnit(rhs).linearized_value); @@ -2296,7 +2300,8 @@ namespace units * @note This may not be suitable for all applications when the underlying_type of unit is a double. */ template - inline constexpr std::enable_if_t || std::is_floating_point_v, bool> operator==( + requires std::is_floating_point_v || std::is_floating_point_v + constexpr bool operator==( const unit& rhs) const noexcept { using CommonUnit = std::common_type_t>; @@ -2310,7 +2315,8 @@ namespace units } template - inline constexpr std::enable_if_t::value && std::is_integral::value, bool> operator==( + requires std::is_integral::value && std::is_integral::value + constexpr bool operator==( const unit& rhs) const noexcept { using CommonUnit = std::common_type_t>; @@ -2325,7 +2331,7 @@ namespace units * @note This may not be suitable for all applications when the underlying_type of unit is a double. */ template - inline constexpr bool operator!=(const unit& rhs) const noexcept + constexpr bool operator!=(const unit& rhs) const noexcept { return !(*this == rhs); } @@ -2336,7 +2342,7 @@ namespace units * @returns value of the unit in it's underlying, non-safe type after appliyng the scale. * */ - inline constexpr underlying_type raw() const noexcept + constexpr underlying_type raw() const noexcept { return static_cast(NumericalScale::scale(linearized_value)); } @@ -2348,7 +2354,7 @@ namespace units * @returns value of the unit in it's underlying, non-safe type. * */ - inline constexpr underlying_type value() const noexcept + constexpr underlying_type value() const noexcept { if constexpr (traits::is_dimensionless_unit::value) return NumericalScale::scale(units::convert, units::dimension::dimensionless, @@ -2364,8 +2370,9 @@ namespace units * @brief unit value * @returns value of the unit converted to an arithmetic, non-safe type. */ - template, int> = 0> - inline constexpr Ty to() const noexcept + template + requires std::is_arithmetic_v + constexpr Ty to() const noexcept { return static_cast(*this); } @@ -2374,7 +2381,7 @@ namespace units * @brief linearized unit value * @returns linearized value of unit which has a (possibly) non-linear scale. */ - inline constexpr T to_linearized() const noexcept + constexpr T to_linearized() const noexcept { return linearized_value; } @@ -2390,7 +2397,7 @@ namespace units * *this. */ template - inline constexpr unit convert() const noexcept + constexpr unit convert() const noexcept { static_assert(traits::is_conversion_factor_v, "Template parameter `Cf` must be a conversion factor."); return unit(*this); @@ -2406,8 +2413,9 @@ namespace units * @returns a unit with the specified parameters containing the equivalent value to * *this. */ - template class UnitType, class = std::enable_if_t, unit>>> - inline constexpr UnitType convert() noexcept + template class UnitType> + requires traits::is_same_dimension_unit_v, unit> + constexpr UnitType convert() noexcept { return UnitType(*this); } @@ -2416,13 +2424,14 @@ namespace units * @brief implicit type unsafe conversion. * @details only enabled for dimensionless unit types. */ - template::value && std::is_arithmetic::value, int> = 0> - inline constexpr operator Ty() const noexcept + template + requires traits::is_dimensionless_unit::value && std::is_arithmetic_v + constexpr operator Ty() const noexcept { // this conversion also resolves any PI exponents, by converting from a non-zero PI ratio to a zero-pi // ratio. return NumericalScale::scale( - units::convert, units::dimension::dimensionless>, Ty, NumericalScale>>(*this) + units::convert, dimension::dimensionless>, Ty, NumericalScale>>(*this) .to_linearized()); } @@ -2430,8 +2439,9 @@ namespace units * @brief explicit type unsafe conversion. * @details only enabled for non-dimensionless unit types. */ - template::value && std::is_arithmetic::value, int> = 0> - inline constexpr explicit operator Ty() const noexcept + template + requires (!traits::is_dimensionless_unit::value && std::is_arithmetic_v) + constexpr explicit operator Ty() const noexcept { return static_cast(this->value()); } @@ -2440,9 +2450,9 @@ namespace units * @brief chrono implicit type conversion. * @details only enabled for time unit types. */ - template && detail::is_losslessly_convertible, int> = 0> - inline constexpr operator std::chrono::duration() const noexcept + template + requires detail::is_time_conversion_factor && detail::is_losslessly_convertible + constexpr operator std::chrono::duration() const noexcept { return std::chrono::duration(units::unit, Rep>(*this).value()); } @@ -2451,7 +2461,7 @@ namespace units * @brief returns the unit name */ template - inline constexpr const char* name() const noexcept + [[nodiscard]] constexpr const char* name() const noexcept { return unit_name_v; } @@ -2460,7 +2470,7 @@ namespace units * @brief returns the unit abbreviation */ template - inline constexpr const char* abbreviation() const noexcept + [[nodiscard]] constexpr const char* abbreviation() const noexcept { return unit_abbreviation_v; } @@ -2489,13 +2499,37 @@ namespace units * @tparam T Arithmetic type. * @param[in] value Arithmetic value that represents a quantity in units of `UnitType`. */ - template, int> = 0> + template + requires detail::is_losslessly_convertible constexpr UnitType make_unit(const T value) noexcept { static_assert(traits::is_unit_v, "Template parameter `UnitType` must be a unit type."); return UnitType(value); } + //------------------------------ + // UNIT DEDUCTION GUIDES + //------------------------------ + + // Conversion factor from Target, type from Source + template + requires traits::is_unit_v> + unit(const unit&)-> unit; + + // Matching Target and Source factors + template + requires traits::is_unit_v> + unit(const unit&)-> unit; + + // Deduce from the same unit type + template + unit(const unit&) -> unit; + + // Deduce type from arithmetic type + template + requires std::is_arithmetic_v + unit(T) -> unit; + #if !defined(UNIT_LIB_DISABLE_IOSTREAM) //----------------------------------------- @@ -2590,7 +2624,7 @@ namespace units #endif //------------------------------ - // std::common_type + // std::ratio helpers //------------------------------ /** @cond */ // DOXYGEN IGNORE @@ -2605,6 +2639,10 @@ namespace units /** @endcond */ // END DOXYGEN IGNORE } // end namespace units +//------------------------------ +// std::common_type +//------------------------------ + namespace std { /** @@ -2787,23 +2825,18 @@ namespace units //---------------------------------- // dimensionless units are the *ONLY* units implicitly convertible to/from built-in types. - struct dimensionless_unit : conversion_factor, units::dimension::dimensionless> - { - }; - UNIT_ADD_SCALED_UNIT_DEFINITION(dimensionless, ::units::linear_scale, dimensionless_unit) + using dimensionless_ = conversion_factor, dimension::dimensionless>; namespace traits { template<> - struct strong> + struct strong> { - using type = dimensionless_unit; + using type = conversion_factor, dimension::dimensionless>; }; } // namespace traits - - template - using dimensionless = unit; + UNIT_ADD_SCALED_UNIT_DEFINITION(dimensionless, ::units::linear_scale, conversion_factor, dimension::dimensionless>) UNIT_ADD_DIMENSION_TRAIT(dimensionless) @@ -3392,7 +3425,7 @@ namespace units template struct power_of_unit<0, U> { - typedef units::dimensionless_unit type; + typedef units::dimensionless_ type; }; } // namespace detail /** @endcond */ // END DOXYGEN IGNORE @@ -3457,9 +3490,9 @@ namespace units * @brief dimensionless unit with decibel scale * @sa See unit for more information on unit type containers. */ - UNIT_ADD_SCALED_UNIT_DEFINITION(dB, ::units::decibel_scale, dimensionless_unit) + UNIT_ADD_SCALED_UNIT_DEFINITION(dB, ::units::decibel_scale, dimensionless_) template - using dB = unit; + using dB = unit; #if !defined(UNIT_LIB_DISABLE_IOSTREAM) template std::ostream& operator<<(std::ostream& os, const dB& obj) diff --git a/unitTests/main.cpp b/unitTests/main.cpp index 8e516ecd..a3c4c283 100644 --- a/unitTests/main.cpp +++ b/unitTests/main.cpp @@ -1,3 +1,6 @@ +#define _SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING // officially, The effect of instantiating the template std::complex for any type other than float, + // double, or long double is unspecified. We don't care though, we want them to work with units in this test + #include #include #include @@ -206,7 +209,7 @@ TEST_F(TypeTraits, inverse) TEST_F(TypeTraits, strong) { - EXPECT_TRUE((std::is_same_v>>)); + EXPECT_TRUE((std::is_same_v>>)); EXPECT_TRUE((std::is_same_v::conversion_factor, traits::strong_t, dimension::length>>>)); EXPECT_TRUE((std::is_same_v::conversion_factor, traits::strong_t::conversion_factor>>)); EXPECT_TRUE((std::is_same_v::conversion_factor, traits::strong_t::conversion_factor>>>)); @@ -659,14 +662,18 @@ TEST_F(STDTypeTraits, std_common_type) using T = std::common_type_t, double>; T a = 50_pct; EXPECT_DOUBLE_EQ(a, 0.5); - // static_assert(std::is_same_v, int>, dimensionless>); - // static_assert(std::is_same_v>, dimensionless>); - // static_assert(std::is_same_v, double>, dimensionless>); - // static_assert(std::is_same_v>, dimensionless>); - // static_assert(std::is_same_v, int>, dimensionless>); - // static_assert(std::is_same_v>, dimensionless>); - // static_assert(std::is_same_v, double>, dimensionless>); - // static_assert(std::is_same_v>, dimensionless>); + static_assert(std::is_same_v, int>, unit, dimension::dimensionless>, int>>); + static_assert(std::is_same_v, dimension::dimensionless>, dimensionless_>); + static_assert(std::is_same_v, int>, unit>); + + static_assert(std::is_same_v, int>, dimensionless>); + static_assert(std::is_same_v>, dimensionless>); + static_assert(std::is_same_v, double>, dimensionless>); + static_assert(std::is_same_v>, dimensionless>); + static_assert(std::is_same_v, int>, dimensionless>); + static_assert(std::is_same_v>, dimensionless>); + static_assert(std::is_same_v, double>, dimensionless>); + static_assert(std::is_same_v>, dimensionless>); } TEST_F(STDSpecializations, hash) @@ -695,9 +702,9 @@ TEST_F(UnitManipulators, squared) test = square_feet(unit>>(0.092903)).value(); EXPECT_NEAR(0.99999956944, test, 5.0e-12); - using dimensionless_2 = traits::strong_t>; // this is actually nonsensical, and should also result in + using dimensionless_2 = traits::strong_t>; // this is actually nonsensical, and should also result in // a dimensionless. - bool isSame = std::is_same_v, unit>; + bool isSame = std::is_same_v, unit>; EXPECT_TRUE(isSame); } @@ -787,7 +794,7 @@ TEST_F(UnitType, trivial) TEST_F(UnitType, complexUnits) { std::complex> x(3_m, 4_m); - EXPECT_TRUE((std::conj(x) == std::complex>{3_m, -4_m})); + EXPECT_TRUE((std::conj(x) == std::complex{3.0_m, -4.0_m})); } TEST_F(UnitType, constructionFromArithmeticType) @@ -875,6 +882,10 @@ TEST_F(UnitType, constructionFromUnitType) EXPECT_EQ(1, g_dim.value()); } +namespace units { + +} + TEST_F(UnitType, CTAD) { #if defined(__cpp_deduction_guides) && __cpp_deduction_guides >= 201907L @@ -889,8 +900,13 @@ TEST_F(UnitType, CTAD) const meters b_m(a_m); static_assert(std::is_same_v, meters>); + const meters b_m2(millimeters(2.0)); + static_assert(std::is_same_v, meters>); + const millimeters a_mm(b_m); + static_assert(std::is_integral_v); static_assert(std::is_same_v, millimeters>); + EXPECT_EQ(a_mm, 1000_mm); const meters c_m(1.0); static_assert(std::is_same_v, meters>); @@ -950,9 +966,14 @@ TEST_F(UnitType, CTAD) const seconds c_s(1.0_s); static_assert(std::is_floating_point_v); - [[maybe_unused]] const seconds c_s(1_min); - [[maybe_unused]] const seconds d_s(1.0_min); - [[maybe_unused]] const seconds e_s(1.0_ms); + const seconds d_s(1_min); + static_assert(std::is_integral_v); + + const seconds e_s(1.0_min); + static_assert(std::is_floating_point_v); + + const seconds f_s(1.0_ms); + static_assert(std::is_floating_point_v); // Dimensionless units. const dimensionless z_dim = 1.0; @@ -994,13 +1015,13 @@ TEST_F(UnitType, CTAD) const dimensionless j_dim(dimensionless(1.0)); static_assert(std::is_same_v, dimensionless>); - const dimensionless k_dim(unit, int>(1)); + const dimensionless k_dim(unit, int>(1)); static_assert(std::is_same_v, dimensionless>); - const dimensionless l_dim(unit, double>(1.0)); + const dimensionless l_dim(unit, double>(1.0)); static_assert(std::is_same_v, dimensionless>); - const dimensionless m_dim(unit, double>(1.0)); + const dimensionless m_dim(unit, double>(1.0)); static_assert(std::is_same_v, dimensionless>); #endif // defined(__cpp_deduction_guides) && __cpp_deduction_guides >= 201907L } @@ -1148,8 +1169,8 @@ TEST_F(UnitType, make_unit) TEST_F(UnitType, unitTypeEquality) { - const meters a_m(0); - const meters b_m(1); + const meters a_m(0.0); + const meters b_m(1.0); EXPECT_TRUE(a_m == a_m); EXPECT_FALSE(a_m == b_m); @@ -1173,8 +1194,8 @@ TEST_F(UnitType, unitTypeEquality) EXPECT_FALSE(a_m != c_m); EXPECT_FALSE(d_m != b_m); - const percent w_m(100); - const percent x_m(1); + const percent w_m(100.0); + const percent x_m(1.0); EXPECT_TRUE(w_m == w_m); EXPECT_FALSE(w_m == x_m); @@ -2988,7 +3009,7 @@ TEST_F(UnitType, to_string_locale) setlocale(LC_ALL, "de-DE"); os1.imbue(std::locale("de-DE")); #else - EXPECT_STREQ("de_DE.utf8", setlocale(LC_ALL, "de_DE.utf8")); + EXPECT_STREQ("de_DE.utf8", setlocale(LC_ALL, "de_DE.utf8")) << "For this test to work, you need a german locale installed: `sudo locale-gen de_DE.UTF-8`"; os1.imbue(std::locale("de_DE.utf8")); #endif @@ -3011,7 +3032,7 @@ TEST_F(UnitType, to_string_locale) setlocale(LC_ALL, "en-US"); os2.imbue(std::locale("en-US")); #else - EXPECT_STREQ("en_US.utf8", setlocale(LC_ALL, "en_US.utf8")); + EXPECT_STREQ("en_US.utf8", setlocale(LC_ALL, "en_US.utf8")) << "For this test to work, you need a USA locale installed: `sudo locale-gen en_US.UTF-8`"; os2.imbue(std::locale("en_US.utf8")); #endif From 7e335f74b9ec59486a3725617646080d08b9b960 Mon Sep 17 00:00:00 2001 From: Nic Holthaus Date: Fri, 6 Dec 2024 17:47:29 -0500 Subject: [PATCH 03/48] refactor: clang-format --- include/units/core.h | 193 +++++++++++++++++++++---------------------- unitTests/main.cpp | 48 +++++------ 2 files changed, 119 insertions(+), 122 deletions(-) diff --git a/include/units/core.h b/include/units/core.h index f32c95b8..40fca7fc 100644 --- a/include/units/core.h +++ b/include/units/core.h @@ -44,8 +44,8 @@ #pragma once -#ifndef units_core_h__ -#define units_core_h__ +#ifndef UNIT_CORE_H +#define UNIT_CORE_H #ifndef UNIT_LIB_DEFAULT_TYPE #define UNIT_LIB_DEFAULT_TYPE double @@ -55,6 +55,7 @@ // INCLUDES //-------------------- +#include "core.h" #include #include #include @@ -66,44 +67,39 @@ #include #include -#include "core.h" - #if !defined(UNIT_LIB_DISABLE_IOSTREAM) #include -#include #include //------------------------------ // STRING FORMATTER //------------------------------ -namespace units +namespace units::detail { - namespace detail + template + std::string to_string(const T& t) { - template - std::string to_string(const T& t) + std::string str{std::to_string(t)}; + + if constexpr (std::is_floating_point_v) { - std::string str{std::to_string(t)}; + unsigned int offset{1}; - if constexpr (std::is_floating_point_v) + // remove trailing decimal points for integer value units. Locale aware! + struct std::lconv* lc; + lc = std::localeconv(); + char decimalPoint = *lc->decimal_point; + if (str.find_last_not_of('0') == str.find(decimalPoint)) { - unsigned int offset{1}; - - // remove trailing decimal points for integer value units. Locale aware! - struct std::lconv* lc; - lc = std::localeconv(); - char decimalPoint = *lc->decimal_point; - if (str.find_last_not_of('0') == str.find(decimalPoint)) - { - offset = 0; - } - str.erase(str.find_last_not_of('0') + offset, std::string::npos); + offset = 0; } - return str; + str.erase(str.find_last_not_of('0') + offset, std::string::npos); } - } // namespace detail -} // namespace units + return str; + } +} // namespace units::detail + #endif // !defined(UNIT_LIB_DISABLE_IOSTREAM) //------------------------------ @@ -117,6 +113,7 @@ namespace units { static constexpr const char* value = nullptr; }; + template struct unit_abbreviation { @@ -125,6 +122,7 @@ namespace units template inline constexpr const char* unit_name_v = unit_name::value; + template inline constexpr const char* unit_abbreviation_v = unit_abbreviation::value; } // namespace units @@ -198,8 +196,8 @@ namespace units * commas to be easily expanded. All the variadic 'arguments' should together * comprise the unit definition. */ -#define UNIT_ADD_SCALED_UNIT_DEFINITION(unitName, scale, /*conversionFactor*/...) \ - template \ +#define UNIT_ADD_SCALED_UNIT_DEFINITION(unitName, scale, /*conversionFactor*/...) \ + template \ using unitName = ::units::unit, Underlying, scale>; /** * @def UNIT_ADD_NAME(namespaceName,namePlural,abbreviation) @@ -498,7 +496,7 @@ namespace units template class Op, class... Args> inline constexpr bool is_detected_convertible_v = is_detected_convertible::value; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE //------------------------------ @@ -524,7 +522,7 @@ namespace units struct is_ratio_impl> : std::true_type { }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE /** @@ -599,7 +597,7 @@ namespace units ///< is not a unit. }; /** @endcond */ // END DOXYGEN IGNORE - } // namespace traits + } // namespace traits /** @cond */ // DOXYGEN IGNORE namespace detail @@ -931,7 +929,7 @@ namespace units using concentration = make_dimension>; ///< Represents a unit of concentration using data = make_dimension; ///< Represents a unit of data size using data_transfer_rate = dimension_divide; ///< Represents a unit of data transfer rate - } // namespace dimension + } // namespace dimension //------------------------------ // CONVERSION FACTOR CLASSES @@ -997,7 +995,7 @@ namespace units { using type = void; }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE namespace traits @@ -1039,7 +1037,7 @@ namespace units struct has_dimension_of_impl, Dim> : std::is_same, Dim>::type { }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE namespace traits @@ -1169,7 +1167,7 @@ namespace units std::ratio_multiply::pi_exponent_ratio, std::ratio<-1>>, std::ratio<0>>; // inverses are rates or change, the translation factor goes away. }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE /** @@ -1195,10 +1193,10 @@ namespace units static_assert(traits::is_conversion_factor_v, "Template parameter `Unit` must be a `unit` type."); using Conversion = typename Unit::conversion_ratio; using type = conversion_factor, - dimension_pow, std::ratio<2>>, - std::ratio_multiply>, typename Unit::translation_ratio>; + dimension_pow, std::ratio<2>>, + std::ratio_multiply>, typename Unit::translation_ratio>; }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE /** @@ -1224,10 +1222,10 @@ namespace units static_assert(traits::is_conversion_factor_v, "Template parameter `Unit` must be a `unit` type."); using Conversion = typename Unit::conversion_ratio; using type = conversion_factor>, - dimension_pow, std::ratio<3>>, - std::ratio_multiply>, typename Unit::translation_ratio>; + dimension_pow, std::ratio<3>>, + std::ratio_multiply>, typename Unit::translation_ratio>; }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE /** @@ -1418,7 +1416,7 @@ namespace units using type = conversion_factor, dimension_root, std::ratio<2>>, std::ratio_divide>, typename Unit::translation_ratio>; }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE /** @@ -1468,7 +1466,7 @@ namespace units struct compound_impl : compound_impl, Us...> { }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE /** @@ -1517,7 +1515,7 @@ namespace units { using type = U; }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE // clang-format off @@ -1631,7 +1629,7 @@ namespace units { return curr == prev ? curr : sqrtNewtonRaphson(x, 0.5 * (curr + x / curr), curr); } - } // namespace Detail + } // namespace Detail /** @endcond */ // END DOXYGEN IGNORE template, int> = 0> @@ -1664,7 +1662,7 @@ namespace units return pow_acc<(Exp - 1) / 2>(acc * base, base * base); } } - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE template, int> = 0> @@ -1696,7 +1694,7 @@ namespace units } return pow_acc(acc * x, x * x, (y - 1) / 2); } - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE template, std::is_unsigned>, int> = 0> @@ -1779,7 +1777,7 @@ namespace units using ResolvedUnitFrom = conversion_factor; using ResolvedUnitTo = conversion_factor; + typename ConversionFactorTo::pi_exponent_ratio>; return convert>(value); }; @@ -1791,14 +1789,13 @@ namespace units // PI REQUIRED, no translation else if constexpr (!std::is_same_v, PiRatio> && std::is_same_v, Translation>) { - using CommonUnderlying = std::common_type_t; + using CommonUnderlying = std::common_type_t; constexpr long double PiRatioValue = PiRatio::num / PiRatio::den; // constexpr pi in numerator if constexpr (PiRatioValue >= 1 && PiRatio::num % PiRatio::den == 0) { - return static_cast( - normal_convert(static_cast(value) * static_cast(pow(detail::PI_VAL, PiRatioValue)))); + return static_cast(normal_convert(static_cast(value) * static_cast(pow(detail::PI_VAL, PiRatioValue)))); } // constexpr pi in denominator else if constexpr (PiRatioValue <= -1 && PiRatio::num % PiRatio::den == 0) @@ -1809,8 +1806,8 @@ namespace units // non-constexpr pi in numerator. This case (only) isn't actually constexpr. else if constexpr (PiRatioValue < 1 && PiRatio::num / PiRatioValue > -1) { - return static_cast(normal_convert( - static_cast(value) * static_cast(std::pow(detail::PI_VAL, PiRatioValue)))); + return static_cast( + normal_convert(static_cast(value) * static_cast(std::pow(detail::PI_VAL, PiRatioValue)))); } } // Translation required, no pi variable @@ -1866,7 +1863,7 @@ namespace units { template struct is_same_dimension_unit; - } // namespace traits + } // namespace traits /** @endcond */ // END DOXYGEN IGNORE /** @@ -1903,7 +1900,7 @@ namespace units template, int> = 0> decltype(NumericalScale::scale(T{})) operator()(T); }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE /** @@ -1977,7 +1974,7 @@ namespace units using conversion_factor = typename T::conversion_factor; }; /** @endcond */ // END DOXYGEN IGNORE - } // namespace traits + } // namespace traits namespace traits { @@ -2057,7 +2054,7 @@ namespace units struct _unit { }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE namespace traits @@ -2169,8 +2166,8 @@ namespace units * @param[in] value unit magnitude. */ template - requires (!traits::is_dimensionless_unit::value && detail::is_losslessly_convertible) - explicit constexpr unit(Ty value) noexcept : linearized_value(NumericalScale::linearize(static_cast(value))) + requires(!traits::is_dimensionless_unit::value && detail::is_losslessly_convertible) + explicit constexpr unit(Ty value) noexcept : linearized_value(NumericalScale::linearize(static_cast(value))) { } @@ -2180,7 +2177,7 @@ namespace units * @param[in] value linearized unit magnitude. */ template - requires detail::is_losslessly_convertible + requires detail::is_losslessly_convertible explicit constexpr unit(Ty value, linearized_value_t) noexcept : linearized_value(value) { } @@ -2191,7 +2188,7 @@ namespace units * @param[in] value value of the unit */ template - requires traits::is_dimensionless_unit::value && detail::is_losslessly_convertible + requires traits::is_dimensionless_unit::value && detail::is_losslessly_convertible constexpr unit(Ty value) noexcept : linearized_value(NumericalScale::linearize(static_cast(value))) { } @@ -2202,7 +2199,7 @@ namespace units * @param[in] value value of the unit */ template - requires detail::is_time_conversion_factor && detail::is_losslessly_convertible + requires detail::is_time_conversion_factor && detail::is_losslessly_convertible constexpr unit(const std::chrono::duration& value) noexcept : linearized_value(units::convert(units::unit, Rep>(value.count())).linearized_value) { @@ -2214,7 +2211,7 @@ namespace units * @param[in] rhs unit to copy. */ template - requires detail::is_losslessly_convertible_unit, unit> + requires detail::is_losslessly_convertible_unit, unit> constexpr unit(const unit& rhs) noexcept : linearized_value(units::convert(rhs).linearized_value) { } @@ -2232,7 +2229,7 @@ namespace units * @param[in] rhs value to copy. */ template - requires traits::is_dimensionless_unit::value + requires traits::is_dimensionless_unit::value constexpr unit& operator=(const underlying_type& rhs) noexcept { unit, units::dimension::dimensionless>, underlying_type, linear_scale> dimensionlessRhs(rhs); @@ -2300,9 +2297,8 @@ namespace units * @note This may not be suitable for all applications when the underlying_type of unit is a double. */ template - requires std::is_floating_point_v || std::is_floating_point_v - constexpr bool operator==( - const unit& rhs) const noexcept + requires std::is_floating_point_v || std::is_floating_point_v + constexpr bool operator==(const unit& rhs) const noexcept { using CommonUnit = std::common_type_t>; using CommonUnderlying = typename CommonUnit::underlying_type; @@ -2315,9 +2311,8 @@ namespace units } template - requires std::is_integral::value && std::is_integral::value - constexpr bool operator==( - const unit& rhs) const noexcept + requires std::is_integral::value && std::is_integral::value + constexpr bool operator==(const unit& rhs) const noexcept { using CommonUnit = std::common_type_t>; return CommonUnit(*this).linearized_value == CommonUnit(rhs).linearized_value; @@ -2371,7 +2366,7 @@ namespace units * @returns value of the unit converted to an arithmetic, non-safe type. */ template - requires std::is_arithmetic_v + requires std::is_arithmetic_v constexpr Ty to() const noexcept { return static_cast(*this); @@ -2414,7 +2409,7 @@ namespace units * *this. */ template class UnitType> - requires traits::is_same_dimension_unit_v, unit> + requires traits::is_same_dimension_unit_v, unit> constexpr UnitType convert() noexcept { return UnitType(*this); @@ -2425,14 +2420,13 @@ namespace units * @details only enabled for dimensionless unit types. */ template - requires traits::is_dimensionless_unit::value && std::is_arithmetic_v + requires traits::is_dimensionless_unit::value && std::is_arithmetic_v constexpr operator Ty() const noexcept { // this conversion also resolves any PI exponents, by converting from a non-zero PI ratio to a zero-pi // ratio. return NumericalScale::scale( - units::convert, dimension::dimensionless>, Ty, NumericalScale>>(*this) - .to_linearized()); + units::convert, dimension::dimensionless>, Ty, NumericalScale>>(*this).to_linearized()); } /** @@ -2440,7 +2434,7 @@ namespace units * @details only enabled for non-dimensionless unit types. */ template - requires (!traits::is_dimensionless_unit::value && std::is_arithmetic_v) + requires(!traits::is_dimensionless_unit::value && std::is_arithmetic_v) constexpr explicit operator Ty() const noexcept { return static_cast(this->value()); @@ -2451,7 +2445,7 @@ namespace units * @details only enabled for time unit types. */ template - requires detail::is_time_conversion_factor && detail::is_losslessly_convertible + requires detail::is_time_conversion_factor && detail::is_losslessly_convertible constexpr operator std::chrono::duration() const noexcept { return std::chrono::duration(units::unit, Rep>(*this).value()); @@ -2500,7 +2494,7 @@ namespace units * @param[in] value Arithmetic value that represents a quantity in units of `UnitType`. */ template - requires detail::is_losslessly_convertible + requires detail::is_losslessly_convertible constexpr UnitType make_unit(const T value) noexcept { static_assert(traits::is_unit_v, "Template parameter `UnitType` must be a unit type."); @@ -2513,13 +2507,13 @@ namespace units // Conversion factor from Target, type from Source template - requires traits::is_unit_v> - unit(const unit&)-> unit; + requires traits::is_unit_v> + unit(const unit&) -> unit; // Matching Target and Source factors template - requires traits::is_unit_v> - unit(const unit&)-> unit; + requires traits::is_unit_v> + unit(const unit&) -> unit; // Deduce from the same unit type template @@ -2527,7 +2521,7 @@ namespace units // Deduce type from arithmetic type template - requires std::is_arithmetic_v + requires std::is_arithmetic_v unit(T) -> unit; #if !defined(UNIT_LIB_DISABLE_IOSTREAM) @@ -2635,7 +2629,7 @@ namespace units */ template using ratio_gcd = std::ratio; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE } // end namespace units @@ -2858,7 +2852,7 @@ namespace units template using type_identity_t = typename type_identity::type; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE template, int> = 0> @@ -3199,9 +3193,9 @@ namespace units std::enable_if_t && traits::is_dimensionless_unit_v && !traits::is_dimensionless_unit_v, int> = 0> - constexpr auto operator/(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept - -> unit::conversion_factor>>, - std::common_type_t> + constexpr auto operator/(const UnitTypeLhs& lhs, + const UnitTypeRhs& rhs) noexcept -> unit::conversion_factor>>, + std::common_type_t> { using CommonUnit = decltype(lhs / rhs); using CommonUnderlying = typename CommonUnit::underlying_type; @@ -3219,9 +3213,9 @@ namespace units /// Division of a dimensionless by a unit type with a linear scale template && traits::has_linear_scale_v, int> = 0> - constexpr auto operator/(T lhs, const UnitTypeRhs& rhs) noexcept - -> unit::conversion_factor>>, - std::common_type_t> + constexpr auto operator/( + T lhs, const UnitTypeRhs& rhs) noexcept -> unit::conversion_factor>>, + std::common_type_t> { using InverseUnit = decltype(lhs / rhs); using UnitConversion = typename units::traits::unit_traits::conversion_factor; @@ -3427,7 +3421,7 @@ namespace units { typedef units::dimensionless_ type; }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE /** @@ -3548,8 +3542,8 @@ namespace units /// Subtraction for convertible unit types with a decibel_scale template && traits::has_decibel_scale_v, int> = 0> - constexpr auto operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept - -> dB::underlying_type> + constexpr auto operator-( + const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept -> dB::underlying_type> { using Dimensionless = decltype(lhs - rhs); using CommonUnit = std::common_type_t; @@ -3574,9 +3568,9 @@ namespace units std::enable_if_t && traits::is_dimensionless_unit_v && !traits::is_dimensionless_unit_v, int> = 0> - constexpr auto operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept - -> unit::conversion_factor>>, - std::common_type_t, decibel_scale> + constexpr auto operator-(const UnitTypeLhs& lhs, + const UnitTypeRhs& rhs) noexcept -> unit::conversion_factor>>, + std::common_type_t, decibel_scale> { using InverseUnit = decltype(lhs - rhs); return InverseUnit(lhs.to_linearized() / rhs.to_linearized(), linearized_value); @@ -3767,8 +3761,9 @@ namespace units * unit type may have errors no larger than `1e-10`. */ template, int> = 0> - constexpr auto sqrt(const UnitType& value) noexcept -> unit::conversion_factor>>, - detail::floating_point_promotion_t::underlying_type>, linear_scale> + constexpr auto sqrt( + const UnitType& value) noexcept -> unit::conversion_factor>>, + detail::floating_point_promotion_t::underlying_type>, linear_scale> { return decltype(units::sqrt(value))(sqrt(value.value())); } @@ -4091,4 +4086,4 @@ namespace units #endif #endif -#endif // units_core_h__ \ No newline at end of file +#endif // UNIT_CORE_H \ No newline at end of file diff --git a/unitTests/main.cpp b/unitTests/main.cpp index a3c4c283..ef6d7d6b 100644 --- a/unitTests/main.cpp +++ b/unitTests/main.cpp @@ -1,5 +1,6 @@ #define _SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING // officially, The effect of instantiating the template std::complex for any type other than float, - // double, or long double is unspecified. We don't care though, we want them to work with units in this test + // double, or long double is unspecified. We don't care though, we want them to work with units in this + // test #include #include @@ -20,8 +21,8 @@ namespace protected: TypeTraits(){}; virtual ~TypeTraits(){}; - void SetUp() override{}; - void TearDown() override{}; + void SetUp() override {}; + void TearDown() override {}; }; class STDTypeTraits : public ::testing::Test @@ -29,8 +30,8 @@ namespace protected: STDTypeTraits(){}; virtual ~STDTypeTraits(){}; - void SetUp() override{}; - void TearDown() override{}; + void SetUp() override {}; + void TearDown() override {}; }; class STDSpecializations : public ::testing::Test @@ -38,8 +39,8 @@ namespace protected: STDSpecializations(){}; virtual ~STDSpecializations(){}; - void SetUp() override{}; - void TearDown() override{}; + void SetUp() override {}; + void TearDown() override {}; }; class UnitManipulators : public ::testing::Test @@ -47,8 +48,8 @@ namespace protected: UnitManipulators(){}; virtual ~UnitManipulators(){}; - void SetUp() override{}; - void TearDown() override{}; + void SetUp() override {}; + void TearDown() override {}; }; class UnitType : public ::testing::Test @@ -56,8 +57,8 @@ namespace protected: UnitType(){}; virtual ~UnitType(){}; - void SetUp() override{}; - void TearDown() override{}; + void SetUp() override {}; + void TearDown() override {}; }; class ConversionFactor : public ::testing::Test @@ -65,8 +66,8 @@ namespace protected: ConversionFactor(){}; virtual ~ConversionFactor(){}; - void SetUp() override{}; - void TearDown() override{}; + void SetUp() override {}; + void TearDown() override {}; }; class UnitMath : public ::testing::Test @@ -74,8 +75,8 @@ namespace protected: UnitMath(){}; virtual ~UnitMath(){}; - void SetUp() override{}; - void TearDown() override{}; + void SetUp() override {}; + void TearDown() override {}; }; class Constexpr : public ::testing::Test @@ -83,8 +84,8 @@ namespace protected: Constexpr(){}; virtual ~Constexpr(){}; - void SetUp() override{}; - void TearDown() override{}; + void SetUp() override {}; + void TearDown() override {}; }; class CaseStudies : public ::testing::Test @@ -92,8 +93,8 @@ namespace protected: CaseStudies(){}; virtual ~CaseStudies(){}; - void SetUp() override{}; - void TearDown() override{}; + void SetUp() override {}; + void TearDown() override {}; }; // Tests that two units have the same conversion ratio to the same dimension. @@ -662,9 +663,9 @@ TEST_F(STDTypeTraits, std_common_type) using T = std::common_type_t, double>; T a = 50_pct; EXPECT_DOUBLE_EQ(a, 0.5); - static_assert(std::is_same_v, int>, unit, dimension::dimensionless>, int>>); + static_assert(std::is_same_v, int>, unit, dimension::dimensionless>, int>>); static_assert(std::is_same_v, dimension::dimensionless>, dimensionless_>); - static_assert(std::is_same_v, int>, unit>); + static_assert(std::is_same_v, int>, unit>); static_assert(std::is_same_v, int>, dimensionless>); static_assert(std::is_same_v>, dimensionless>); @@ -703,7 +704,7 @@ TEST_F(UnitManipulators, squared) EXPECT_NEAR(0.99999956944, test, 5.0e-12); using dimensionless_2 = traits::strong_t>; // this is actually nonsensical, and should also result in - // a dimensionless. + // a dimensionless. bool isSame = std::is_same_v, unit>; EXPECT_TRUE(isSame); } @@ -882,7 +883,8 @@ TEST_F(UnitType, constructionFromUnitType) EXPECT_EQ(1, g_dim.value()); } -namespace units { +namespace units +{ } From ea7fd22c1052c6f96df9f41522e884578eba3520 Mon Sep 17 00:00:00 2001 From: Nic Holthaus Date: Fri, 6 Dec 2024 18:04:28 -0500 Subject: [PATCH 04/48] refactor:formatting, clang tidy --- include/units/core.h | 81 ++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/include/units/core.h b/include/units/core.h index 40fca7fc..79a57e1e 100644 --- a/include/units/core.h +++ b/include/units/core.h @@ -87,9 +87,8 @@ namespace units::detail unsigned int offset{1}; // remove trailing decimal points for integer value units. Locale aware! - struct std::lconv* lc; - lc = std::localeconv(); - char decimalPoint = *lc->decimal_point; + std::lconv* lc = std::localeconv(); + char decimalPoint = *lc->decimal_point; if (str.find_last_not_of('0') == str.find(decimalPoint)) { offset = 0; @@ -133,7 +132,7 @@ namespace units /** * @def UNIT_ADD_STRONG_CONVERSION_FACTOR(namespaceName, namePlural, __VA_ARGS__) - * @brief Helper macro for generating the boiler-plate code generating the tags of a new unit. + * @brief Helper macro for generating the boilerplate code generating the tags of a new unit. * @details The macro generates singular, plural, and abbreviated forms * of the unit definition (e.g. `meter`, `meters`, and `m`), as aliases for the * unit tag. @@ -166,7 +165,7 @@ namespace units /** * @def UNIT_ADD_UNIT_DEFINITION(namespaceName,namePlural) - * @brief Macro for generating the boiler-plate code for the unit type definition. + * @brief Macro for generating the boilerplate code for the unit type definition. * @details The macro generates the definition of the unit container types, e.g. `meter` * @param namespaceName namespace in which the new units will be encapsulated. * @param namePlural - plural version of the unit name, e.g. 'meters' @@ -182,11 +181,11 @@ namespace units /** * @def UNIT_ADD_SCALED_UNIT_DEFINITION(unitName, scale, definition) - * @brief Macro for generating the boiler-plate code for the scaled unit template definition. + * @brief Macro for generating the boilerplate code for the scaled unit template definition. * @details The macro generates the definition of the scaled unit templates as a strong type template alias, * e.g. `meters` * @param unitName unit name, e.g. 'meters' - * @param scale the non linear scale template argument of the unit's base + * @param scale the non-linear scale template argument of the unit's base * @param definition - the variadic parameter is used for the definition of the unit * (e.g. `conversion_factor, units::dimension::length>`) * @param __VA_ARGS__ - the conversion factor definition for the unit type. Taken as variadiac @@ -248,7 +247,7 @@ namespace units /** * @def UNIT_ADD(namespaceName, namePlural, abbreviation, definition) - * @brief Macro for generating the boiler-plate code needed for a new unit. + * @brief Macro for generating the boilerplate code needed for a new unit. * @details The macro generates singular, plural, and abbreviated forms * of the unit definition (e.g. `meter`, `meters`, and `m`), as well as the * appropriately named unit container (e.g. `meter_t`). A literal suffix is created @@ -293,17 +292,17 @@ namespace units * @def UNIT_ADD_DIMENSION_TRAIT(unitdimension) * @brief Macro to create the `is_dimension_unit` type trait. * @details This trait allows users to test whether a given type matches - * an intended dimension. This macro comprises all the boiler-plate + * an intended dimension. This macro comprises all the boilerplate * code necessary to do so. * @param unitdimension The name of the dimension of unit, e.g. length or mass. */ #define UNIT_ADD_DIMENSION_TRAIT(unitdimension) \ - /** @ingroup TypeTraits*/ \ - /** @brief `UnaryTypeTrait` for querying whether `T` represents a unit of unitdimension*/ \ - /** @details The base characteristic is a specialization of the template `std::bool_constant`.*/ \ - /** Use `is_ ## unitdimension ## _unit_v` to test the unit represents a unitdimension quantity.*/ \ - /** @tparam T type to test*/ \ + /** @ingroup TypeTraits*/ \ + /** @brief `UnaryTypeTrait` for querying whether `T` represents a unit of unitdimension*/ \ + /** @details The base characteristic is a specialization of the template `std::bool_constant`.*/ \ + /** Use `is_ ## unitdimension ## _unit_v` to test the unit represents a unitdimension quantity.*/ \ + /** @tparam T type to test*/ \ namespace traits \ { \ template \ @@ -316,7 +315,7 @@ namespace units /** * @def UNIT_ADD_WITH_METRIC_PREFIXES(namespaceName, namePlural, abbreviation, definition) - * @brief Macro for generating the boiler-plate code needed for a new unit, including its metric + * @brief Macro for generating the boilerplate code needed for a new unit, including its metric * prefixes from femto to peta. * @details See UNIT_ADD. In addition to generating the unit definition and containers '(e.g. `meters` and * 'meter_t', it also creates corresponding units with metric suffixes such as `millimeters`, and @@ -351,7 +350,7 @@ namespace units /** * @def UNIT_ADD_WITH_METRIC_AND_BINARY_PREFIXES(namespaceName, namePlural, abbreviation, definition) - * @brief Macro for generating the boiler-plate code needed for a new unit, including its metric + * @brief Macro for generating the boilerplate code needed for a new unit, including its metric * prefixes from femto to peta, and binary prefixes from kibi to exbi. * @details See UNIT_ADD. In addition to generating the unit definition and containers '(e.g. `bytes` and 'byte_t', * it also creates corresponding units with metric suffixes such as `millimeters`, and `millimeter_t`), as @@ -1327,9 +1326,10 @@ namespace units // The error |f(Rem)-V| = |(U-W*V)x/(W*x+1)| <= |U-W*V|*Rem <= |U-W*V|/I' where // I' is the std::integer part of reciprocal of Rem. template - struct ContinuedFraction { + struct ContinuedFraction + { template - using Abs_ = std::conditional_t::value, std::ratio_subtract, T>; + using Abs_ = std::conditional_t, std::ratio_subtract, T>; using R = Tr; using Last_ = ContinuedFraction; @@ -1357,22 +1357,22 @@ namespace units struct Sqrt_ : Sqrt_ {}; template - struct Sqrt_::Error, Eps>::value>> { + struct Sqrt_::Error, Eps>>> { using type = typename ContinuedFraction::V; }; template struct Sqrt { - static_assert(std::ratio_greater_equal::value, "R can't be negative"); + static_assert(std::ratio_greater_equal_v, "R can't be negative"); }; template - struct Sqrt::value && IsPerfectSquare::value>> { + struct Sqrt && IsPerfectSquare::value>> { using type = typename IsPerfectSquare::Sqrt; }; template - struct Sqrt::value && !IsPerfectSquare::value)>> : Sqrt_{}; + struct Sqrt && !IsPerfectSquare::value)>> : Sqrt_{}; } // clang-format on /** @endcond */ // END DOXYGEN IGNORE @@ -1476,7 +1476,7 @@ namespace units * be formed from any number of other conversion factors, and unit manipulators like `inverse` and * `squared` are supported. E.g. to specify acceleration, one could declare * `using acceleration = compound_conversion factor>;` - * @tparam U... conversion factor which, when multiplied together, + * @tparam U conversion factor which, when multiplied together, * form the desired compound conversion factor. * @ingroup ConversionFactor */ @@ -2311,7 +2311,7 @@ namespace units } template - requires std::is_integral::value && std::is_integral::value + requires std::is_integral_v && std::is_integral_v constexpr bool operator==(const unit& rhs) const noexcept { using CommonUnit = std::common_type_t>; @@ -2355,8 +2355,8 @@ namespace units return NumericalScale::scale(units::convert, units::dimension::dimensionless, typename traits::conversion_factor_traits::pi_exponent_ratio, typename traits::conversion_factor_traits::translation_ratio>, - T, NumericalScale>>(*this) - .to_linearized()); + T, NumericalScale>>(*this) + .to_linearized()); else return raw(); } @@ -3193,9 +3193,9 @@ namespace units std::enable_if_t && traits::is_dimensionless_unit_v && !traits::is_dimensionless_unit_v, int> = 0> - constexpr auto operator/(const UnitTypeLhs& lhs, - const UnitTypeRhs& rhs) noexcept -> unit::conversion_factor>>, - std::common_type_t> + constexpr auto operator/(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept + -> unit::conversion_factor>>, + std::common_type_t> { using CommonUnit = decltype(lhs / rhs); using CommonUnderlying = typename CommonUnit::underlying_type; @@ -3213,9 +3213,9 @@ namespace units /// Division of a dimensionless by a unit type with a linear scale template && traits::has_linear_scale_v, int> = 0> - constexpr auto operator/( - T lhs, const UnitTypeRhs& rhs) noexcept -> unit::conversion_factor>>, - std::common_type_t> + constexpr auto operator/(T lhs, const UnitTypeRhs& rhs) noexcept + -> unit::conversion_factor>>, + std::common_type_t> { using InverseUnit = decltype(lhs / rhs); using UnitConversion = typename units::traits::unit_traits::conversion_factor; @@ -3542,8 +3542,8 @@ namespace units /// Subtraction for convertible unit types with a decibel_scale template && traits::has_decibel_scale_v, int> = 0> - constexpr auto operator-( - const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept -> dB::underlying_type> + constexpr auto operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept + -> dB::underlying_type> { using Dimensionless = decltype(lhs - rhs); using CommonUnit = std::common_type_t; @@ -3568,9 +3568,9 @@ namespace units std::enable_if_t && traits::is_dimensionless_unit_v && !traits::is_dimensionless_unit_v, int> = 0> - constexpr auto operator-(const UnitTypeLhs& lhs, - const UnitTypeRhs& rhs) noexcept -> unit::conversion_factor>>, - std::common_type_t, decibel_scale> + constexpr auto operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept + -> unit::conversion_factor>>, + std::common_type_t, decibel_scale> { using InverseUnit = decltype(lhs - rhs); return InverseUnit(lhs.to_linearized() / rhs.to_linearized(), linearized_value); @@ -3761,9 +3761,8 @@ namespace units * unit type may have errors no larger than `1e-10`. */ template, int> = 0> - constexpr auto sqrt( - const UnitType& value) noexcept -> unit::conversion_factor>>, - detail::floating_point_promotion_t::underlying_type>, linear_scale> + constexpr auto sqrt(const UnitType& value) noexcept -> unit::conversion_factor>>, + detail::floating_point_promotion_t::underlying_type>, linear_scale> { return decltype(units::sqrt(value))(sqrt(value.value())); } @@ -4054,7 +4053,7 @@ namespace std //------------------------------ template - class numeric_limits> : public std::numeric_limits + struct numeric_limits> : public std::numeric_limits { }; } // namespace std From bbee148f72cdf7139e0ccffed575361f7379596a Mon Sep 17 00:00:00 2001 From: Nic Holthaus Date: Mon, 9 Dec 2024 14:46:55 -0500 Subject: [PATCH 05/48] fix: numeric_limits return unit types --- CMakeLists.txt | 2 +- include/units/core.h | 84 ++++++++++++++++++++++--- unitTests/main.cpp | 144 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 201 insertions(+), 29 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c7440b7..f24a3c34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16 FATAL_ERROR) +cmake_minimum_required(VERSION 3.16...3.31 FATAL_ERROR) PROJECT(units VERSION 3.1.0 LANGUAGES CXX) diff --git a/include/units/core.h b/include/units/core.h index 79a57e1e..76fbec31 100644 --- a/include/units/core.h +++ b/include/units/core.h @@ -298,11 +298,11 @@ namespace units */ #define UNIT_ADD_DIMENSION_TRAIT(unitdimension) \ - /** @ingroup TypeTraits*/ \ - /** @brief `UnaryTypeTrait` for querying whether `T` represents a unit of unitdimension*/ \ - /** @details The base characteristic is a specialization of the template `std::bool_constant`.*/ \ - /** Use `is_ ## unitdimension ## _unit_v` to test the unit represents a unitdimension quantity.*/ \ - /** @tparam T type to test*/ \ + /** @ingroup TypeTraits*/ \ + /** @brief `UnaryTypeTrait` for querying whether `T` represents a unit of unitdimension*/ \ + /** @details The base characteristic is a specialization of the template `std::bool_constant`.*/ \ + /** Use `is_ ## unitdimension ## _unit_v` to test the unit represents a unitdimension quantity.*/ \ + /** @tparam T type to test*/ \ namespace traits \ { \ template \ @@ -4047,15 +4047,79 @@ namespace std } } }; +} // namespace std - //------------------------------ - // std::numeric_limits - //------------------------------ +//---------------------------------------------------------------------------------------------------------------------- +// NUMERIC LIMITS +//---------------------------------------------------------------------------------------------------------------------- - template - struct numeric_limits> : public std::numeric_limits +namespace std +{ + template + struct numeric_limits> { + static constexpr units::unit min() + { + return units::unit(std::numeric_limits::min()); + } + static constexpr units::unit denorm_min() noexcept + { + return units::unit(std::numeric_limits::denorm_min()); + } + static constexpr units::unit max() + { + return units::unit(std::numeric_limits::max()); + } + static constexpr units::unit lowest() + { + return units::unit(std::numeric_limits::lowest()); + } + static constexpr units::unit epsilon() + { + return units::unit(std::numeric_limits::epsilon()); + } + static constexpr units::unit round_error() + { + return units::unit(std::numeric_limits::round_error()); + } + static constexpr units::unit infinity() + { + return units::unit(std::numeric_limits::infinity()); + } + static constexpr units::unit quiet_NaN() + { + return units::unit(std::numeric_limits::quiet_NaN()); + } + static constexpr units::unit signaling_NaN() + { + return units::unit(std::numeric_limits::signaling_NaN()); + } + static constexpr bool is_specialized = std::numeric_limits::is_specialized; + static constexpr bool is_signed = std::numeric_limits::is_signed; + static constexpr bool is_integer = std::numeric_limits::is_integer; + static constexpr bool is_exact = std::numeric_limits::is_exact; + static constexpr bool has_infinity = std::numeric_limits::has_infinity; + static constexpr bool has_quiet_NaN = std::numeric_limits::has_quiet_NaN; + static constexpr bool has_signaling_NaN = std::numeric_limits::has_signaling_NaN; }; + + template + bool isnan(const units::unit& x) + { + return std::isnan(x()); + } + + template + bool isinf(const units::unit& x) + { + return std::isinf(x()); + } + + template + bool signbit(const units::unit& x) + { + return std::signbit(x()); + } } // namespace std //---------------------------------------------------------------------------------------------------------------------- diff --git a/unitTests/main.cpp b/unitTests/main.cpp index ef6d7d6b..b51b29e3 100644 --- a/unitTests/main.cpp +++ b/unitTests/main.cpp @@ -19,8 +19,8 @@ namespace class TypeTraits : public ::testing::Test { protected: - TypeTraits(){}; - virtual ~TypeTraits(){}; + TypeTraits() {}; + virtual ~TypeTraits() {}; void SetUp() override {}; void TearDown() override {}; }; @@ -28,8 +28,8 @@ namespace class STDTypeTraits : public ::testing::Test { protected: - STDTypeTraits(){}; - virtual ~STDTypeTraits(){}; + STDTypeTraits() {}; + virtual ~STDTypeTraits() {}; void SetUp() override {}; void TearDown() override {}; }; @@ -37,8 +37,8 @@ namespace class STDSpecializations : public ::testing::Test { protected: - STDSpecializations(){}; - virtual ~STDSpecializations(){}; + STDSpecializations() {}; + virtual ~STDSpecializations() {}; void SetUp() override {}; void TearDown() override {}; }; @@ -46,8 +46,8 @@ namespace class UnitManipulators : public ::testing::Test { protected: - UnitManipulators(){}; - virtual ~UnitManipulators(){}; + UnitManipulators() {}; + virtual ~UnitManipulators() {}; void SetUp() override {}; void TearDown() override {}; }; @@ -55,8 +55,8 @@ namespace class UnitType : public ::testing::Test { protected: - UnitType(){}; - virtual ~UnitType(){}; + UnitType() {}; + virtual ~UnitType() {}; void SetUp() override {}; void TearDown() override {}; }; @@ -64,8 +64,8 @@ namespace class ConversionFactor : public ::testing::Test { protected: - ConversionFactor(){}; - virtual ~ConversionFactor(){}; + ConversionFactor() {}; + virtual ~ConversionFactor() {}; void SetUp() override {}; void TearDown() override {}; }; @@ -73,8 +73,8 @@ namespace class UnitMath : public ::testing::Test { protected: - UnitMath(){}; - virtual ~UnitMath(){}; + UnitMath() {}; + virtual ~UnitMath() {}; void SetUp() override {}; void TearDown() override {}; }; @@ -82,17 +82,26 @@ namespace class Constexpr : public ::testing::Test { protected: - Constexpr(){}; - virtual ~Constexpr(){}; + Constexpr() {}; + virtual ~Constexpr() {}; void SetUp() override {}; void TearDown() override {}; }; + class UnitLimits : public ::testing::Test + { + protected: + UnitLimits() {}; + virtual ~UnitLimits() {}; + virtual void SetUp() {}; + virtual void TearDown() {}; + }; + class CaseStudies : public ::testing::Test { protected: - CaseStudies(){}; - virtual ~CaseStudies(){}; + CaseStudies() {}; + virtual ~CaseStudies() {}; void SetUp() override {}; void TearDown() override {}; }; @@ -5034,6 +5043,105 @@ TEST_F(Constexpr, stdArray) EXPECT_TRUE(equal); } +TEST_F(UnitLimits, UnitMin) +{ + EXPECT_EQ(meters(std::numeric_limits::min()), std::numeric_limits>::min()); + EXPECT_EQ(seconds(std::numeric_limits::min()), std::numeric_limits>::min()); +} + +TEST_F(UnitLimits, UnitDenormMin) +{ + EXPECT_EQ(meters(std::numeric_limits::denorm_min()), std::numeric_limits>::denorm_min()); + EXPECT_EQ(seconds(std::numeric_limits::denorm_min()), std::numeric_limits>::denorm_min()); +} + +TEST_F(UnitLimits, UnitMax) +{ + EXPECT_EQ(meters(std::numeric_limits::max()), std::numeric_limits>::max()); + EXPECT_EQ(seconds(std::numeric_limits::max()), std::numeric_limits>::max()); +} + +TEST_F(UnitLimits, UnitLowest) +{ + EXPECT_EQ(meters(std::numeric_limits::lowest()), std::numeric_limits>::lowest()); + EXPECT_EQ(seconds(std::numeric_limits::lowest()), std::numeric_limits>::lowest()); +} + +TEST_F(UnitLimits, UnitEpsilon) +{ + EXPECT_EQ(meters(std::numeric_limits::epsilon()), std::numeric_limits>::epsilon()); + EXPECT_EQ(seconds(std::numeric_limits::epsilon()), std::numeric_limits>::epsilon()); +} + +TEST_F(UnitLimits, UnitRoundError) +{ + EXPECT_EQ(meters(std::numeric_limits::round_error()), std::numeric_limits>::round_error()); + EXPECT_EQ(seconds(std::numeric_limits::round_error()), std::numeric_limits>::round_error()); +} + +TEST_F(UnitLimits, UnitInfinity) +{ + EXPECT_TRUE(std::numeric_limits>::has_infinity); + EXPECT_TRUE(std::numeric_limits>::infinity() > std::numeric_limits>::max()); + EXPECT_FALSE(std::numeric_limits>::has_infinity); +} + +TEST_F(UnitLimits, UnitQuietNaN) +{ + EXPECT_NE(meters(std::numeric_limits::quiet_NaN()), std::numeric_limits>::quiet_NaN()); + EXPECT_NE(seconds(std::numeric_limits::quiet_NaN()), std::numeric_limits>::quiet_NaN()); + EXPECT_TRUE(units::isnan(std::numeric_limits>::quiet_NaN())); +} + +TEST_F(UnitLimits, UnitSignalingNaN) +{ + EXPECT_NE(meters(std::numeric_limits::signaling_NaN()), std::numeric_limits>::signaling_NaN()); + EXPECT_NE(seconds(std::numeric_limits::signaling_NaN()), std::numeric_limits>::signaling_NaN()); + EXPECT_TRUE(units::isnan(std::numeric_limits>::signaling_NaN())); +} + +TEST_F(UnitLimits, UnitIsSpecialized) +{ + EXPECT_TRUE(std::numeric_limits::is_specialized == std::numeric_limits>::is_specialized); + EXPECT_TRUE(std::numeric_limits::is_specialized == std::numeric_limits>::is_specialized); +} + +TEST_F(UnitLimits, UnitIsSigned) +{ + EXPECT_TRUE(std::numeric_limits::is_signed == std::numeric_limits>::is_signed); + EXPECT_TRUE(std::numeric_limits::is_signed == std::numeric_limits>::is_signed); +} + +TEST_F(UnitLimits, UnitIsInteger) +{ + EXPECT_TRUE(std::numeric_limits::is_integer == std::numeric_limits>::is_integer); + EXPECT_TRUE(std::numeric_limits::is_integer == std::numeric_limits>::is_integer); +} + +TEST_F(UnitLimits, UnitIsExact) +{ + EXPECT_TRUE(std::numeric_limits::is_exact == std::numeric_limits>::is_exact); + EXPECT_TRUE(std::numeric_limits::is_exact == std::numeric_limits>::is_exact); +} + +TEST_F(UnitLimits, UnitHasInifinity) +{ + EXPECT_TRUE(std::numeric_limits::has_infinity == std::numeric_limits>::has_infinity); + EXPECT_TRUE(std::numeric_limits::has_infinity == std::numeric_limits>::has_infinity); +} + +TEST_F(UnitLimits, UnitHasQuietNaN) +{ + EXPECT_TRUE(std::numeric_limits::has_quiet_NaN == std::numeric_limits>::has_quiet_NaN); + EXPECT_TRUE(std::numeric_limits::has_quiet_NaN == std::numeric_limits>::has_quiet_NaN); +} + +TEST_F(UnitLimits, UnitHasSignalingNaN) +{ + EXPECT_TRUE(std::numeric_limits::has_signaling_NaN == std::numeric_limits>::has_signaling_NaN); + EXPECT_TRUE(std::numeric_limits::has_signaling_NaN == std::numeric_limits>::has_signaling_NaN); +} + TEST_F(CaseStudies, radarRangeEquation) { watts<> P_t; // transmit power From e14ab7737a55402d4dbe641599d5312ef383dfbf Mon Sep 17 00:00:00 2001 From: Nic Holthaus Date: Mon, 9 Dec 2024 16:15:33 -0500 Subject: [PATCH 06/48] fix: undef pascal on mingw when adding pressure units --- include/units/pressure.h | 9 +++++++++ unitTests/main.cpp | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/include/units/pressure.h b/include/units/pressure.h index 321528e1..ff4e6c37 100644 --- a/include/units/pressure.h +++ b/include/units/pressure.h @@ -51,6 +51,11 @@ #undef pascal #endif // _MSC_VER +#if defined(__MINGW64__) || defined(__MINGW32__) +# pragma push_macro("pascal") +# undef pascal +#endif // __MINGW64__ or __MINGW32__ + #include #include @@ -79,4 +84,8 @@ namespace units #pragma pop_macro("pascal") #endif // _MSC_VER +#if defined(__MINGW64__) || defined(__MINGW32__) +# pragma pop_macro("pascal") +#endif // __MINGW64__ or __MINGW32__ + #endif // units_pressure_h__ diff --git a/unitTests/main.cpp b/unitTests/main.cpp index b51b29e3..46933a35 100644 --- a/unitTests/main.cpp +++ b/unitTests/main.cpp @@ -2054,6 +2054,9 @@ TEST_F(UnitType, unitTypeMultiplication) EXPECT_NEAR(0.2, result, 5.0e-5); result = 4 * percent(5.0); EXPECT_NEAR(0.2, result, 5.0e-5); + + auto value = 10.0_pct * 100.0_m; + EXPECT_EQ(value, 10.0_m); } TEST_F(UnitType, unitTypeMixedUnitMultiplication) @@ -4759,6 +4762,10 @@ TEST_F(UnitMath, sqrt) EXPECT_NEAR(feet(3.16227766017).to(), sqrt(square_feet(10.0)).to(), 5.0e-9); EXPECT_NEAR(feet(3.16227766017).to(), resultFt.to(), 5.0e-9); EXPECT_EQ(resultFt, sqrt(square_feet(10.0))); + + percent resultPct = sqrt(16.0_pct); + EXPECT_EQ(resultPct, 4.0_pct); + EXPECT_EQ(0.04, resultPct); } TEST_F(UnitMath, hypot) From 1c0edfc240fa2edbc14c3aeddf9362a2808ffa8f Mon Sep 17 00:00:00 2001 From: Guillaume Giraud Date: Tue, 4 Apr 2023 10:19:14 +0200 Subject: [PATCH 07/48] MSVC-specific Empty Baseclass Optimization activation. Ensures sizof(unit type) is the same as sizeof(underlying_type). --- include/units/core.h | 9 ++++++++- unitTests/main.cpp | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/units/core.h b/include/units/core.h index 76fbec31..51826715 100644 --- a/include/units/core.h +++ b/include/units/core.h @@ -2133,8 +2133,15 @@ namespace units * - \ref concentrationUnits "concentration units" * - \ref constantUnits "constant units" */ +#ifdef _WIN32 +// Microsoft compiler requires explicit activation of empty base class optimization +// so that sizeof(unit<..., double, ...>) == sizeof(double) +#define MSVC_EBO __declspec(empty_bases) +#else +#define MSVC_EBO +#endif template - class unit : public ConversionFactor, NumericalScale, units::detail::_unit + class MSVC_EBO unit : public ConversionFactor, NumericalScale, units::detail::_unit { static_assert(traits::is_conversion_factor_v, "Template parameter `ConversionFactor` must be a conversion factor. Check that you aren't using an unit " diff --git a/unitTests/main.cpp b/unitTests/main.cpp index b51b29e3..b306e35c 100644 --- a/unitTests/main.cpp +++ b/unitTests/main.cpp @@ -116,9 +116,15 @@ namespace }; } // namespace +TEST_F(TypeTraits, sizeOf) { + static_assert(sizeof(dimensionless) == sizeof(double)); + static_assert(sizeof(meters) == sizeof(double)); + static_assert(sizeof(degrees_squared) == sizeof(double)); +} + TEST_F(TypeTraits, isRatio) { - EXPECT_TRUE(traits::is_ratio_v>); + EXPECT_TRUE(traits::is_ratio_v>); EXPECT_FALSE(traits::is_ratio_v); } From bf371c42dec8254958b4cc63d531bee874ba99b6 Mon Sep 17 00:00:00 2001 From: Nic Holthaus Date: Mon, 9 Dec 2024 16:20:12 -0500 Subject: [PATCH 08/48] refactor: cland-format --- unitTests/main.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/unitTests/main.cpp b/unitTests/main.cpp index def255b9..85ec800c 100644 --- a/unitTests/main.cpp +++ b/unitTests/main.cpp @@ -116,15 +116,16 @@ namespace }; } // namespace -TEST_F(TypeTraits, sizeOf) { - static_assert(sizeof(dimensionless) == sizeof(double)); - static_assert(sizeof(meters) == sizeof(double)); - static_assert(sizeof(degrees_squared) == sizeof(double)); +TEST_F(TypeTraits, sizeOf) +{ + static_assert(sizeof(dimensionless) == sizeof(double)); + static_assert(sizeof(meters) == sizeof(double)); + static_assert(sizeof(degrees_squared) == sizeof(double)); } TEST_F(TypeTraits, isRatio) { - EXPECT_TRUE(traits::is_ratio_v>); + EXPECT_TRUE(traits::is_ratio_v>); EXPECT_FALSE(traits::is_ratio_v); } From 7c484aa04cc82ed51b286aa7ff4be05359346979 Mon Sep 17 00:00:00 2001 From: Nic Holthaus Date: Mon, 9 Dec 2024 16:24:50 -0500 Subject: [PATCH 09/48] Fix: compilation error when using units::sqrt with a float underlying type courtesy of https://github.com/Guillaume227 --- include/units/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/units/core.h b/include/units/core.h index 51826715..f2cf8f81 100644 --- a/include/units/core.h +++ b/include/units/core.h @@ -1627,7 +1627,7 @@ namespace units template, int> = 0> constexpr T sqrtNewtonRaphson(T x, T curr, T prev) { - return curr == prev ? curr : sqrtNewtonRaphson(x, 0.5 * (curr + x / curr), curr); + return curr == prev ? curr : sqrtNewtonRaphson(x, T{0.5} * (curr + x / curr), curr); } } // namespace Detail /** @endcond */ // END DOXYGEN IGNORE From 447d700cbe0150ca815223ee40329647fe519629 Mon Sep 17 00:00:00 2001 From: Alex Wang Date: Fri, 15 Sep 2023 08:34:52 -0400 Subject: [PATCH 10/48] Fix mil definition A mil was defined as 1000 inches instead of 1/1000 inches. --- include/units/length.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/units/length.h b/include/units/length.h index bee8bba6..2cf275b7 100644 --- a/include/units/length.h +++ b/include/units/length.h @@ -61,7 +61,7 @@ namespace units UNIT_ADD_WITH_METRIC_PREFIXES(length, meters, m, conversion_factor, dimension::length>) UNIT_ADD(length, feet, ft, conversion_factor, meters<>>) UNIT_ADD(length, inches, in, conversion_factor, feet<>>) - UNIT_ADD(length, mils, mil, conversion_factor, inches<>>) + UNIT_ADD(length, mils, mil, conversion_factor, inches<>>) UNIT_ADD(length, miles, mi, conversion_factor, feet<>>) UNIT_ADD(length, nautical_miles, nmi, conversion_factor, meters<>>) UNIT_ADD(length, astronomical_units, au, conversion_factor, meters<>>) From 8968b80a68721f5f45a6abfe86aa8c2aa6a1b95b Mon Sep 17 00:00:00 2001 From: Nic Holthaus Date: Mon, 9 Dec 2024 16:39:12 -0500 Subject: [PATCH 11/48] feat: fix mils definition and add mils unit test --- unitTests/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/unitTests/main.cpp b/unitTests/main.cpp index 85ec800c..2d6d9ba4 100644 --- a/unitTests/main.cpp +++ b/unitTests/main.cpp @@ -3360,6 +3360,10 @@ TEST_F(ConversionFactor, length) EXPECT_NEAR(17702.8, test, 5.0e-2); test = chains(meters(1.0)).value(); EXPECT_NEAR(0.0497097, test, 5.0e-7); + test = inches(mils(1.0)).value(); + EXPECT_NEAR(0.001, test, 5.0e-7); + test = mils(inches(1.0)).value(); + EXPECT_NEAR(1000, test, 5.0e-7); EXPECT_EQ(metres(1), meters(1)); } From 35b484f63370c6b6d993158219f639244134a557 Mon Sep 17 00:00:00 2001 From: Guillaume Giraud Date: Mon, 9 Dec 2024 22:51:28 +0100 Subject: [PATCH 12/48] -Wshadow compilation warning fix - use -Wshadow for compiling tests (#318) Co-authored-by: Nic Holthaus --- include/units/core.h | 10 +++++----- unitTests/CMakeLists.txt | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/units/core.h b/include/units/core.h index f2cf8f81..b987ada1 100644 --- a/include/units/core.h +++ b/include/units/core.h @@ -1765,20 +1765,20 @@ namespace units std::ratio_divide, typename ConversionFactorTo::conversion_ratio>; - [[maybe_unused]] constexpr auto normal_convert = [](const auto& value) + [[maybe_unused]] constexpr auto normal_convert = [](const auto& val) { using ResolvedUnitFrom = conversion_factor; using ResolvedUnitTo = conversion_factor; - return convert>(value); + return convert>(val); }; - [[maybe_unused]] constexpr auto pi_convert = [](const auto& value) + [[maybe_unused]] constexpr auto pi_convert = [](const auto& val) { using ResolvedUnitFrom = conversion_factor; using ResolvedUnitTo = conversion_factor; - return convert>(value); + typename ConversionFactorTo::pi_exponent_ratio>; + return convert>(val); }; // same exact unit on both sides diff --git a/unitTests/CMakeLists.txt b/unitTests/CMakeLists.txt index 040bd1ad..f36cba5a 100644 --- a/unitTests/CMakeLists.txt +++ b/unitTests/CMakeLists.txt @@ -34,7 +34,7 @@ SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAN TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${LIBRARIES}) IF(NOT MSVC) - target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wconversion) + target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wconversion -Wshadow) ENDIF() ADD_TEST(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME}) From a72235fcccbe2243aaf5ff2e230575815a27e32e Mon Sep 17 00:00:00 2001 From: Nic Holthaus Date: Tue, 10 Dec 2024 20:35:03 -0500 Subject: [PATCH 13/48] feat: about half of SFINAE is replaced by concepts --- include/units/core.h | 534 +++++++++++++++++++++++-------------------- 1 file changed, 288 insertions(+), 246 deletions(-) diff --git a/include/units/core.h b/include/units/core.h index b987ada1..9d07039b 100644 --- a/include/units/core.h +++ b/include/units/core.h @@ -432,6 +432,7 @@ namespace units { inline constexpr UNIT_LIB_DEFAULT_TYPE PI_VAL = 3.14159265358979323846264338327950288419716939937510; } + /** @endcond */ // END DOXYGEN IGNORE //------------------------------ @@ -495,7 +496,7 @@ namespace units template class Op, class... Args> inline constexpr bool is_detected_convertible_v = is_detected_convertible::value; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE //------------------------------ @@ -521,7 +522,7 @@ namespace units struct is_ratio_impl> : std::true_type { }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE /** @@ -582,21 +583,26 @@ namespace units template struct conversion_factor_traits> + std::void_t> { - using dimension_type = typename T::dimension_type; ///< Unit type that the unit was derived from. May be a `dimension` or - ///< another `conversion_factor`. Use the `dimension_of_t` trait to find the - ///< SI dimension type. This will be `void` if type `T` is not a unit. - using conversion_ratio = typename T::conversion_ratio; ///< `std::ratio` representing the conversion factor to the - ///< `dimension_type`. This will be `void` if type `T` is not a unit. - using pi_exponent_ratio = typename T::pi_exponent_ratio; ///< `std::ratio` representing the exponent of pi to be used in the - ///< conversion. This will be `void` if type `T` is not a unit. - using translation_ratio = typename T::translation_ratio; ///< `std::ratio` representing a datum translation to the dimension (i.e. - ///< degrees C to degrees F conversion). This will be `void` if type `T` - ///< is not a unit. + using dimension_type = typename T::dimension_type; + ///< Unit type that the unit was derived from. May be a `dimension` or + ///< another `conversion_factor`. Use the `dimension_of_t` trait to find the + ///< SI dimension type. This will be `void` if type `T` is not a unit. + using conversion_ratio = typename T::conversion_ratio; + ///< `std::ratio` representing the conversion factor to the + ///< `dimension_type`. This will be `void` if type `T` is not a unit. + using pi_exponent_ratio = typename T::pi_exponent_ratio; + ///< `std::ratio` representing the exponent of pi to be used in the + ///< conversion. This will be `void` if type `T` is not a unit. + using translation_ratio = typename T::translation_ratio; + ///< `std::ratio` representing a datum translation to the dimension (i.e. + ///< degrees C to degrees F conversion). This will be `void` if type `T` + ///< is not a unit. }; + /** @endcond */ // END DOXYGEN IGNORE - } // namespace traits + } // namespace traits /** @cond */ // DOXYGEN IGNORE namespace detail @@ -633,7 +639,8 @@ namespace units * strong type alias of `T`, if any, and `T` otherwise. Otherwise, there is no `type` member. * This may be specialized only if `T` depends on a program-defined type. */ - template && std::is_same_v>>> + template + requires is_conversion_factor_v && std::is_same_v> struct strong : T { typedef T type; @@ -895,31 +902,31 @@ namespace units using angle = make_dimension; ///< Represents a quantity of angle // SI DERIVED UNIT TYPES - using solid_angle = dimension_pow>; ///< Represents an SI derived unit of solid angle - using frequency = make_dimension>; ///< Represents an SI derived unit of frequency - using velocity = dimension_divide; ///< Represents an SI derived unit of velocity - using angular_velocity = dimension_divide; ///< Represents an SI derived unit of angular velocity - using acceleration = dimension_divide; ///< Represents an SI derived unit of acceleration - using force = dimension_multiply; ///< Represents an SI derived unit of force - using area = dimension_pow>; ///< Represents an SI derived unit of area - using pressure = dimension_divide; ///< Represents an SI derived unit of pressure - using charge = dimension_multiply; ///< Represents an SI derived unit of charge - using energy = dimension_multiply; ///< Represents an SI derived unit of energy - using power = dimension_divide; ///< Represents an SI derived unit of power - using voltage = dimension_divide; ///< Represents an SI derived unit of voltage - using capacitance = dimension_divide; ///< Represents an SI derived unit of capacitance - using impedance = dimension_divide; ///< Represents an SI derived unit of impedance - using conductance = dimension_divide; ///< Represents an SI derived unit of conductance - using magnetic_flux = dimension_divide; ///< Represents an SI derived unit of magnetic flux - using inductance = dimension_multiply; ///< Represents an SI derived unit of inductance - using luminous_flux = dimension_multiply; ///< Represents an SI derived unit of luminous flux - using illuminance = make_dimension, length, std::ratio<-2>>; ///< Represents an SI derived unit of illuminance - using luminance = make_dimension, length, std::ratio<-2>>; ///< Represents an SI derived unit of luminance - using radioactivity = make_dimension, time, std::ratio<-2>>; ///< Represents an SI derived unit of radioactivity - using substance_mass = dimension_divide; + using solid_angle = dimension_pow>; ///< Represents an SI derived unit of solid angle + using frequency = make_dimension>; ///< Represents an SI derived unit of frequency + using velocity = dimension_divide; ///< Represents an SI derived unit of velocity + using angular_velocity = dimension_divide; ///< Represents an SI derived unit of angular velocity + using acceleration = dimension_divide; ///< Represents an SI derived unit of acceleration + using force = dimension_multiply; ///< Represents an SI derived unit of force + using area = dimension_pow>; ///< Represents an SI derived unit of area + using pressure = dimension_divide; ///< Represents an SI derived unit of pressure + using charge = dimension_multiply; ///< Represents an SI derived unit of charge + using energy = dimension_multiply; ///< Represents an SI derived unit of energy + using power = dimension_divide; ///< Represents an SI derived unit of power + using voltage = dimension_divide; ///< Represents an SI derived unit of voltage + using capacitance = dimension_divide; ///< Represents an SI derived unit of capacitance + using impedance = dimension_divide; ///< Represents an SI derived unit of impedance + using conductance = dimension_divide; ///< Represents an SI derived unit of conductance + using magnetic_flux = dimension_divide; ///< Represents an SI derived unit of magnetic flux + using inductance = dimension_multiply; ///< Represents an SI derived unit of inductance + using luminous_flux = dimension_multiply; ///< Represents an SI derived unit of luminous flux + using illuminance = make_dimension, length, std::ratio<-2>>; ///< Represents an SI derived unit of illuminance + using luminance = make_dimension, length, std::ratio<-2>>; ///< Represents an SI derived unit of luminance + using radioactivity = make_dimension, time, std::ratio<-2>>; ///< Represents an SI derived unit of radioactivity + using substance_mass = dimension_divide; using substance_concentration = dimension_divide; using magnetic_field_strength = - make_dimension, time, std::ratio<-2>, current, std::ratio<-1>>; ///< Represents an SI derived unit of magnetic field strength + make_dimension, time, std::ratio<-2>, current, std::ratio<-1>>; ///< Represents an SI derived unit of magnetic field strength // OTHER UNIT TYPES using torque = dimension_multiply; ///< Represents an SI derived unit of torque @@ -928,7 +935,7 @@ namespace units using concentration = make_dimension>; ///< Represents a unit of concentration using data = make_dimension; ///< Represents a unit of data size using data_transfer_rate = dimension_divide; ///< Represents a unit of data transfer rate - } // namespace dimension + } // namespace dimension //------------------------------ // CONVERSION FACTOR CLASSES @@ -940,6 +947,7 @@ namespace units */ template struct conversion_factor; + template struct conversion_factor, PiExponent, Translation> : units::detail::_conversion_factor { @@ -956,6 +964,7 @@ namespace units using translation_ratio = Translation; using pi_exponent_ratio = PiExponent; }; + /** @endcond */ // END DOXYGEN IGNORE /** @cond */ // DOXYGEN IGNORE @@ -994,7 +1003,7 @@ namespace units { using type = void; }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE namespace traits @@ -1028,7 +1037,7 @@ namespace units template struct has_dimension_of_impl, Dim, true> - : std::is_same::dimension_type, Dim>::type + : std::is_same::dimension_type, Dim>::type { }; @@ -1036,7 +1045,7 @@ namespace units struct has_dimension_of_impl, Dim> : std::is_same, Dim>::type { }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE namespace traits @@ -1166,7 +1175,7 @@ namespace units std::ratio_multiply::pi_exponent_ratio, std::ratio<-1>>, std::ratio<0>>; // inverses are rates or change, the translation factor goes away. }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE /** @@ -1192,10 +1201,10 @@ namespace units static_assert(traits::is_conversion_factor_v, "Template parameter `Unit` must be a `unit` type."); using Conversion = typename Unit::conversion_ratio; using type = conversion_factor, - dimension_pow, std::ratio<2>>, - std::ratio_multiply>, typename Unit::translation_ratio>; + dimension_pow, std::ratio<2>>, + std::ratio_multiply>, typename Unit::translation_ratio>; }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE /** @@ -1221,10 +1230,10 @@ namespace units static_assert(traits::is_conversion_factor_v, "Template parameter `Unit` must be a `unit` type."); using Conversion = typename Unit::conversion_ratio; using type = conversion_factor>, - dimension_pow, std::ratio<3>>, - std::ratio_multiply>, typename Unit::translation_ratio>; + dimension_pow, std::ratio<3>>, + std::ratio_multiply>, typename Unit::translation_ratio>; }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE /** @@ -1416,7 +1425,7 @@ namespace units using type = conversion_factor, dimension_root, std::ratio<2>>, std::ratio_divide>, typename Unit::translation_ratio>; }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE /** @@ -1457,16 +1466,18 @@ namespace units */ template struct compound_impl; + template struct compound_impl { using type = U; }; + template struct compound_impl : compound_impl, Us...> { }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE /** @@ -1515,7 +1526,7 @@ namespace units { using type = U; }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE // clang-format off @@ -1577,7 +1588,7 @@ namespace units */ template struct is_same_dimension_conversion_factor - : std::conjunction, is_conversion_factor, + : std::conjunction, is_conversion_factor, std::is_same::dimension_type>, traits::dimension_of_t::dimension_type>>> { @@ -1624,23 +1635,26 @@ namespace units namespace Detail { - template, int> = 0> + template + requires std::is_floating_point_v constexpr T sqrtNewtonRaphson(T x, T curr, T prev) { return curr == prev ? curr : sqrtNewtonRaphson(x, T{0.5} * (curr + x / curr), curr); } - } // namespace Detail + } // namespace Detail /** @endcond */ // END DOXYGEN IGNORE - template, int> = 0> + template + requires std::is_arithmetic_v constexpr detail::floating_point_promotion_t sqrt(T x_) { using FloatingPoint = detail::floating_point_promotion_t; const FloatingPoint x(x_); - return x >= 0 && x < std::numeric_limits::infinity() ? Detail::sqrtNewtonRaphson(x, x, FloatingPoint(0)) - : std::numeric_limits::quiet_NaN(); + return x >= 0 && x < std::numeric_limits::infinity() + ? Detail::sqrtNewtonRaphson(x, x, FloatingPoint(0)) + : std::numeric_limits::quiet_NaN(); } /** @cond */ // DOXYGEN IGNORE @@ -1662,10 +1676,11 @@ namespace units return pow_acc<(Exp - 1) / 2>(acc * base, base * base); } } - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE - template, int> = 0> + template + requires std::is_arithmetic_v constexpr detail::floating_point_promotion_t pow(B base) noexcept { using promoted_t = detail::floating_point_promotion_t; @@ -1694,17 +1709,19 @@ namespace units } return pow_acc(acc * x, x * x, (y - 1) / 2); } - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE - template, std::is_unsigned>, int> = 0> + template + requires std::is_arithmetic_v && std::is_unsigned_v constexpr detail::floating_point_promotion_t pow(T1 x, T2 y) noexcept { using promoted_t = detail::floating_point_promotion_t; return detail::pow_acc(static_cast(1.0), static_cast(x), y); } - template, std::is_signed>, int> = 0> + template + requires std::is_arithmetic_v && std::is_signed_v constexpr detail::floating_point_promotion_t pow(T1 x, T2 y) noexcept { if (y >= 0) @@ -1714,7 +1731,8 @@ namespace units return 1 / (x * pow(x, static_cast(-(y + 1)))); } - template, int> = 0> + template + requires std::is_arithmetic_v constexpr T abs(T x) { return x < 0 ? -x : x; @@ -1732,6 +1750,7 @@ namespace units { explicit linearized_value_t() = default; }; + inline constexpr linearized_value_t linearized_value{}; /** @@ -1753,14 +1772,13 @@ namespace units * @returns value, converted from units of `ConverionFactorFrom` to `ConverionFactorTo`. * The value represents a quantity in units of `ConverionFactorTo`. */ - template && std::is_arithmetic_v && - std::is_arithmetic_v, - int> = 0> + template + requires traits::is_same_dimension_conversion_factor_v && std::is_arithmetic_v && std::is_arithmetic_v< + From> constexpr To convert(const From& value) noexcept { - using Ratio = std::ratio_divide; - using PiRatio = std::ratio_subtract; + using Ratio = std::ratio_divide; + using PiRatio = std::ratio_subtract; using Translation = std::ratio_divide, typename ConversionFactorTo::conversion_ratio>; @@ -1776,8 +1794,8 @@ namespace units { using ResolvedUnitFrom = conversion_factor; - using ResolvedUnitTo = conversion_factor; + using ResolvedUnitTo = conversion_factor; return convert>(val); }; @@ -1863,7 +1881,7 @@ namespace units { template struct is_same_dimension_unit; - } // namespace traits + } // namespace traits /** @endcond */ // END DOXYGEN IGNORE /** @@ -1878,7 +1896,8 @@ namespace units * @tparam UnitTo unit to convert `from` to. `is_unit_v` shall be `true`. * @returns from, converted from units of `UnitFrom` to `UnitTo`. */ - template::value, int> = 0> + template + requires traits::is_same_dimension_unit::value constexpr UnitTo convert(const UnitFrom& from) noexcept { return UnitTo(convert(from.to_linearized()), @@ -1897,10 +1916,11 @@ namespace units template struct invocable_scale { - template, int> = 0> + template + requires std::is_same_v decltype(NumericalScale::scale(T{})) operator()(T); }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE /** @@ -1973,8 +1993,9 @@ namespace units using value_type = typename T::value_type; using conversion_factor = typename T::conversion_factor; }; + /** @endcond */ // END DOXYGEN IGNORE - } // namespace traits + } // namespace traits namespace traits { @@ -1989,15 +2010,15 @@ namespace units * @sa is_same_dimension_conversion_factor */ template - struct is_same_dimension_unit : std::conjunction, is_unit, - is_same_dimension_conversion_factor::conversion_factor, - typename units::traits::unit_traits::conversion_factor>> + struct is_same_dimension_unit + : std::conjunction, is_unit, + is_same_dimension_conversion_factor::conversion_factor, + typename units::traits::unit_traits::conversion_factor>> { }; template inline constexpr bool is_same_dimension_unit_v = is_same_dimension_unit::value; - } // namespace traits //---------------------------------- @@ -2054,7 +2075,7 @@ namespace units struct _unit { }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE namespace traits @@ -2134,8 +2155,8 @@ namespace units * - \ref constantUnits "constant units" */ #ifdef _WIN32 -// Microsoft compiler requires explicit activation of empty base class optimization -// so that sizeof(unit<..., double, ...>) == sizeof(double) + // Microsoft compiler requires explicit activation of empty base class optimization + // so that sizeof(unit<..., double, ...>) == sizeof(double) #define MSVC_EBO __declspec(empty_bases) #else #define MSVC_EBO @@ -2174,7 +2195,8 @@ namespace units */ template requires(!traits::is_dimensionless_unit::value && detail::is_losslessly_convertible) - explicit constexpr unit(Ty value) noexcept : linearized_value(NumericalScale::linearize(static_cast(value))) + explicit constexpr unit(Ty value) noexcept + : linearized_value(NumericalScale::linearize(static_cast(value))) { } @@ -2185,7 +2207,8 @@ namespace units */ template requires detail::is_losslessly_convertible - explicit constexpr unit(Ty value, linearized_value_t) noexcept : linearized_value(value) + explicit constexpr unit(Ty value, linearized_value_t) noexcept + : linearized_value(value) { } @@ -2196,7 +2219,8 @@ namespace units */ template requires traits::is_dimensionless_unit::value && detail::is_losslessly_convertible - constexpr unit(Ty value) noexcept : linearized_value(NumericalScale::linearize(static_cast(value))) + constexpr unit(Ty value) noexcept + : linearized_value(NumericalScale::linearize(static_cast(value))) { } @@ -2208,7 +2232,7 @@ namespace units template requires detail::is_time_conversion_factor && detail::is_losslessly_convertible constexpr unit(const std::chrono::duration& value) noexcept - : linearized_value(units::convert(units::unit, Rep>(value.count())).linearized_value) + : linearized_value(units::convert(units::unit, Rep>(value.count())).linearized_value) { } @@ -2219,7 +2243,8 @@ namespace units */ template requires detail::is_losslessly_convertible_unit, unit> - constexpr unit(const unit& rhs) noexcept : linearized_value(units::convert(rhs).linearized_value) + constexpr unit(const unit& rhs) noexcept + : linearized_value(units::convert(rhs).linearized_value) { } @@ -2360,10 +2385,10 @@ namespace units { if constexpr (traits::is_dimensionless_unit::value) return NumericalScale::scale(units::convert, units::dimension::dimensionless, - typename traits::conversion_factor_traits::pi_exponent_ratio, - typename traits::conversion_factor_traits::translation_ratio>, + typename traits::conversion_factor_traits::pi_exponent_ratio, + typename traits::conversion_factor_traits::translation_ratio>, T, NumericalScale>>(*this) - .to_linearized()); + .to_linearized()); else return raw(); } @@ -2636,9 +2661,9 @@ namespace units */ template using ratio_gcd = std::ratio; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE -} // end namespace units +} // end namespace units //------------------------------ // std::common_type @@ -2656,40 +2681,40 @@ namespace std */ template struct common_type, units::unit> - : std::enable_if, + : std::enable_if, units::unit, - units::traits::dimension_of_t, - units::detail::ratio_gcd, - units::detail::ratio_gcd>>, + units::detail::ratio_gcd, + units::traits::dimension_of_t, + units::detail::ratio_gcd, + units::detail::ratio_gcd>>, common_type_t, NumericalScale>> { }; template struct common_type, T, NumericalScale>, chrono::duration> - : std::common_type, T, NumericalScale>, decltype(units::unit{chrono::duration{}})> + : std::common_type, T, NumericalScale>, decltype(units::unit{chrono::duration{}})> { }; template struct common_type, units::unit> - : std::common_type, chrono::duration> + : std::common_type, chrono::duration> { }; template struct common_type> - : std::enable_if && units::traits::is_dimensionless_unit>::value, + : std::enable_if && units::traits::is_dimensionless_unit>::value, units::unit, units::dimension::dimensionless>, common_type_t, NumericalScale>> { }; template struct common_type, Ty> - : std::enable_if && units::traits::is_dimensionless_unit>::value, + : std::enable_if && units::traits::is_dimensionless_unit>::value, units::unit, units::dimension::dimensionless>, - /*units::detail::floating_point_promotion_t<*/ common_type_t /*>*/, NumericalScale>> + common_type_t, NumericalScale>> { }; @@ -2698,18 +2723,19 @@ namespace std * @brief `linear_scale` preferring specializations. */ template - struct common_type, units::unit> - : common_type, units::unit> + struct common_type, units::unit> + : common_type, units::unit> { }; template - struct common_type, units::unit> - : common_type, units::unit> + struct common_type, units::unit> + : common_type, units::unit> { }; + /** @endcond */ // END DOXYGEN IGNORE -} // namespace std +} // namespace std namespace units { @@ -2732,7 +2758,8 @@ namespace units * @sa unit::to */ template - constexpr std::enable_if_t && traits::is_unit_v, T> unit_cast(const UnitType& value) noexcept + requires std::is_arithmetic_v && traits::is_unit_v + constexpr T unit_cast(const UnitType& value) noexcept { return static_cast(value); } @@ -2773,7 +2800,6 @@ namespace units template inline constexpr bool has_decibel_scale_v = has_decibel_scale::value; - } // namespace traits //---------------------------------- @@ -2859,61 +2885,67 @@ namespace units template using type_identity_t = typename type_identity::type; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE - template, int> = 0> + template + requires traits::is_unit_v constexpr UnitTypeLhs& operator+=(UnitTypeLhs& lhs, const detail::type_identity_t& rhs) noexcept { lhs = lhs + rhs; return lhs; } - template && !traits::is_unit_v, int> = 0> + template + requires (traits::is_unit_v && !traits::is_unit_v) constexpr UnitTypeLhs& operator+=(UnitTypeLhs& lhs, T rhs) noexcept { lhs = lhs + rhs; return lhs; } - template, int> = 0> + template + requires traits::is_unit_v constexpr UnitTypeLhs& operator-=(UnitTypeLhs& lhs, const detail::type_identity_t& rhs) noexcept { lhs = lhs - rhs; return lhs; } - template && !traits::is_unit_v, int> = 0> + template + requires (traits::is_unit_v && !traits::is_unit_v) constexpr UnitTypeLhs& operator-=(UnitTypeLhs& lhs, const T& rhs) noexcept { lhs = lhs - rhs; return lhs; } - template, int> = 0> + template + requires traits::is_unit_v constexpr UnitTypeLhs& operator*=(UnitTypeLhs& lhs, const typename UnitTypeLhs::underlying_type& rhs) noexcept { lhs = lhs * rhs; return lhs; } - template, int> = 0> + template + requires traits::is_unit_v constexpr UnitTypeLhs& operator/=(UnitTypeLhs& lhs, const typename UnitTypeLhs::underlying_type& rhs) noexcept { lhs = lhs / rhs; return lhs; } - template && !traits::is_dimensionless_unit_v, int> = 0> + template + requires (traits::is_unit_v && !traits::is_dimensionless_unit_v) constexpr UnitTypeLhs& operator%=(UnitTypeLhs& lhs, const detail::type_identity_t& rhs) noexcept { lhs = lhs % rhs; return lhs; } - template && traits::is_dimensionless_unit_v && traits::is_dimensionless_unit_v, int> = - 0> + template + requires traits::is_unit_v && traits::is_dimensionless_unit_v && traits::is_dimensionless_unit_v constexpr UnitTypeLhs& operator%=(UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept { using CommonUnit = decltype(lhs % rhs); @@ -2923,7 +2955,8 @@ namespace units return lhs; } - template, int> = 0> + template + requires traits::is_unit_v constexpr UnitTypeLhs& operator%=(UnitTypeLhs& lhs, const typename UnitTypeLhs::underlying_type& rhs) noexcept { lhs = lhs % rhs; @@ -2935,14 +2968,16 @@ namespace units //------------------------------ // unary addition: +T - template, int> = 0> + template + requires traits::is_unit_v constexpr UnitTypeLhs operator+(const UnitTypeLhs& u) noexcept { return u; } // prefix increment: ++T - template, int> = 0> + template + requires traits::is_unit_v constexpr UnitTypeLhs& operator++(UnitTypeLhs& u) noexcept { u = UnitTypeLhs(u.raw() + 1); @@ -2950,7 +2985,8 @@ namespace units } // postfix increment: T++ - template, int> = 0> + template + requires traits::is_unit_v constexpr UnitTypeLhs operator++(UnitTypeLhs& u, int) noexcept { auto ret = u; @@ -2959,14 +2995,16 @@ namespace units } // unary addition: -T - template, int> = 0> + template + requires traits::is_unit_v constexpr UnitTypeLhs operator-(const UnitTypeLhs& u) noexcept { return UnitTypeLhs(-u.raw()); } // prefix increment: --T - template, int> = 0> + template + requires traits::is_unit_v constexpr UnitTypeLhs& operator--(UnitTypeLhs& u) noexcept { u = UnitTypeLhs(u.raw() - 1); @@ -2974,7 +3012,8 @@ namespace units } // postfix increment: T-- - template, int> = 0> + template + requires traits::is_unit_v constexpr UnitTypeLhs operator--(UnitTypeLhs& u, int) noexcept { auto ret = u; @@ -2987,8 +3026,8 @@ namespace units //------------------------------ /// Addition operator for unit types with a linear_scale. - template && traits::has_linear_scale_v, int> = 0> + template + requires traits::is_same_dimension_unit_v && traits::has_linear_scale_v constexpr std::common_type_t operator+(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept { using CommonUnit = std::common_type_t; @@ -2997,8 +3036,8 @@ namespace units /// Addition operator for dimensionless unit types with a linear_scale. dimensionless types can be implicitly /// converted to built-in types. - template && traits::has_linear_scale_v && traits::is_dimensionless_unit_v, int> = 0> + template + requires std::is_arithmetic_v && traits::has_linear_scale_v && traits::is_dimensionless_unit_v constexpr traits::replace_underlying_t> operator+( const UnitTypeLhs& lhs, T rhs) noexcept { @@ -3011,8 +3050,8 @@ namespace units /// Addition operator for dimensionless unit types with a linear_scale. dimensionless types can be implicitly /// converted to built-in types. - template && traits::has_linear_scale_v && traits::is_dimensionless_unit_v, int> = 0> + template + requires std::is_arithmetic_v && traits::has_linear_scale_v && traits::is_dimensionless_unit_v constexpr traits::replace_underlying_t> operator+( T lhs, const UnitTypeRhs& rhs) noexcept { @@ -3024,8 +3063,8 @@ namespace units } /// Subtraction operator for unit types with a linear_scale. - template && traits::has_linear_scale_v, int> = 0> + template + requires traits::is_same_dimension_unit_v && traits::has_linear_scale_v constexpr std::common_type_t operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept { using CommonUnit = decltype(lhs - rhs); @@ -3034,8 +3073,8 @@ namespace units /// Subtraction operator for dimensionless unit types with a linear_scale. dimensionless types can be implicitly /// converted to built-in types. - template && traits::has_linear_scale_v && traits::is_dimensionless_unit_v, int> = 0> + template + requires std::is_arithmetic_v && traits::has_linear_scale_v && traits::is_dimensionless_unit_v constexpr traits::replace_underlying_t> operator-( const UnitTypeLhs& lhs, T rhs) noexcept { @@ -3048,8 +3087,8 @@ namespace units /// Subtraction operator for dimensionless unit types with a linear_scale. dimensionless types can be implicitly /// converted to built-in types. - template && traits::has_linear_scale_v && traits::is_dimensionless_unit_v, int> = 0> + template + requires std::is_arithmetic_v && traits::has_linear_scale_v && traits::is_dimensionless_unit_v constexpr traits::replace_underlying_t> operator-( T lhs, const UnitTypeRhs& rhs) noexcept { @@ -3062,8 +3101,8 @@ namespace units /// Multiplication type for convertible unit types with a linear scale. @returns the multiplied value, with the same /// type as left-hand side unit. - template && traits::has_linear_scale_v, int> = 0> + template + requires traits::is_same_dimension_unit_v && traits::has_linear_scale_v constexpr auto operator*(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept -> unit>::conversion_factor>>, typename std::common_type_t::underlying_type> @@ -3075,13 +3114,12 @@ namespace units /// Multiplication type for non-convertible unit types with a linear scale. @returns the multiplied value, whose /// type is a compound unit of the left and right hand side values. - template && traits::has_linear_scale_v && - !traits::is_dimensionless_unit_v && !traits::is_dimensionless_unit_v, - int> = 0> + template + requires (!traits::is_same_dimension_unit_v && traits::has_linear_scale_v && + !traits::is_dimensionless_unit_v && !traits::is_dimensionless_unit_v) constexpr auto operator*(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept -> unit::conversion_factor, - typename units::traits::unit_traits::conversion_factor>>, + typename units::traits::unit_traits::conversion_factor>>, std::common_type_t> { using CompoundUnit = decltype(lhs * rhs); @@ -3090,10 +3128,9 @@ namespace units } /// Multiplication by a dimensionless unit for unit types with a linear scale. - template && !traits::is_dimensionless_unit_v && - traits::is_dimensionless_unit_v, - int> = 0> + template + requires (traits::has_linear_scale_v && !traits::is_dimensionless_unit_v && + traits::is_dimensionless_unit_v) constexpr traits::replace_underlying_t> operator*(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept { @@ -3103,10 +3140,9 @@ namespace units } /// Multiplication by a dimensionless unit for unit types with a linear scale. - template && traits::is_dimensionless_unit_v && - !traits::is_dimensionless_unit_v, - int> = 0> + template + requires (traits::has_linear_scale_v && traits::is_dimensionless_unit_v && + !traits::is_dimensionless_unit_v) constexpr traits::replace_underlying_t> operator*(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept { @@ -3116,8 +3152,8 @@ namespace units } /// Multiplication by an arithmetic type for dimensioned unit types with a linear scale. - template && traits::has_linear_scale_v && !traits::is_dimensionless_unit_v, int> = 0> + template + requires (std::is_arithmetic_v && traits::has_linear_scale_v && !traits::is_dimensionless_unit_v) constexpr traits::replace_underlying_t> operator*( const UnitTypeLhs& lhs, T rhs) noexcept { @@ -3126,8 +3162,8 @@ namespace units } /// Multiplication by an arithmetic type for dimensioned unit types with a linear scale. - template && traits::has_linear_scale_v && !traits::is_dimensionless_unit_v, int> = 0> + template + requires (std::is_arithmetic_v && traits::has_linear_scale_v && !traits::is_dimensionless_unit_v) constexpr traits::replace_underlying_t> operator*( T lhs, const UnitTypeRhs& rhs) noexcept { @@ -3136,8 +3172,8 @@ namespace units } /// Multiplication by an arithmetic type for dimensionless unit types with a linear scale. - template && traits::has_linear_scale_v && traits::is_dimensionless_unit_v, int> = 0> + template + requires (std::is_arithmetic_v && traits::has_linear_scale_v && traits::is_dimensionless_unit_v) constexpr traits::replace_underlying_t> operator*( const UnitTypeLhs& lhs, T rhs) noexcept { @@ -3146,8 +3182,8 @@ namespace units } /// Multiplication by an arithmetic type for dimensionless unit types with a linear scale. - template && traits::has_linear_scale_v && traits::is_dimensionless_unit_v, int> = 0> + template + requires std::is_arithmetic_v && traits::has_linear_scale_v && traits::is_dimensionless_unit_v constexpr traits::replace_underlying_t> operator*( T lhs, const UnitTypeRhs& rhs) noexcept { @@ -3157,8 +3193,8 @@ namespace units /// Division for convertible unit types with a linear scale. @returns the lhs divided by rhs value, whose type is a /// dimensionless - template && traits::has_linear_scale_v, int> = 0> + template + requires traits::is_same_dimension_unit_v && traits::has_linear_scale_v constexpr dimensionless> operator/( const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept { @@ -3168,13 +3204,12 @@ namespace units /// Division for non-convertible unit types with a linear scale. @returns the lhs divided by the rhs, with a /// compound unit type of lhs/rhs - template && traits::has_linear_scale_v && - !traits::is_dimensionless_unit_v && !traits::is_dimensionless_unit_v, - int> = 0> + template + requires (!traits::is_same_dimension_unit_v && traits::has_linear_scale_v && + !traits::is_dimensionless_unit_v && !traits::is_dimensionless_unit_v) constexpr auto operator/(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept - -> unit::conversion_factor, - inverse::conversion_factor>>>, + -> unit::conversion_factor, + inverse::conversion_factor>>>, std::common_type_t> { using CompoundUnit = decltype(lhs / rhs); @@ -3183,10 +3218,9 @@ namespace units } /// Division by a dimensionless unit for unit types with a linear scale - template && !traits::is_dimensionless_unit_v && - traits::is_dimensionless_unit_v, - int> = 0> + template + requires (traits::has_linear_scale_v && !traits::is_dimensionless_unit_v && + traits::is_dimensionless_unit_v) constexpr traits::replace_underlying_t> operator/(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept { @@ -3196,10 +3230,9 @@ namespace units } /// Division of a dimensionless unit by a unit type with a linear scale - template && traits::is_dimensionless_unit_v && - !traits::is_dimensionless_unit_v, - int> = 0> + template + requires (traits::has_linear_scale_v && traits::is_dimensionless_unit_v && + !traits::is_dimensionless_unit_v) constexpr auto operator/(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept -> unit::conversion_factor>>, std::common_type_t> @@ -3210,7 +3243,8 @@ namespace units } /// Division by a dimensionless for unit types with a linear scale - template && traits::has_linear_scale_v, int> = 0> + template + requires std::is_arithmetic_v && traits::has_linear_scale_v constexpr traits::replace_underlying_t> operator/( const UnitTypeLhs& lhs, T rhs) noexcept { @@ -3219,7 +3253,8 @@ namespace units } /// Division of a dimensionless by a unit type with a linear scale - template && traits::has_linear_scale_v, int> = 0> + template + requires std::is_arithmetic_v && traits::has_linear_scale_v constexpr auto operator/(T lhs, const UnitTypeRhs& rhs) noexcept -> unit::conversion_factor>>, std::common_type_t> @@ -3233,22 +3268,20 @@ namespace units /// Modulo for convertible unit types with a linear scale. @returns the lhs value modulo the rhs value, whose type /// is their common type - template && traits::has_linear_scale_v && - !traits::is_dimensionless_unit_v && !traits::is_dimensionless_unit_v, - int> = 0> - constexpr traits::replace_underlying_t::underlying_type> operator%( - const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept + template + requires (traits::is_same_dimension_unit_v && traits::has_linear_scale_v && + !traits::is_dimensionless_unit_v && !traits::is_dimensionless_unit_v) + constexpr auto operator%(const UnitTypeLhs& lhs, + const UnitTypeRhs& rhs) noexcept -> traits::replace_underlying_t::underlying_type> { using CommonUnit = decltype(lhs % rhs); return CommonUnit(CommonUnit(lhs).raw() % CommonUnit(rhs).raw()); } /// Modulo by a dimensionless for unit types with a linear scale - template && !traits::is_dimensionless_unit_v && - traits::is_dimensionless_unit_v, - int> = 0> + template + requires (traits::has_linear_scale_v && !traits::is_dimensionless_unit_v && + traits::is_dimensionless_unit_v) constexpr traits::replace_underlying_t> operator%(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept { @@ -3259,10 +3292,9 @@ namespace units /// Modulo for two dimensionless unit types with a linear scale. /// @returns the lhs value modulo the rhs value, whose type is their common type - template && traits::has_linear_scale_v && - traits::is_dimensionless_unit_v && traits::is_dimensionless_unit_v, - int> = 0> + template + requires traits::is_same_dimension_unit_v && traits::has_linear_scale_v && + traits::is_dimensionless_unit_v && traits::is_dimensionless_unit_v constexpr traits::replace_underlying_t::underlying_type> operator%( const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept { @@ -3271,7 +3303,8 @@ namespace units } /// Modulo by an arithmetic type for unit types with a linear scale - template && traits::has_linear_scale_v, int> = 0> + template + requires std::is_arithmetic_v && traits::has_linear_scale_v constexpr traits::replace_underlying_t> operator%( const UnitTypeLhs& lhs, const T& rhs) noexcept { @@ -3284,8 +3317,8 @@ namespace units //---------------------------------- template - constexpr std::enable_if_t && std::is_arithmetic_v, bool> operator==( - const T& lhs, const DimensionlessUnit& rhs) noexcept + requires traits::is_dimensionless_unit_v && std::is_arithmetic_v + constexpr bool operator==(const T& lhs, const DimensionlessUnit& rhs) noexcept { using CommonUnderlying = std::common_type_t; @@ -3304,22 +3337,22 @@ namespace units } template - constexpr std::enable_if_t && std::is_arithmetic_v, bool> operator==( - const DimensionlessUnit& lhs, const T& rhs) noexcept + requires traits::is_dimensionless_unit_v && std::is_arithmetic_v + constexpr bool operator==(const DimensionlessUnit& lhs, const T& rhs) noexcept { return rhs == lhs; } template - constexpr std::enable_if_t && std::is_arithmetic_v, bool> operator!=( - const T& lhs, const DimensionlessUnit& rhs) noexcept + requires traits::is_dimensionless_unit_v && std::is_arithmetic_v + constexpr bool operator!=(const T& lhs, const DimensionlessUnit& rhs) noexcept { return !(lhs == rhs); } template - constexpr std::enable_if_t && std::is_arithmetic_v, bool> operator!=( - const DimensionlessUnit& lhs, const T& rhs) noexcept + requires traits::is_dimensionless_unit_v && std::is_arithmetic_v + constexpr bool operator!=(const DimensionlessUnit& lhs, const T& rhs) noexcept { return !(lhs == rhs); } @@ -3423,12 +3456,13 @@ namespace units { typedef U type; }; + template struct power_of_unit<0, U> { typedef units::dimensionless_ type; }; - } // namespace detail + } // namespace detail /** @endcond */ // END DOXYGEN IGNORE /** @@ -3439,7 +3473,7 @@ namespace units * @param[in] value `unit` derived type to raise to the given power * @returns new unit, raised to the given exponent */ - template, int> = 0> + template, int> = 0> constexpr auto pow(const UnitType& value) noexcept -> unit::conversion_factor>::type>, detail::floating_point_promotion_t::underlying_type>, linear_scale> @@ -3511,7 +3545,7 @@ namespace units /// Addition for convertible unit types with a decibel_scale template && traits::has_decibel_scale_v, int> = 0> + std::enable_if_t && traits::has_decibel_scale_v, int> = 0> constexpr auto operator+(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept -> unit>::conversion_factor>>, typename std::common_type_t::underlying_type, decibel_scale> @@ -3525,8 +3559,8 @@ namespace units /// Addition between unit types with a decibel_scale and dimensionless dB units template && !traits::is_dimensionless_unit_v && - traits::is_dimensionless_unit_v, - int> = 0> + traits::is_dimensionless_unit_v, + int> = 0> constexpr traits::replace_underlying_t> operator+(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept { @@ -3537,8 +3571,8 @@ namespace units /// Addition between unit types with a decibel_scale and dimensionless dB units template && traits::is_dimensionless_unit_v && - !traits::is_dimensionless_unit_v, - int> = 0> + !traits::is_dimensionless_unit_v, + int> = 0> constexpr traits::replace_underlying_t> operator+(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept { @@ -3548,7 +3582,7 @@ namespace units /// Subtraction for convertible unit types with a decibel_scale template && traits::has_decibel_scale_v, int> = 0> + std::enable_if_t && traits::has_decibel_scale_v, int> = 0> constexpr auto operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept -> dB::underlying_type> { @@ -3561,8 +3595,8 @@ namespace units /// Subtraction between unit types with a decibel_scale and dimensionless dB units template && !traits::is_dimensionless_unit_v && - traits::is_dimensionless_unit_v, - int> = 0> + traits::is_dimensionless_unit_v, + int> = 0> constexpr traits::replace_underlying_t> operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept { @@ -3573,8 +3607,8 @@ namespace units /// Subtraction between unit types with a decibel_scale and dimensionless dB units template && traits::is_dimensionless_unit_v && - !traits::is_dimensionless_unit_v, - int> = 0> + !traits::is_dimensionless_unit_v, + int> = 0> constexpr auto operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept -> unit::conversion_factor>>, std::common_type_t, decibel_scale> @@ -3606,14 +3640,14 @@ namespace units // MIN/MAX FUNCTIONS //---------------------------------- - template, int> = 0> + template, int> = 0> constexpr std::common_type_t min(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) { using CommonUnit = decltype(units::min(lhs, rhs)); return (lhs < rhs ? CommonUnit(lhs) : CommonUnit(rhs)); } - template, int> = 0> + template, int> = 0> constexpr std::common_type_t max(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) { using CommonUnit = decltype(units::max(lhs, rhs)); @@ -3637,7 +3671,7 @@ namespace units * function returns HUGE_VAL (or HUGE_VALF or HUGE_VALL) with the proper sign, and an overflow range * error occurs */ - template, int> = 0> + template, int> = 0> dimensionless> exp(const dimensionlessUnit x) noexcept { return std::exp(x.value()); @@ -3652,7 +3686,7 @@ namespace units * @sa log10 for more common base-10 logarithms * @returns Natural logarithm of x. */ - template, int> = 0> + template, int> = 0> dimensionless> log(const dimensionlessUnit x) noexcept { return std::log(x.value()); @@ -3666,7 +3700,7 @@ namespace units * domain error occurs. * @returns Common logarithm of x. */ - template, int> = 0> + template, int> = 0> dimensionless> log10(const dimensionlessUnit x) noexcept { return std::log10(x.value()); @@ -3684,7 +3718,7 @@ namespace units * @returns The fractional part of x, with the same sign. */ template && std::is_floating_point_v, int> = 0> + std::enable_if_t && std::is_floating_point_v, int> = 0> dimensionless modf(const dimensionlessUnit x, dimensionlessUnit* intpart) noexcept { typename dimensionlessUnit::underlying_type intp; @@ -3700,7 +3734,7 @@ namespace units * @param[in] x Value of the exponent. * @returns 2 raised to the power of x. */ - template, int> = 0> + template, int> = 0> dimensionless> exp2(const dimensionlessUnit x) noexcept { return std::exp2(x.value()); @@ -3714,7 +3748,7 @@ namespace units * @param[in] x Value of the exponent. * @returns e raised to the power of x, minus one. */ - template, int> = 0> + template, int> = 0> dimensionless> expm1(const dimensionlessUnit x) noexcept { return std::expm1(x.value()); @@ -3729,7 +3763,7 @@ namespace units * domain error occurs. * @returns The natural logarithm of (1+x). */ - template, int> = 0> + template, int> = 0> dimensionless> log1p(const dimensionlessUnit x) noexcept { return std::log1p(x.value()); @@ -3743,7 +3777,7 @@ namespace units * domain error occurs. * @returns The binary logarithm of x: log2x. */ - template, int> = 0> + template, int> = 0> dimensionless> log2(const dimensionlessUnit x) noexcept { return std::log2(x.value()); @@ -3767,7 +3801,7 @@ namespace units * In some cases, _both_ the returned value _and_ conversion factor of the returned * unit type may have errors no larger than `1e-10`. */ - template, int> = 0> + template, int> = 0> constexpr auto sqrt(const UnitType& value) noexcept -> unit::conversion_factor>>, detail::floating_point_promotion_t::underlying_type>, linear_scale> { @@ -3784,7 +3818,7 @@ namespace units * as x. */ template && traits::has_linear_scale_v, int> = 0> + std::enable_if_t && traits::has_linear_scale_v, int> = 0> detail::floating_point_promotion_t> hypot(const UnitTypeLhs& x, const UnitTypeRhs& y) { using CommonUnit = decltype(units::hypot(x, y)); @@ -3802,7 +3836,7 @@ namespace units * @param[in] x Unit value to round up. * @returns The smallest integral value that is not less than x. */ - template, int> = 0> + template, int> = 0> detail::floating_point_promotion_t ceil(const UnitType x) noexcept { return detail::floating_point_promotion_t(std::ceil(x.value())); @@ -3815,7 +3849,7 @@ namespace units * @param[in] x Unit value to round down. * @returns The value of x rounded downward. */ - template, int> = 0> + template, int> = 0> detail::floating_point_promotion_t floor(const UnitType x) noexcept { return detail::floating_point_promotion_t(std::floor(x.value())); @@ -3829,7 +3863,7 @@ namespace units * @param[in] denom Value of the quotient denominator. * @returns The remainder of dividing the arguments. */ - template, int> = 0> + template, int> = 0> detail::floating_point_promotion_t> fmod(const UnitTypeLhs numer, const UnitTypeRhs denom) noexcept { using CommonUnit = decltype(units::fmod(numer, denom)); @@ -3844,7 +3878,7 @@ namespace units * @param[in] x Value to truncate * @returns The nearest integral value that is not larger in magnitude than x. */ - template, int> = 0> + template, int> = 0> detail::floating_point_promotion_t trunc(const UnitType x) noexcept { return detail::floating_point_promotion_t(std::trunc(x.value())); @@ -3858,7 +3892,7 @@ namespace units * @param[in] x value to round. * @returns The value of x rounded to the nearest integral. */ - template, int> = 0> + template, int> = 0> detail::floating_point_promotion_t round(const UnitType x) noexcept { return detail::floating_point_promotion_t(std::round(x.value())); @@ -3877,14 +3911,14 @@ namespace units * @param[in] y Value with the sign of the resulting value. * @returns value with the magnitude and dimension of x, and the sign of y. */ - template && traits::is_unit_v, int> = 0> + template && traits::is_unit_v, int> = 0> detail::floating_point_promotion_t copysign(const UnitTypeLhs x, const UnitTypeRhs y) noexcept { return detail::floating_point_promotion_t(std::copysign(x.value(), y.value())); // no need for conversion to get the correct sign. } /// Overload to copy the sign from a raw double - template && traits::is_unit_v, int> = 0> + template && traits::is_unit_v, int> = 0> detail::floating_point_promotion_t copysign(const UnitTypeLhs x, const T& y) noexcept { return detail::floating_point_promotion_t(std::copysign(x.value(), y)); @@ -3902,7 +3936,7 @@ namespace units * @param[in] y Values whose difference is calculated. * @returns The positive difference between x and y. */ - template, int> = 0> + template, int> = 0> detail::floating_point_promotion_t> fdim(const UnitTypeLhs x, const UnitTypeRhs y) noexcept { using CommonUnit = decltype(units::fdim(x, y)); @@ -3917,7 +3951,7 @@ namespace units * @param[in] y Values among which the function selects a maximum. * @returns The maximum numeric value of its arguments. */ - template, int> = 0> + template, int> = 0> detail::floating_point_promotion_t> fmax(const UnitTypeLhs x, const UnitTypeRhs y) noexcept { using CommonUnit = decltype(units::fmax(x, y)); @@ -3933,7 +3967,7 @@ namespace units * @param[in] y Values among which the function selects a minimum. * @returns The minimum numeric value of its arguments. */ - template, int> = 0> + template, int> = 0> detail::floating_point_promotion_t> fmin(const UnitTypeLhs x, const UnitTypeRhs y) noexcept { using CommonUnit = decltype(units::fmin(x, y)); @@ -3951,7 +3985,7 @@ namespace units * @param[in] x Value whose absolute value is returned. * @returns The absolute value of x. */ - template, int> = 0> + template, int> = 0> detail::floating_point_promotion_t fabs(const UnitType x) noexcept { return detail::floating_point_promotion_t(std::fabs(x.value())); @@ -3964,7 +3998,7 @@ namespace units * @param[in] x Value whose absolute value is returned. * @returns The absolute value of x. */ - template, int> = 0> + template, int> = 0> UnitType abs(const UnitType x) noexcept { return UnitType(std::abs(x.value())); @@ -3982,10 +4016,10 @@ namespace units */ template && traits::is_unit_v && traits::is_unit_v && - traits::is_same_dimension_conversion_factor_v::conversion_factor, - typename units::traits::unit_traits::conversion_factor>, - typename units::traits::unit_traits::conversion_factor>, - int> = 0> + traits::is_same_dimension_conversion_factor_v::conversion_factor, + typename units::traits::unit_traits::conversion_factor>, + typename units::traits::unit_traits::conversion_factor>, + int> = 0> auto fma(const UnitTypeLhs x, const UnitMultiply y, const UnitAdd z) noexcept -> std::common_type_t(x) * detail::floating_point_promotion_t(y)), UnitAdd> { @@ -4026,7 +4060,6 @@ namespace units { return std::isunordered(lhs.value(), rhs.value()); } - } // end namespace units //---------------------------------------------------------------------------------------------------------------------- @@ -4069,38 +4102,47 @@ namespace std { return units::unit(std::numeric_limits::min()); } + static constexpr units::unit denorm_min() noexcept { return units::unit(std::numeric_limits::denorm_min()); } + static constexpr units::unit max() { return units::unit(std::numeric_limits::max()); } + static constexpr units::unit lowest() { return units::unit(std::numeric_limits::lowest()); } + static constexpr units::unit epsilon() { return units::unit(std::numeric_limits::epsilon()); } + static constexpr units::unit round_error() { return units::unit(std::numeric_limits::round_error()); } + static constexpr units::unit infinity() { return units::unit(std::numeric_limits::infinity()); } + static constexpr units::unit quiet_NaN() { return units::unit(std::numeric_limits::quiet_NaN()); } + static constexpr units::unit signaling_NaN() { return units::unit(std::numeric_limits::signaling_NaN()); } + static constexpr bool is_specialized = std::numeric_limits::is_specialized; static constexpr bool is_signed = std::numeric_limits::is_signed; static constexpr bool is_integer = std::numeric_limits::is_integer; From 9f94a27344f4a71a345f3b18b1ed5693945edb75 Mon Sep 17 00:00:00 2001 From: Nic Holthaus Date: Thu, 12 Dec 2024 10:01:29 -0500 Subject: [PATCH 14/48] feat: replaced the other half of low-hanging SFINAE with concepts --- include/units/core.h | 182 ++++++++++++++++++++++++------------------- 1 file changed, 104 insertions(+), 78 deletions(-) diff --git a/include/units/core.h b/include/units/core.h index 9d07039b..ba21d3fb 100644 --- a/include/units/core.h +++ b/include/units/core.h @@ -3358,64 +3358,64 @@ namespace units } template - constexpr std::enable_if_t && std::is_arithmetic_v, bool> operator>=( - const T& lhs, const DimensionlessUnit& rhs) noexcept + requires traits::is_dimensionless_unit_v && std::is_arithmetic_v + constexpr bool operator>=(const T& lhs, const DimensionlessUnit& rhs) noexcept { using CommonUnderlying = std::common_type_t; return lhs >= static_cast(rhs); } template - constexpr std::enable_if_t && std::is_arithmetic_v, bool> operator>=( - const DimensionlessUnit& lhs, const T& rhs) noexcept + requires traits::is_dimensionless_unit_v && std::is_arithmetic_v + constexpr bool operator>=(const DimensionlessUnit& lhs, const T& rhs) noexcept { using CommonUnderlying = std::common_type_t; return static_cast(lhs) >= rhs; } template - constexpr std::enable_if_t && std::is_arithmetic_v, bool> operator>( - const T& lhs, const DimensionlessUnit& rhs) noexcept + requires traits::is_dimensionless_unit_v && std::is_arithmetic_v + constexpr bool operator>(const T& lhs, const DimensionlessUnit& rhs) noexcept { using CommonUnderlying = std::common_type_t; return lhs > static_cast(rhs); } template - constexpr std::enable_if_t && std::is_arithmetic_v, bool> operator>( - const DimensionlessUnit& lhs, const T& rhs) noexcept + requires traits::is_dimensionless_unit_v && std::is_arithmetic_v + constexpr bool operator>(const DimensionlessUnit& lhs, const T& rhs) noexcept { using CommonUnderlying = std::common_type_t; return static_cast(lhs) > rhs; } template - constexpr std::enable_if_t && std::is_arithmetic_v, bool> operator<=( - const T& lhs, const DimensionlessUnit& rhs) noexcept + requires traits::is_dimensionless_unit_v && std::is_arithmetic_v + constexpr bool operator<=(const T& lhs, const DimensionlessUnit& rhs) noexcept { using CommonUnderlying = std::common_type_t; return lhs <= static_cast(rhs); } template - constexpr std::enable_if_t && std::is_arithmetic_v, bool> operator<=( - const DimensionlessUnit& lhs, const T& rhs) noexcept + requires traits::is_dimensionless_unit_v && std::is_arithmetic_v + constexpr bool operator<=(const DimensionlessUnit& lhs, const T& rhs) noexcept { using CommonUnderlying = std::common_type_t; return static_cast(lhs) <= rhs; } template - constexpr std::enable_if_t && std::is_arithmetic_v, bool> operator<( - const T& lhs, const DimensionlessUnit& rhs) noexcept + requires traits::is_dimensionless_unit_v && std::is_arithmetic_v + constexpr bool operator<(const T& lhs, const DimensionlessUnit& rhs) noexcept { using CommonUnderlying = std::common_type_t; return lhs < static_cast(rhs); } template - constexpr std::enable_if_t && std::is_arithmetic_v, bool> operator<( - const DimensionlessUnit& lhs, const T& rhs) noexcept + requires traits::is_dimensionless_unit_v && std::is_arithmetic_v + constexpr bool operator<(const DimensionlessUnit& lhs, const T& rhs) noexcept { using CommonUnderlying = std::common_type_t; return static_cast(lhs) < rhs; @@ -3473,7 +3473,8 @@ namespace units * @param[in] value `unit` derived type to raise to the given power * @returns new unit, raised to the given exponent */ - template, int> = 0> + template + requires traits::has_linear_scale_v constexpr auto pow(const UnitType& value) noexcept -> unit::conversion_factor>::type>, detail::floating_point_promotion_t::underlying_type>, linear_scale> @@ -3544,8 +3545,8 @@ namespace units //------------------------------ /// Addition for convertible unit types with a decibel_scale - template && traits::has_decibel_scale_v, int> = 0> + template + requires traits::is_same_dimension_unit_v && traits::has_decibel_scale_v constexpr auto operator+(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept -> unit>::conversion_factor>>, typename std::common_type_t::underlying_type, decibel_scale> @@ -3557,10 +3558,9 @@ namespace units } /// Addition between unit types with a decibel_scale and dimensionless dB units - template && !traits::is_dimensionless_unit_v && - traits::is_dimensionless_unit_v, - int> = 0> + template + requires (traits::has_decibel_scale_v && !traits::is_dimensionless_unit_v && traits::is_dimensionless_unit_v< + UnitTypeRhs>) constexpr traits::replace_underlying_t> operator+(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept { @@ -3569,10 +3569,9 @@ namespace units } /// Addition between unit types with a decibel_scale and dimensionless dB units - template && traits::is_dimensionless_unit_v && - !traits::is_dimensionless_unit_v, - int> = 0> + template + requires (traits::has_decibel_scale_v && traits::is_dimensionless_unit_v && + !traits::is_dimensionless_unit_v) constexpr traits::replace_underlying_t> operator+(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept { @@ -3581,8 +3580,8 @@ namespace units } /// Subtraction for convertible unit types with a decibel_scale - template && traits::has_decibel_scale_v, int> = 0> + template + requires traits::is_same_dimension_unit_v && traits::has_decibel_scale_v constexpr auto operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept -> dB::underlying_type> { @@ -3593,10 +3592,9 @@ namespace units } /// Subtraction between unit types with a decibel_scale and dimensionless dB units - template && !traits::is_dimensionless_unit_v && - traits::is_dimensionless_unit_v, - int> = 0> + template + requires (traits::has_decibel_scale_v && !traits::is_dimensionless_unit_v && + traits::is_dimensionless_unit_v) constexpr traits::replace_underlying_t> operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept { @@ -3605,10 +3603,9 @@ namespace units } /// Subtraction between unit types with a decibel_scale and dimensionless dB units - template && traits::is_dimensionless_unit_v && - !traits::is_dimensionless_unit_v, - int> = 0> + template + requires (traits::has_decibel_scale_v && traits::is_dimensionless_unit_v && + !traits::is_dimensionless_unit_v) constexpr auto operator-(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept -> unit::conversion_factor>>, std::common_type_t, decibel_scale> @@ -3640,14 +3637,16 @@ namespace units // MIN/MAX FUNCTIONS //---------------------------------- - template, int> = 0> + template + requires traits::is_same_dimension_unit_v constexpr std::common_type_t min(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) { using CommonUnit = decltype(units::min(lhs, rhs)); return (lhs < rhs ? CommonUnit(lhs) : CommonUnit(rhs)); } - template, int> = 0> + template + requires traits::is_same_dimension_unit_v constexpr std::common_type_t max(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) { using CommonUnit = decltype(units::max(lhs, rhs)); @@ -3671,7 +3670,8 @@ namespace units * function returns HUGE_VAL (or HUGE_VALF or HUGE_VALL) with the proper sign, and an overflow range * error occurs */ - template, int> = 0> + template + requires traits::is_dimensionless_unit_v dimensionless> exp(const dimensionlessUnit x) noexcept { return std::exp(x.value()); @@ -3686,7 +3686,8 @@ namespace units * @sa log10 for more common base-10 logarithms * @returns Natural logarithm of x. */ - template, int> = 0> + template + requires traits::is_dimensionless_unit_v dimensionless> log(const dimensionlessUnit x) noexcept { return std::log(x.value()); @@ -3700,7 +3701,8 @@ namespace units * domain error occurs. * @returns Common logarithm of x. */ - template, int> = 0> + template + requires traits::is_dimensionless_unit_v dimensionless> log10(const dimensionlessUnit x) noexcept { return std::log10(x.value()); @@ -3717,8 +3719,8 @@ namespace units * is stored with the same sign as x. * @returns The fractional part of x, with the same sign. */ - template && std::is_floating_point_v, int> = 0> + template + requires traits::is_dimensionless_unit_v && std::is_floating_point_v dimensionless modf(const dimensionlessUnit x, dimensionlessUnit* intpart) noexcept { typename dimensionlessUnit::underlying_type intp; @@ -3734,7 +3736,8 @@ namespace units * @param[in] x Value of the exponent. * @returns 2 raised to the power of x. */ - template, int> = 0> + template + requires traits::is_dimensionless_unit_v dimensionless> exp2(const dimensionlessUnit x) noexcept { return std::exp2(x.value()); @@ -3748,7 +3751,8 @@ namespace units * @param[in] x Value of the exponent. * @returns e raised to the power of x, minus one. */ - template, int> = 0> + template + requires traits::is_dimensionless_unit_v dimensionless> expm1(const dimensionlessUnit x) noexcept { return std::expm1(x.value()); @@ -3763,7 +3767,8 @@ namespace units * domain error occurs. * @returns The natural logarithm of (1+x). */ - template, int> = 0> + template + requires traits::is_dimensionless_unit_v dimensionless> log1p(const dimensionlessUnit x) noexcept { return std::log1p(x.value()); @@ -3777,7 +3782,8 @@ namespace units * domain error occurs. * @returns The binary logarithm of x: log2x. */ - template, int> = 0> + template + requires traits::is_dimensionless_unit_v dimensionless> log2(const dimensionlessUnit x) noexcept { return std::log2(x.value()); @@ -3801,9 +3807,10 @@ namespace units * In some cases, _both_ the returned value _and_ conversion factor of the returned * unit type may have errors no larger than `1e-10`. */ - template, int> = 0> - constexpr auto sqrt(const UnitType& value) noexcept -> unit::conversion_factor>>, - detail::floating_point_promotion_t::underlying_type>, linear_scale> + template + requires traits::has_linear_scale_v + constexpr auto sqrt(const UnitType& value) noexcept -> unit::conversion_factor>>, + detail::floating_point_promotion_t::underlying_type>> { return decltype(units::sqrt(value))(sqrt(value.value())); } @@ -3817,8 +3824,8 @@ namespace units * @returns square root of the sum-of-squares of x and y in the same units * as x. */ - template && traits::has_linear_scale_v, int> = 0> + template + requires traits::is_same_dimension_unit_v && traits::has_linear_scale_v detail::floating_point_promotion_t> hypot(const UnitTypeLhs& x, const UnitTypeRhs& y) { using CommonUnit = decltype(units::hypot(x, y)); @@ -3836,7 +3843,8 @@ namespace units * @param[in] x Unit value to round up. * @returns The smallest integral value that is not less than x. */ - template, int> = 0> + template + requires traits::is_unit_v detail::floating_point_promotion_t ceil(const UnitType x) noexcept { return detail::floating_point_promotion_t(std::ceil(x.value())); @@ -3849,7 +3857,8 @@ namespace units * @param[in] x Unit value to round down. * @returns The value of x rounded downward. */ - template, int> = 0> + template + requires traits::is_unit_v detail::floating_point_promotion_t floor(const UnitType x) noexcept { return detail::floating_point_promotion_t(std::floor(x.value())); @@ -3863,7 +3872,8 @@ namespace units * @param[in] denom Value of the quotient denominator. * @returns The remainder of dividing the arguments. */ - template, int> = 0> + template + requires traits::is_same_dimension_unit_v detail::floating_point_promotion_t> fmod(const UnitTypeLhs numer, const UnitTypeRhs denom) noexcept { using CommonUnit = decltype(units::fmod(numer, denom)); @@ -3878,7 +3888,8 @@ namespace units * @param[in] x Value to truncate * @returns The nearest integral value that is not larger in magnitude than x. */ - template, int> = 0> + template + requires traits::is_unit_v detail::floating_point_promotion_t trunc(const UnitType x) noexcept { return detail::floating_point_promotion_t(std::trunc(x.value())); @@ -3892,7 +3903,8 @@ namespace units * @param[in] x value to round. * @returns The value of x rounded to the nearest integral. */ - template, int> = 0> + template + requires traits::is_unit_v detail::floating_point_promotion_t round(const UnitType x) noexcept { return detail::floating_point_promotion_t(std::round(x.value())); @@ -3911,14 +3923,16 @@ namespace units * @param[in] y Value with the sign of the resulting value. * @returns value with the magnitude and dimension of x, and the sign of y. */ - template && traits::is_unit_v, int> = 0> + template + requires traits::is_unit_v && traits::is_unit_v detail::floating_point_promotion_t copysign(const UnitTypeLhs x, const UnitTypeRhs y) noexcept { return detail::floating_point_promotion_t(std::copysign(x.value(), y.value())); // no need for conversion to get the correct sign. } /// Overload to copy the sign from a raw double - template && traits::is_unit_v, int> = 0> + template + requires std::is_arithmetic_v && traits::is_unit_v detail::floating_point_promotion_t copysign(const UnitTypeLhs x, const T& y) noexcept { return detail::floating_point_promotion_t(std::copysign(x.value(), y)); @@ -3936,7 +3950,8 @@ namespace units * @param[in] y Values whose difference is calculated. * @returns The positive difference between x and y. */ - template, int> = 0> + template + requires traits::is_same_dimension_unit_v detail::floating_point_promotion_t> fdim(const UnitTypeLhs x, const UnitTypeRhs y) noexcept { using CommonUnit = decltype(units::fdim(x, y)); @@ -3951,7 +3966,8 @@ namespace units * @param[in] y Values among which the function selects a maximum. * @returns The maximum numeric value of its arguments. */ - template, int> = 0> + template + requires traits::is_same_dimension_unit_v detail::floating_point_promotion_t> fmax(const UnitTypeLhs x, const UnitTypeRhs y) noexcept { using CommonUnit = decltype(units::fmax(x, y)); @@ -3967,7 +3983,8 @@ namespace units * @param[in] y Values among which the function selects a minimum. * @returns The minimum numeric value of its arguments. */ - template, int> = 0> + template + requires traits::is_same_dimension_unit_v detail::floating_point_promotion_t> fmin(const UnitTypeLhs x, const UnitTypeRhs y) noexcept { using CommonUnit = decltype(units::fmin(x, y)); @@ -3985,7 +4002,8 @@ namespace units * @param[in] x Value whose absolute value is returned. * @returns The absolute value of x. */ - template, int> = 0> + template + requires traits::is_unit_v detail::floating_point_promotion_t fabs(const UnitType x) noexcept { return detail::floating_point_promotion_t(std::fabs(x.value())); @@ -3998,7 +4016,8 @@ namespace units * @param[in] x Value whose absolute value is returned. * @returns The absolute value of x. */ - template, int> = 0> + template + requires traits::is_unit_v UnitType abs(const UnitType x) noexcept { return UnitType(std::abs(x.value())); @@ -4031,32 +4050,37 @@ namespace units // NAN support //---------------------------- - template>> - inline bool isnan(const UnitType& x) noexcept + template + requires traits::is_unit_v + constexpr bool isnan(const UnitType& x) noexcept { return std::isnan(x.value()); } - template>> - inline bool isinf(const UnitType& x) noexcept + template + requires traits::is_unit_v + constexpr bool isinf(const UnitType& x) noexcept { return std::isinf(x.value()); } - template>> - inline bool isfinite(const UnitType& x) noexcept + template + requires traits::is_unit_v + constexpr bool isfinite(const UnitType& x) noexcept { return std::isfinite(x.value()); } - template>> - inline bool isnormal(const UnitType& x) noexcept + template + requires traits::is_unit_v + constexpr bool isnormal(const UnitType& x) noexcept { return std::isnormal(x.value()); } - template>> - inline bool isunordered(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept + template + requires traits::is_same_dimension_unit_v + constexpr bool isunordered(const UnitTypeLhs& lhs, const UnitTypeRhs& rhs) noexcept { return std::isunordered(lhs.value(), rhs.value()); } @@ -4180,16 +4204,18 @@ namespace std #include namespace units { - template>> + template + requires traits::is_unit_v void from_json(const nlohmann::json& j, UnitType& u) { - using underlying = typename units::traits::unit_traits::underlying_type; + using underlying = typename traits::unit_traits::underlying_type; underlying value; j.get_to(value); u = UnitType(value); } - template>> + template + requires traits::is_unit_v void to_json(nlohmann::json& j, const UnitType& u) { j = u.raw(); From b6175e8d0be43cedfad267740e5bf9e2a1c25245 Mon Sep 17 00:00:00 2001 From: Nic Holthaus Date: Thu, 12 Dec 2024 10:11:34 -0500 Subject: [PATCH 15/48] feat: a little more SFINAE is gone --- include/units/core.h | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/include/units/core.h b/include/units/core.h index ba21d3fb..f75e5f38 100644 --- a/include/units/core.h +++ b/include/units/core.h @@ -1289,11 +1289,12 @@ namespace units template struct SingleSidedSearch_ : SingleSidedSearch_::value>{}; - static constexpr const std::intmax_t value = SingleSidedSearch_<1>::value; + static constexpr std::intmax_t value = SingleSidedSearch_<1>::value; }; template