feat: MagnitudeSpecExpr and PowerVBase removed and some functions renamed to limit possible ambiguity in overload resolution

This commit is contained in:
Mateusz Pusz
2024-10-25 22:53:03 +02:00
parent f17e707edc
commit d83cbc049f
2 changed files with 35 additions and 53 deletions

View File

@@ -82,8 +82,21 @@ concept MagArg = std::integral<T> || MagConstant<T>;
} }
/**
* @brief Any type which can be used as a basis vector in a power_v.
*
* 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).
*/
// TODO Unify with `power` if UTPs (P1985) are accepted by the Committee // TODO Unify with `power` if UTPs (P1985) are accepted by the Committee
template<detail::PowerVBase auto V, int Num, int... Den> template<auto V, int Num, int... Den>
requires(detail::valid_ratio<Num, Den...> && !detail::ratio_one<Num, Den...>) requires(detail::valid_ratio<Num, Den...> && !detail::ratio_one<Num, Den...>)
struct power_v { struct power_v {
static constexpr auto base = V; static constexpr auto base = V;
@@ -92,7 +105,7 @@ struct power_v {
namespace detail { namespace detail {
template<MagnitudeSpecExpr Element> template<typename Element>
[[nodiscard]] consteval auto get_base(Element element) [[nodiscard]] consteval auto get_base(Element element)
{ {
if constexpr (is_specialization_of_v<Element, power_v>) if constexpr (is_specialization_of_v<Element, power_v>)
@@ -101,7 +114,7 @@ template<MagnitudeSpecExpr Element>
return element; return element;
} }
template<MagnitudeSpecExpr Element> template<typename Element>
[[nodiscard]] consteval auto get_base_value(Element element) [[nodiscard]] consteval auto get_base_value(Element element)
{ {
if constexpr (is_specialization_of_v<Element, power_v>) if constexpr (is_specialization_of_v<Element, power_v>)
@@ -112,7 +125,7 @@ template<MagnitudeSpecExpr Element>
return element; return element;
} }
template<MagnitudeSpecExpr Element> template<typename Element>
[[nodiscard]] MP_UNITS_CONSTEVAL ratio get_exponent(Element) [[nodiscard]] MP_UNITS_CONSTEVAL ratio get_exponent(Element)
{ {
if constexpr (is_specialization_of_v<Element, power_v>) if constexpr (is_specialization_of_v<Element, power_v>)
@@ -121,7 +134,7 @@ template<MagnitudeSpecExpr Element>
return ratio{1}; return ratio{1};
} }
template<PowerVBase auto V, ratio R> template<auto V, ratio R>
[[nodiscard]] consteval auto power_v_or_T() [[nodiscard]] consteval auto power_v_or_T()
{ {
if constexpr (R.den == 1) { if constexpr (R.den == 1) {
@@ -134,8 +147,8 @@ template<PowerVBase auto V, ratio R>
} }
} }
template<MagnitudeSpecExpr M> template<typename M>
[[nodiscard]] consteval auto inverse(M) [[nodiscard]] consteval auto mag_inverse(M)
{ {
return power_v_or_T<get_base(M{}), -1 * get_exponent(M{})>(); return power_v_or_T<get_base(M{}), -1 * get_exponent(M{})>();
} }
@@ -148,7 +161,7 @@ using widen_t = conditional<std::is_arithmetic_v<T>,
T>; T>;
template<typename T> template<typename T>
[[nodiscard]] consteval widen_t<T> compute_base_power(MagnitudeSpecExpr auto el) [[nodiscard]] consteval widen_t<T> compute_base_power(auto el)
{ {
// This utility can only handle integer powers. To compute rational powers at compile time, we'll // This utility can only handle integer powers. To compute rational powers at compile time, we'll
// need to write a custom function. // need to write a custom function.
@@ -161,7 +174,7 @@ template<typename T>
if constexpr (std::is_integral_v<T>) { if constexpr (std::is_integral_v<T>) {
std::abort(); // Cannot represent reciprocal as integer std::abort(); // Cannot represent reciprocal as integer
} else { } else {
return T{1} / compute_base_power<T>(inverse(el)); return T{1} / compute_base_power<T>(mag_inverse(el));
} }
} }
@@ -180,17 +193,17 @@ template<typename T>
} }
} }
[[nodiscard]] consteval bool is_rational(MagnitudeSpecExpr auto element) [[nodiscard]] consteval bool is_rational_impl(auto element)
{ {
return std::is_integral_v<decltype(get_base(element))> && get_exponent(element).den == 1; return std::is_integral_v<decltype(get_base(element))> && get_exponent(element).den == 1;
} }
[[nodiscard]] consteval bool is_integral(MagnitudeSpecExpr auto element) [[nodiscard]] consteval bool is_integral_impl(auto element)
{ {
return is_rational(element) && get_exponent(element).num > 0; return is_rational_impl(element) && get_exponent(element).num > 0;
} }
[[nodiscard]] consteval bool is_positive_integral_power(MagnitudeSpecExpr auto element) [[nodiscard]] consteval bool is_positive_integral_power_impl(auto element)
{ {
auto exp = get_exponent(element); auto exp = get_exponent(element);
return exp.den == 1 && exp.num > 0; return exp.den == 1 && exp.num > 0;
@@ -198,7 +211,7 @@ template<typename T>
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude product implementation. // Magnitude product implementation.
[[nodiscard]] consteval bool less(MagnitudeSpecExpr auto lhs, MagnitudeSpecExpr auto rhs) [[nodiscard]] consteval bool mag_less(auto lhs, auto rhs)
{ {
// clang-arm64 raises "error: implicit conversion from 'long' to 'long double' may lose precision" so we need an // clang-arm64 raises "error: implicit conversion from 'long' to 'long double' may lose precision" so we need an
// explicit cast // explicit cast
@@ -232,14 +245,14 @@ struct magnitude_base<magnitude<H, T...>> {
template<auto H2, auto... T2> template<auto H2, auto... T2>
[[nodiscard]] friend consteval Magnitude auto _multiply_impl(magnitude<H, T...>, magnitude<H2, T2...>) [[nodiscard]] friend consteval Magnitude auto _multiply_impl(magnitude<H, T...>, magnitude<H2, T2...>)
{ {
if constexpr (less(H, H2)) { if constexpr (mag_less(H, H2)) {
if constexpr (sizeof...(T) == 0) { if constexpr (sizeof...(T) == 0) {
// Shortcut for the "pure prepend" case, which makes it easier to implement some of the other cases. // Shortcut for the "pure prepend" case, which makes it easier to implement some of the other cases.
return magnitude<H, H2, T2...>{}; return magnitude<H, H2, T2...>{};
} else { } else {
return magnitude<H>{} * (magnitude<T...>{} * magnitude<H2, T2...>{}); return magnitude<H>{} * (magnitude<T...>{} * magnitude<H2, T2...>{});
} }
} else if constexpr (less(H2, H)) { } else if constexpr (mag_less(H2, H)) {
return magnitude<H2>{} * (magnitude<H, T...>{} * magnitude<T2...>{}); return magnitude<H2>{} * (magnitude<H, T...>{} * magnitude<T2...>{});
} else { } else {
if constexpr (is_same_v<decltype(get_base(H)), decltype(get_base(H2))>) { if constexpr (is_same_v<decltype(get_base(H)), decltype(get_base(H2))>) {
@@ -412,7 +425,7 @@ 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 * Magnitudes can be treated as values. Each type encodes exactly one value. Users can multiply, divide, raise to
* rational powers, and compare for equality. * rational powers, and compare for equality.
*/ */
template<detail::MagnitudeSpecExpr auto... Ms> template<auto... Ms>
struct magnitude : detail::magnitude_base<magnitude<Ms...>> { struct magnitude : detail::magnitude_base<magnitude<Ms...>> {
template<Magnitude M> template<Magnitude M>
[[nodiscard]] friend consteval Magnitude auto operator*(magnitude m1, M m2) [[nodiscard]] friend consteval Magnitude auto operator*(magnitude m1, M m2)
@@ -435,18 +448,18 @@ struct magnitude : detail::magnitude_base<magnitude<Ms...>> {
private: private:
// all below functions should in fact be in a `detail` namespace but are placed here to benefit from the ADL // 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_integral(const magnitude&) { return (detail::is_integral_impl(Ms) && ...); }
[[nodiscard]] friend consteval bool _is_rational(const magnitude&) { return (detail::is_rational(Ms) && ...); } [[nodiscard]] friend consteval bool _is_rational(const magnitude&) { return (detail::is_rational_impl(Ms) && ...); }
[[nodiscard]] friend consteval bool _is_positive_integral_power(const magnitude&) [[nodiscard]] friend consteval bool _is_positive_integral_power(const magnitude&)
{ {
return (detail::is_positive_integral_power(Ms) && ...); return (detail::is_positive_integral_power_impl(Ms) && ...);
} }
/** /**
* @brief The value of a Magnitude in a desired type T. * @brief The value of a Magnitude in a desired type T.
*/ */
template<typename T> template<typename T>
requires((detail::is_integral(Ms) && ...)) || treat_as_floating_point<T> requires((detail::is_integral_impl(Ms) && ...)) || treat_as_floating_point<T>
[[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. // Force the expression to be evaluated in a constexpr context, to catch, e.g., overflow.

View File

@@ -49,38 +49,7 @@ struct mag_constant;
MP_UNITS_EXPORT template<typename T> MP_UNITS_EXPORT template<typename T>
concept MagConstant = detail::TagType<T> && is_derived_from_specialization_of_v<T, mag_constant>; concept MagConstant = detail::TagType<T> && is_derived_from_specialization_of_v<T, mag_constant>;
namespace detail { template<auto... Ms>
/**
* @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<typename T>
concept PowerVBase = one_of<T, int, std::intmax_t> || MagConstant<T>;
} // namespace detail
template<detail::PowerVBase auto V, int Num, int... Den>
requires(detail::valid_ratio<Num, Den...> && !detail::ratio_one<Num, Den...>)
struct power_v;
namespace detail {
template<typename T>
concept MagnitudeSpecExpr = PowerVBase<T> || is_specialization_of_v<T, power_v>;
}
template<detail::MagnitudeSpecExpr auto... Ms>
struct magnitude; struct magnitude;
/** /**