diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6d558ffe..fa6c7d95 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -56,6 +56,7 @@ add_mp_units_module( include/mp-units/framework/dimension_concepts.h include/mp-units/framework/expression_template.h include/mp-units/framework/magnitude.h + include/mp-units/framework/magnitude_concepts.h include/mp-units/framework/quantity.h include/mp-units/framework/quantity_cast.h include/mp-units/framework/quantity_concepts.h diff --git a/src/core/include/mp-units/bits/sudo_cast.h b/src/core/include/mp-units/bits/sudo_cast.h index 9f24949a..b3928002 100644 --- a/src/core/include/mp-units/bits/sudo_cast.h +++ b/src/core/include/mp-units/bits/sudo_cast.h @@ -79,9 +79,9 @@ struct conversion_value_traits { static constexpr Magnitude auto num = _numerator(M); static constexpr Magnitude auto den = _denominator(M); static constexpr Magnitude auto irr = M * (den / num); - static constexpr T num_mult = get_value(num); - static constexpr T den_mult = get_value(den); - static constexpr T irr_mult = get_value(irr); + static constexpr T num_mult = _get_value(num); + static constexpr T den_mult = _get_value(den); + static constexpr T irr_mult = _get_value(irr); static constexpr T ratio = num_mult / den_mult * irr_mult; }; @@ -120,10 +120,10 @@ template(_numerator(c_mag)); }); - else if constexpr (is_integral(pow<-1>(c_mag))) - return scale([&](auto value) { return value / get_value(_denominator(c_mag)); }); + if constexpr (_is_integral(c_mag)) + return scale([&](auto value) { return value * _get_value(_numerator(c_mag)); }); + else if constexpr (_is_integral(_pow<-1>(c_mag))) + return scale([&](auto value) { return value / _get_value(_denominator(c_mag)); }); else { using value_traits = conversion_value_traits; if constexpr (std::is_floating_point_v) diff --git a/src/core/include/mp-units/framework.h b/src/core/include/mp-units/framework.h index 0e9d4d19..2de63579 100644 --- a/src/core/include/mp-units/framework.h +++ b/src/core/include/mp-units/framework.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/src/core/include/mp-units/framework/magnitude.h b/src/core/include/mp-units/framework/magnitude.h index facf83b1..d4b960bc 100644 --- a/src/core/include/mp-units/framework/magnitude.h +++ b/src/core/include/mp-units/framework/magnitude.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -61,68 +62,37 @@ using factorizer = wheel_factorizer<4>; MP_UNITS_EXPORT template struct mag_constant { - static constexpr auto symbol = Symbol; + static constexpr auto _symbol_ = Symbol; }; #else -MP_UNITS_EXPORT template +MP_UNITS_EXPORT template + requires(Value > 0) struct mag_constant { - static constexpr auto symbol = Symbol; - static constexpr auto value = Value; + static constexpr auto _symbol_ = Symbol; + static constexpr long double _value_ = Value; }; #endif - -MP_UNITS_EXPORT template -concept MagConstant = - is_derived_from_specialization_of_v && std::is_empty_v && std::is_final_v && requires { - { +T::value } -> std::same_as; - }; +namespace detail { template concept MagArg = std::integral || MagConstant; -/** - * @brief Any type which can be used as a basis vector in a PowerV. - * - * We have two categories. - * - * The first is just an integral type (either `int` or `std::intmax_t`). This is for prime number bases. - * These can always be used directly as NTTPs. - * - * The second category is a _custom tag type_, which inherits from `mag_constant` and has a static member variable - * `value` of type `long double` that holds its value. We choose `long double` to get the greatest degree of precision; - * users who need a different type can convert from this at compile time. This category is for any irrational base - * we admit into our representation (on which, more details below). - */ -template -concept PowerVBase = one_of || MagConstant; +} // TODO Unify with `power` if UTPs (P1985) are accepted by the Committee -template +template requires(detail::valid_ratio && !detail::ratio_one) struct power_v { static constexpr auto base = V; static constexpr detail::ratio exponent{Num, Den...}; }; -template -concept MagnitudeSpec = PowerVBase || is_specialization_of_v; - -// TODO refactor to typenames? -template -struct magnitude; - -/** - * @brief Concept to detect whether T is a valid Magnitude. - */ -MP_UNITS_EXPORT template -concept Magnitude = is_specialization_of_v; - namespace detail { -template +template [[nodiscard]] consteval auto get_base(Element element) { if constexpr (is_specialization_of_v) @@ -131,18 +101,18 @@ template return element; } -template +template [[nodiscard]] consteval auto get_base_value(Element element) { if constexpr (is_specialization_of_v) return get_base_value(Element::base); else if constexpr (MagConstant) - return element.value; + return element._value_; else return element; } -template +template [[nodiscard]] MP_UNITS_CONSTEVAL ratio get_exponent(Element) { if constexpr (is_specialization_of_v) @@ -164,7 +134,7 @@ template } } -template +template [[nodiscard]] consteval auto inverse(M) { return power_v_or_T(); @@ -178,7 +148,7 @@ using widen_t = conditional, T>; template -[[nodiscard]] consteval widen_t compute_base_power(MagnitudeSpec auto el) +[[nodiscard]] consteval widen_t compute_base_power(MagnitudeSpecExpr auto el) { // This utility can only handle integer powers. To compute rational powers at compile time, we'll // need to write a custom function. @@ -210,23 +180,22 @@ template } } -[[nodiscard]] consteval bool is_rational(MagnitudeSpec auto element) +[[nodiscard]] consteval bool is_rational(MagnitudeSpecExpr auto element) { - static_assert(!Magnitude); // magnitudes are handles by another overload - return std::is_integral_v && (get_exponent(element).den == 1); + return std::is_integral_v && get_exponent(element).den == 1; } -[[nodiscard]] consteval bool is_integral(MagnitudeSpec auto element) +[[nodiscard]] consteval bool is_integral(MagnitudeSpecExpr auto element) { - static_assert(!Magnitude); // magnitudes are handles by another overload return is_rational(element) && get_exponent(element).num > 0; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Magnitude product implementation. -[[nodiscard]] consteval bool less(MagnitudeSpec auto lhs, MagnitudeSpec auto rhs) +[[nodiscard]] consteval bool less(MagnitudeSpecExpr auto lhs, MagnitudeSpecExpr auto rhs) { - // clang-arm64 raises "error: implicit conversion from 'long' to 'long double' may lose precision" so we need an explicit cast + // clang-arm64 raises "error: implicit conversion from 'long' to 'long double' may lose precision" so we need an + // explicit cast using ct = std::common_type_t; return static_cast(get_base_value(lhs)) < static_cast(get_base_value(rhs)); } @@ -358,7 +327,7 @@ template Out, auto M, auto... Rest> bool negative_power) { auto to_symbol = [&](T v) { - out = copy_symbol(get_base(v).symbol, fmt.encoding, negative_power, out); + out = copy_symbol(get_base(v)._symbol_, fmt.encoding, negative_power, out); constexpr ratio r = get_exponent(T{}); return copy_symbol_exponent(fmt.encoding, negative_power, out); }; @@ -370,7 +339,7 @@ template(Num); + constexpr auto num_value = _get_value(Num); if constexpr (num_value != 1) { constexpr auto num = detail::regular(); out = copy_symbol(num, fmt.encoding, false, out); @@ -386,7 +355,7 @@ constexpr Out magnitude_symbol_impl(Out out, const unit_symbol_formatting& fmt) using enum unit_symbol_solidus; bool denominator = false; - constexpr auto den_value = get_value(Den); + constexpr auto den_value = _get_value(Den); constexpr auto den_constants_size = magnitude_list_size(DenConstants); constexpr auto den_size = (den_value != 1) + den_constants_size; auto start_denominator = [&]() { @@ -437,20 +406,8 @@ constexpr Out magnitude_symbol_impl(Out out, const unit_symbol_formatting& fmt) * Magnitudes can be treated as values. Each type encodes exactly one value. Users can multiply, divide, raise to * rational powers, and compare for equality. */ -template +template struct magnitude : detail::magnitude_base> { - [[nodiscard]] friend consteval bool is_integral(const magnitude&) - { - using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec - return (is_integral(Ms) && ...); - } - - [[nodiscard]] friend consteval bool is_rational(const magnitude&) - { - using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec - return (is_rational(Ms) && ...); - } - template [[nodiscard]] friend consteval Magnitude auto operator*(magnitude m1, M m2) { @@ -462,7 +419,7 @@ struct magnitude : detail::magnitude_base> { return _multiply_impl(m1, m2); } - [[nodiscard]] friend consteval auto operator/(magnitude l, Magnitude auto r) { return l * pow<-1>(r); } + [[nodiscard]] friend consteval auto operator/(magnitude l, Magnitude auto r) { return l * _pow<-1>(r); } template [[nodiscard]] friend consteval bool operator==(magnitude, M2) @@ -470,12 +427,17 @@ struct magnitude : detail::magnitude_base> { return std::is_same_v; } +private: + // all below functions should in fact be in a `detail` namespace but are placed here to benefit from the ADL + [[nodiscard]] friend consteval bool _is_integral(const magnitude&) { return (detail::is_integral(Ms) && ...); } + [[nodiscard]] friend consteval bool _is_rational(const magnitude&) { return (detail::is_rational(Ms) && ...); } + /** * @brief The value of a Magnitude in a desired type T. */ template requires((detail::is_integral(Ms) && ...)) || treat_as_floating_point - [[nodiscard]] friend consteval T get_value(const magnitude&) + [[nodiscard]] friend consteval T _get_value(const magnitude&) { // Force the expression to be evaluated in a constexpr context, to catch, e.g., overflow. constexpr T result = detail::checked_static_cast((detail::compute_base_power(Ms) * ... * T{1})); @@ -485,7 +447,7 @@ struct magnitude : detail::magnitude_base> { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Magnitude rational powers implementation. template - [[nodiscard]] friend consteval auto pow(magnitude) + [[nodiscard]] friend consteval auto _pow(magnitude) { if constexpr (Num == 0) { return magnitude<>{}; @@ -495,10 +457,6 @@ struct magnitude : detail::magnitude_base> { } } - [[nodiscard]] friend consteval auto sqrt(magnitude m) { return pow<1, 2>(m); } - [[nodiscard]] friend consteval auto cbrt(magnitude m) { return pow<1, 3>(m); } - -private: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Magnitude numerator and denominator implementation. [[nodiscard]] friend consteval auto _numerator(magnitude) @@ -506,7 +464,7 @@ private: return (detail::integer_part(magnitude{}) * ... * magnitude<>{}); } - [[nodiscard]] friend consteval auto _denominator(magnitude) { return _numerator(pow<-1>(magnitude{})); } + [[nodiscard]] friend consteval auto _denominator(magnitude) { return _numerator(_pow<-1>(magnitude{})); } [[nodiscard]] friend consteval auto _remove_positive_powers(magnitude) { @@ -690,10 +648,6 @@ struct prime_factorization<1> { template constexpr auto prime_factorization_v = prime_factorization::value; -} // namespace detail - -namespace detail { - template [[nodiscard]] consteval Magnitude auto make_magnitude() { @@ -707,7 +661,7 @@ template MP_UNITS_EXPORT_BEGIN -template +template requires(detail::get_base_value(V) > 0) constexpr Magnitude auto mag = detail::make_magnitude(); @@ -718,16 +672,16 @@ constexpr Magnitude auto mag_ratio = detail::prime_factorization_v / detail:: /** * @brief Create a Magnitude which is some rational number raised to a rational power. */ -template +template requires(detail::get_base_value(Base) > 0) -constexpr Magnitude auto mag_power = pow(mag); +constexpr Magnitude auto mag_power = _pow(mag); /** * @brief A convenient Magnitude constant for pi, which we can manipulate like a regular number. */ #if defined MP_UNITS_COMP_CLANG || MP_UNITS_COMP_CLANG < 18 inline constexpr struct pi final : mag_constant { - static constexpr auto value = std::numbers::pi_v; + static constexpr auto _value_ = std::numbers::pi_v; #else inline constexpr struct pi final : mag_constant> { #endif @@ -744,7 +698,7 @@ template requires(detail::get_base_value(Base) > 0) [[nodiscard]] consteval Magnitude auto mag_power_lazy() { - return pow(mag); + return _pow(mag); } } // namespace detail diff --git a/src/core/include/mp-units/framework/magnitude_concepts.h b/src/core/include/mp-units/framework/magnitude_concepts.h new file mode 100644 index 00000000..c5861f21 --- /dev/null +++ b/src/core/include/mp-units/framework/magnitude_concepts.h @@ -0,0 +1,92 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +// IWYU pragma: private, include +#include +#include +#include +#include + +#ifndef MP_UNITS_IN_MODULE_INTERFACE +#ifdef MP_UNITS_IMPORT_STD +import std; +#else +#include +#endif +#endif + +namespace mp_units { + +#if defined MP_UNITS_COMP_CLANG || MP_UNITS_COMP_CLANG < 18 +MP_UNITS_EXPORT template +#else +MP_UNITS_EXPORT template + requires(Value > 0) +#endif +struct mag_constant; + +MP_UNITS_EXPORT template +concept MagConstant = detail::TagType && is_derived_from_specialization_of_v; + +namespace detail { + +/** + * @brief Any type which can be used as a basis vector in a PowerV. + * + * We have two categories. + * + * The first is just an integral type (either `int` or `std::intmax_t`). This is for prime number bases. + * These can always be used directly as NTTPs. + * + * The second category is a _custom tag type_, which inherits from `mag_constant` and has a static member variable + * `value` of type `long double` that holds its value. We choose `long double` to get the greatest degree of precision; + * users who need a different type can convert from this at compile time. This category is for any irrational base + * we admit into our representation (on which, more details below). + */ +template +concept PowerVBase = one_of || MagConstant; + +} // namespace detail + +template + requires(detail::valid_ratio && !detail::ratio_one) +struct power_v; + +namespace detail { + +template +concept MagnitudeSpecExpr = PowerVBase || is_specialization_of_v; + +} + +template +struct magnitude; + +/** + * @brief Concept to detect whether T is a valid Magnitude. + */ +MP_UNITS_EXPORT template +concept Magnitude = is_specialization_of_v; + +} // namespace mp_units diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index ffb04edc..9e1f2c21 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -56,7 +56,7 @@ template if constexpr (is_same_v) return true; else - return is_integral(get_canonical_unit(from).mag / get_canonical_unit(to).mag); + return _is_integral(get_canonical_unit(from).mag / get_canonical_unit(to).mag); } template diff --git a/src/core/include/mp-units/framework/unit.h b/src/core/include/mp-units/framework/unit.h index 4cf11aa0..1c1d315d 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -544,7 +544,7 @@ template template [[nodiscard]] consteval auto get_canonical_unit_impl(const power&, const type_list&) { - auto mag = (mp_units::mag<1> * ... * pow(get_canonical_unit_impl(Us{}, Us{}).mag)); + auto mag = (mp_units::mag<1> * ... * _pow(get_canonical_unit_impl(Us{}, Us{}).mag)); auto u = (one * ... * pow(get_canonical_unit_impl(Us{}, Us{}).reference_unit)); return canonical_unit{mag, u}; } @@ -556,9 +556,9 @@ template if constexpr (requires { typename decltype(base.reference_unit)::_num_; }) { auto num = get_canonical_unit_impl(power{}, typename decltype(base.reference_unit)::_num_{}); auto den = get_canonical_unit_impl(power{}, typename decltype(base.reference_unit)::_den_{}); - return canonical_unit{pow(base.mag) * num.mag / den.mag, num.reference_unit / den.reference_unit}; + return canonical_unit{_pow(base.mag) * num.mag / den.mag, num.reference_unit / den.reference_unit}; } else { - return canonical_unit{pow(base.mag), + return canonical_unit{_pow(base.mag), derived_unit>{}}; } } @@ -677,9 +677,9 @@ template constexpr auto canonical_lhs = get_canonical_unit(U1{}); constexpr auto canonical_rhs = get_canonical_unit(U2{}); - if constexpr (is_integral(canonical_lhs.mag / canonical_rhs.mag)) + if constexpr (_is_integral(canonical_lhs.mag / canonical_rhs.mag)) return u2; - else if constexpr (is_integral(canonical_rhs.mag / canonical_lhs.mag)) + else if constexpr (_is_integral(canonical_rhs.mag / canonical_lhs.mag)) return u1; else { if constexpr (detail::unit_less::value) diff --git a/src/systems/include/mp-units/systems/si/chrono.h b/src/systems/include/mp-units/systems/si/chrono.h index f809f7b5..86589107 100644 --- a/src/systems/include/mp-units/systems/si/chrono.h +++ b/src/systems/include/mp-units/systems/si/chrono.h @@ -120,9 +120,9 @@ struct quantity_point_like_traits(_numerator(m)), get_value(_denominator(m))>{}; + return std::ratio<_get_value(_numerator(m)), _get_value(_denominator(m))>{}; } } // namespace detail diff --git a/test/static/unit_symbol_test.cpp b/test/static/unit_symbol_test.cpp index 87112c3f..f8dc4b6c 100644 --- a/test/static/unit_symbol_test.cpp +++ b/test/static/unit_symbol_test.cpp @@ -143,7 +143,7 @@ static_assert(unit_symbol(mag_ratio<1, 18000> * (metre / // magnitude constants #if defined MP_UNITS_COMP_CLANG || MP_UNITS_COMP_CLANG < 18 inline constexpr struct e final : mag_constant<"e"> { - static constexpr long double value = std::numbers::e_v; + static constexpr long double _value_ = std::numbers::e_v; #else inline constexpr struct e final : mag_constant<"e", std::numbers::e_v > { #endif