Skip to content

Commit

Permalink
refactor: unit-related constexpr functions compile-time evaluation …
Browse files Browse the repository at this point in the history
…limited to only one that determines the return type
  • Loading branch information
mpusz committed Jun 5, 2024
1 parent fb77585 commit 760d485
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 123 deletions.
9 changes: 5 additions & 4 deletions src/core/include/mp-units/bits/sudo_cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,11 @@ template<Quantity To, typename From>
// warnings on conversions
} else {
// scale the number
constexpr Magnitude auto c_mag = get_canonical_unit(q_unit).mag / get_canonical_unit(To::unit).mag;
constexpr Magnitude auto num = numerator(c_mag);
constexpr Magnitude auto den = denominator(c_mag);
constexpr Magnitude auto irr = c_mag * (den / num);
constexpr Magnitude auto c_mag =
decltype(decltype(get_canonical_unit(q_unit))::mag / decltype(get_canonical_unit(To::unit))::mag){};
constexpr Magnitude auto num = decltype(numerator(c_mag)){};
constexpr Magnitude auto den = decltype(denominator(c_mag)){};
constexpr Magnitude auto irr = decltype(c_mag * decltype(den / num){}){};
using c_rep_type = maybe_common_type<typename std::remove_reference_t<From>::rep, typename To::rep>;
using c_mag_type = common_magnitude_type<c_mag>;
using multiplier_type = conditional<
Expand Down
5 changes: 3 additions & 2 deletions src/core/include/mp-units/framework/quantity.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ namespace mp_units {
namespace detail {

template<auto UFrom, auto UTo>
concept IntegralConversionFactor = Unit<decltype(UFrom)> && Unit<decltype(UTo)> &&
is_integral(get_canonical_unit(UFrom).mag / get_canonical_unit(UTo).mag);
concept IntegralConversionFactor =
Unit<decltype(UFrom)> && Unit<decltype(UTo)> &&
is_integral(decltype(decltype(get_canonical_unit(UFrom))::mag / decltype(get_canonical_unit(UTo))::mag){});

template<typename QFrom, typename QTo>
concept QuantityConvertibleTo =
Expand Down
169 changes: 63 additions & 106 deletions src/core/include/mp-units/framework/unit.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,22 +321,12 @@ struct is_one<struct one> : std::true_type {};
* @tparam U a unit to use as a `reference_unit`
* @tparam M a Magnitude representing an absolute scaling factor of this unit
*/
template<Magnitude M, Unit U>
template<Magnitude auto M, Unit auto U>
struct canonical_unit {
M mag;
U reference_unit;
static constexpr auto mag = M;
static constexpr auto reference_unit = U;
};

#if MP_UNITS_COMP_CLANG

template<Magnitude M, Unit U>
canonical_unit(M, U) -> canonical_unit<M, U>;

#endif

template<Unit T, symbol_text Symbol, detail::QuantityKindSpec auto Q, auto... Args>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q, Args...>&);

template<Unit T, symbol_text Symbol, auto... Args>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Args...>&);

Expand All @@ -352,64 +342,61 @@ template<Unit T, typename... Expr>
template<Unit T, auto M, typename U>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const scaled_unit<M, U>&)
{
auto base = get_canonical_unit_impl(U{}, U{});
return canonical_unit{M * base.mag, base.reference_unit};
}

template<Unit T, symbol_text Symbol, detail::QuantityKindSpec auto Q, auto... Args>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q, Args...>&)
{
return canonical_unit{mag<1>, t};
using base = decltype(get_canonical_unit_impl(U{}, U{}));
return canonical_unit<decltype(M * base::mag){}, base::reference_unit>{};
}

template<Unit T, symbol_text Symbol, auto... Args>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Args...>&)
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, Args...>&)
{
return canonical_unit{mag<1>, t};
return canonical_unit<mag<1>, T{}>{};
}

template<Unit T, symbol_text Symbol, Unit auto U, auto... Args>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, U, Args...>&)
{
return get_canonical_unit_impl(U, U);
return decltype(get_canonical_unit_impl(U, U)){};
}

template<typename F, int Num, int... Den, typename... Us>
[[nodiscard]] consteval auto get_canonical_unit_impl(const power<F, Num, Den...>&, const type_list<Us...>&)
{
auto mag = (mp_units::mag<1> * ... * pow<Num, Den...>(get_canonical_unit_impl(Us{}, Us{}).mag));
auto u = (one * ... * pow<Num, Den...>(get_canonical_unit_impl(Us{}, Us{}).reference_unit));
return canonical_unit{mag, u};
using mag = decltype((mp_units::mag<1> * ... * pow<Num, Den...>(decltype(get_canonical_unit_impl(Us{}, Us{}))::mag)));
using u = decltype((one * ... * pow<Num, Den...>(decltype(get_canonical_unit_impl(Us{}, Us{}))::reference_unit)));
return canonical_unit<mag{}, u{}>{};
}

template<typename T, typename F, int Num, int... Den>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const power<F, Num, Den...>&)
{
auto base = get_canonical_unit_impl(F{}, F{});
if constexpr (requires { typename decltype(base.reference_unit)::_num_; }) {
auto num = get_canonical_unit_impl(power<F, Num, Den...>{}, typename decltype(base.reference_unit)::_num_{});
auto den = get_canonical_unit_impl(power<F, Num, Den...>{}, typename decltype(base.reference_unit)::_den_{});
return canonical_unit{pow<Num, Den...>(base.mag) * num.mag / den.mag, num.reference_unit / den.reference_unit};
using base = decltype(get_canonical_unit_impl(F{}, F{}));
if constexpr (requires { typename decltype(base::reference_unit)::_num_; }) {
using num =
decltype(get_canonical_unit_impl(power<F, Num, Den...>{}, typename decltype(base::reference_unit)::_num_{}));
using den =
decltype(get_canonical_unit_impl(power<F, Num, Den...>{}, typename decltype(base::reference_unit)::_den_{}));
return canonical_unit<decltype(decltype(pow<Num, Den...>(base::mag) * num::mag){} / den::mag){},
decltype(num::reference_unit / den::reference_unit){}>{};
} else {
return canonical_unit{pow<Num, Den...>(base.mag),
derived_unit<power<decltype(base.reference_unit), Num, Den...>>{}};
return canonical_unit<decltype(pow<Num, Den...>(base::mag)){},
derived_unit<power<std::remove_const_t<decltype(base::reference_unit)>, Num, Den...>>{}>{};
}
}

template<typename... Us>
[[nodiscard]] consteval auto get_canonical_unit_impl(const type_list<Us...>&)
{
auto m = (mp_units::mag<1> * ... * get_canonical_unit_impl(Us{}, Us{}).mag);
auto u = (one * ... * get_canonical_unit_impl(Us{}, Us{}).reference_unit);
return canonical_unit{m, u};
using m = decltype((mp_units::mag<1> * ... * decltype(get_canonical_unit_impl(Us{}, Us{}))::mag));
using u = decltype((one * ... * decltype(get_canonical_unit_impl(Us{}, Us{}))::reference_unit));
return canonical_unit<m{}, u{}>{};
}

template<Unit T, typename... Expr>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit<Expr...>&)
{
auto num = get_canonical_unit_impl(typename derived_unit<Expr...>::_num_{});
auto den = get_canonical_unit_impl(typename derived_unit<Expr...>::_den_{});
return canonical_unit{num.mag / den.mag, num.reference_unit / den.reference_unit};
using num = decltype(get_canonical_unit_impl(typename derived_unit<Expr...>::_num_{}));
using den = decltype(get_canonical_unit_impl(typename derived_unit<Expr...>::_den_{}));
return canonical_unit<decltype(num::mag / den::mag){}, decltype(num::reference_unit / den::reference_unit){}>{};
}

template<Unit Lhs, Unit Rhs>
Expand All @@ -422,7 +409,10 @@ using type_list_of_unit_less = expr_less<T1, T2, unit_less>;

// TODO this should really be in the `details` namespace but is used in `chrono.h` (a part of mp_units.systems)
// Even though it is not exported, it is visible to the other module via ADL
[[nodiscard]] consteval auto get_canonical_unit(Unit auto u) { return detail::get_canonical_unit_impl(u, u); }
[[nodiscard]] consteval auto get_canonical_unit(Unit auto u)
{
return decltype(detail::get_canonical_unit_impl(u, u)){};
}

MP_UNITS_EXPORT_BEGIN

Expand All @@ -448,7 +438,7 @@ template<Magnitude M, Unit U>
template<Magnitude M, Unit U>
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator/(M mag, U u)
{
return mag * inverse(u);
return decltype(mag * inverse(u)){};
}

/**
Expand All @@ -460,13 +450,13 @@ template<Unit Lhs, Unit Rhs>
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator*(Lhs lhs, Rhs rhs)
{
if constexpr (detail::is_specialization_of_scaled_unit<Lhs> && detail::is_specialization_of_scaled_unit<Rhs>)
return (Lhs::mag * Rhs::mag) * (Lhs::reference_unit * Rhs::reference_unit);
return decltype(Lhs::mag * Rhs::mag){} * decltype(Lhs::reference_unit * Rhs::reference_unit){};
else if constexpr (detail::is_specialization_of_scaled_unit<Lhs>)
return Lhs::mag * (Lhs::reference_unit * rhs);
return decltype(Lhs::mag * decltype(Lhs::reference_unit * rhs){}){};
else if constexpr (detail::is_specialization_of_scaled_unit<Rhs>)
return Rhs::mag * (lhs * Rhs::reference_unit);
return decltype(Rhs::mag * decltype(lhs * Rhs::reference_unit){}){};
else
return detail::expr_multiply<derived_unit, struct one, detail::type_list_of_unit_less>(lhs, rhs);
return decltype(detail::expr_multiply<derived_unit, struct one, detail::type_list_of_unit_less>(lhs, rhs)){};
}

/**
Expand All @@ -478,69 +468,36 @@ template<Unit Lhs, Unit Rhs>
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator/(Lhs lhs, Rhs rhs)
{
if constexpr (detail::is_specialization_of_scaled_unit<Lhs> && detail::is_specialization_of_scaled_unit<Rhs>)
return (Lhs::mag / Rhs::mag) * (Lhs::reference_unit / Rhs::reference_unit);
return decltype(Lhs::mag / Rhs::mag){} * decltype(Lhs::reference_unit / Rhs::reference_unit){};
else if constexpr (detail::is_specialization_of_scaled_unit<Lhs>)
return Lhs::mag * (Lhs::reference_unit / rhs);
return Lhs::mag * decltype(Lhs::reference_unit / rhs){};
else if constexpr (detail::is_specialization_of_scaled_unit<Rhs>)
return mag<1> / Rhs::mag * (lhs / Rhs::reference_unit);
return decltype(mag<1> / Rhs::mag){} * decltype(lhs / Rhs::reference_unit){};
else
return detail::expr_divide<derived_unit, struct one, detail::type_list_of_unit_less>(lhs, rhs);
}

[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto inverse(Unit auto u) { return one / u; }
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto inverse(Unit auto u) { return decltype(one / u){}; }

MP_UNITS_EXPORT_END

namespace detail {

[[nodiscard]] consteval bool have_same_canonical_reference_unit_impl(...) { return false; }

template<symbol_text Symbol, auto... D>
[[nodiscard]] consteval bool have_same_canonical_reference_unit_impl(const named_unit<Symbol, D...>&,
const named_unit<Symbol, D...>&)
{
return true;
}

template<typename F1, typename F2, auto... Vs>
[[nodiscard]] consteval bool have_same_canonical_reference_unit_impl(const power<F1, Vs...>&, const power<F2, Vs...>&)
{
return have_same_canonical_reference_unit_impl(F1{}, F2{});
}

template<typename... Us1, typename... Us2>
requires(sizeof...(Us1) == sizeof...(Us2))
[[nodiscard]] consteval bool have_same_canonical_reference_unit_impl(const type_list<Us1...>&, const type_list<Us2...>&)
{
return (... && have_same_canonical_reference_unit_impl(Us1{}, Us2{}));
}

template<typename... Expr1, typename... Expr2>
[[nodiscard]] consteval bool have_same_canonical_reference_unit_impl(const derived_unit<Expr1...>&,
const derived_unit<Expr2...>&)
{
return have_same_canonical_reference_unit_impl(typename derived_unit<Expr1...>::_num_{},
typename derived_unit<Expr2...>::_num_{}) &&
have_same_canonical_reference_unit_impl(typename derived_unit<Expr1...>::_den_{},
typename derived_unit<Expr2...>::_den_{});
}

[[nodiscard]] consteval bool have_same_canonical_reference_unit(Unit auto u1, Unit auto u2)
[[nodiscard]] consteval auto have_same_canonical_reference_unit(Unit auto u1, Unit auto u2)
{
auto canonical_lhs = get_canonical_unit(u1);
auto canonical_rhs = get_canonical_unit(u2);
return have_same_canonical_reference_unit_impl(canonical_lhs.reference_unit, canonical_rhs.reference_unit);
using canonical_lhs = decltype(get_canonical_unit(u1));
using canonical_rhs = decltype(get_canonical_unit(u2));
return std::is_same<decltype(canonical_lhs::reference_unit), decltype(canonical_rhs::reference_unit)>{};
}

} // namespace detail


MP_UNITS_EXPORT [[nodiscard]] consteval bool operator==(Unit auto lhs, Unit auto rhs)
MP_UNITS_EXPORT template<Unit U1, Unit U2>
[[nodiscard]] consteval bool operator==(U1 lhs, U2 rhs)
{
auto canonical_lhs = get_canonical_unit(lhs);
auto canonical_rhs = get_canonical_unit(rhs);
return detail::have_same_canonical_reference_unit(canonical_lhs.reference_unit, canonical_rhs.reference_unit) &&
canonical_lhs.mag == canonical_rhs.mag;
return decltype(detail::have_same_canonical_reference_unit(lhs, rhs))::value &&
decltype(get_canonical_unit(lhs))::mag == decltype(get_canonical_unit(rhs))::mag;
}

namespace detail {
Expand Down Expand Up @@ -575,7 +532,7 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Unit U>
else if constexpr (detail::is_specialization_of_scaled_unit<U>)
return scaled_unit<pow<Num, Den>(U::mag), decltype(pow<Num, Den>(U::reference_unit))>{};
else if constexpr (detail::is_specialization_of_derived_unit<U>)
return detail::expr_pow<Num, Den, derived_unit, struct one, detail::type_list_of_unit_less>(u);
return decltype(detail::expr_pow<Num, Den, derived_unit, struct one, detail::type_list_of_unit_less>(u)){};
else if constexpr (Den == 1)
return derived_unit<power<U, Num>>{};
else
Expand All @@ -589,7 +546,7 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Unit U>
*
* @return Unit The result of computation
*/
[[nodiscard]] consteval Unit auto sqrt(Unit auto u) { return pow<1, 2>(u); }
[[nodiscard]] consteval Unit auto sqrt(Unit auto u) { return decltype(pow<1, 2>(u)){}; }

/**
* @brief Computes the cubic root of a unit
Expand All @@ -598,7 +555,7 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Unit U>
*
* @return Unit The result of computation
*/
[[nodiscard]] consteval Unit auto cbrt(Unit auto u) { return pow<1, 3>(u); }
[[nodiscard]] consteval Unit auto cbrt(Unit auto u) { return decltype(pow<1, 3>(u)){}; }

/**
* @brief Computes the square power of a unit
Expand All @@ -607,7 +564,7 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Unit U>
*
* @return Unit The result of computation
*/
[[nodiscard]] consteval Unit auto square(Unit auto u) { return pow<2>(u); }
[[nodiscard]] consteval Unit auto square(Unit auto u) { return decltype(pow<2>(u)){}; }

/**
* @brief Computes the cubic power of a unit
Expand All @@ -616,7 +573,7 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Unit U>
*
* @return Unit The result of computation
*/
[[nodiscard]] consteval Unit auto cubic(Unit auto u) { return pow<3>(u); }
[[nodiscard]] consteval Unit auto cubic(Unit auto u) { return decltype(pow<3>(u)){}; }


// common dimensionless units
Expand All @@ -631,15 +588,15 @@ inline constexpr auto ppm = parts_per_million;
// convertible_to
[[nodiscard]] consteval bool convertible(Unit auto from, Unit auto to)
{
return detail::have_same_canonical_reference_unit(from, to);
return decltype(detail::have_same_canonical_reference_unit(from, to))::value;
}

// Common unit
[[nodiscard]] consteval Unit auto common_unit(Unit auto u) { return u; }

template<Unit U1, Unit U2>
[[nodiscard]] consteval Unit auto common_unit(U1 u1, U2 u2)
requires(detail::have_same_canonical_reference_unit(u1, u2))
requires(decltype(detail::have_same_canonical_reference_unit(u1, u2))::value)
{
if constexpr (U1{} == U2{}) {
if constexpr (std::derived_from<U1, U2>)
Expand All @@ -648,26 +605,26 @@ template<Unit U1, Unit U2>
return u2;
else
// TODO Check if there is a better choice here
return detail::better_type_name(u1, u2);
return decltype(detail::better_type_name(u1, u2)){};
} else {
constexpr auto canonical_lhs = get_canonical_unit(U1{});
constexpr auto canonical_rhs = get_canonical_unit(U2{});
using canonical_lhs = decltype(get_canonical_unit(U1{}));
using canonical_rhs = decltype(get_canonical_unit(U2{}));

if constexpr (is_integral(canonical_lhs.mag / canonical_rhs.mag))
if constexpr (is_integral(decltype(canonical_lhs::mag / canonical_rhs::mag){}))
return u2;
else if constexpr (is_integral(canonical_rhs.mag / canonical_lhs.mag))
else if constexpr (is_integral(decltype(canonical_rhs::mag / canonical_lhs::mag){}))
return u1;
else {
constexpr auto cm = detail::common_magnitude(canonical_lhs.mag, canonical_rhs.mag);
return scaled_unit<cm, decltype(canonical_lhs.reference_unit)>{};
constexpr auto cm = decltype(detail::common_magnitude(canonical_lhs::mag, canonical_rhs::mag)){};
return scaled_unit<cm, std::remove_const_t<decltype(canonical_lhs::reference_unit)>>{};
}
}
}

[[nodiscard]] consteval Unit auto common_unit(Unit auto u1, Unit auto u2, Unit auto u3, Unit auto... rest)
requires requires { common_unit(common_unit(u1, u2), u3, rest...); }
{
return common_unit(common_unit(u1, u2), u3, rest...);
return decltype(common_unit(common_unit(u1, u2), u3, rest...)){};
}


Expand Down
Loading

0 comments on commit 760d485

Please sign in to comment.