mirror of
https://github.com/mpusz/mp-units.git
synced 2025-08-03 12:24:26 +02:00
feat: MagnitudeSpecExpr
and PowerVBase
removed and some functions renamed to limit possible ambiguity in overload resolution
This commit is contained in:
@@ -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.
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user