refactor: unit-related constexpr functions compile-time evaluation limited to only one that determines the return type

This commit is contained in:
Mateusz Pusz
2024-06-05 08:24:33 +02:00
parent fb77585593
commit 760d48502d
5 changed files with 79 additions and 123 deletions

View File

@ -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<

View File

@ -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 =

View File

@ -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...>&);
@ -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>
@ -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
@ -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)){};
}
/**
@ -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)){};
}
/**
@ -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...>&)
[[nodiscard]] consteval auto have_same_canonical_reference_unit(Unit auto u1, Unit auto u2)
{
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)
{
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 {
@ -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
@ -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
@ -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
@ -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
@ -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
@ -631,7 +588,7 @@ 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
@ -639,7 +596,7 @@ inline constexpr auto ppm = parts_per_million;
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>)
@ -648,18 +605,18 @@ 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)>>{};
}
}
}
@ -667,7 +624,7 @@ template<Unit U1, Unit U2>
[[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...)){};
}

View File

@ -33,9 +33,8 @@ namespace mp_units {
namespace detail {
// do not refactor below to a variable template - GCC-11 does not like it
template<typename T>
struct is_unit : std::false_type {};
inline constexpr bool is_unit = false;
} // namespace detail
@ -45,7 +44,7 @@ struct is_unit : std::false_type {};
* Satisfied by all unit types provided by the library.
*/
MP_UNITS_EXPORT template<typename T>
concept Unit = detail::is_unit<T>::value;
concept Unit = detail::is_unit<T>;
template<Magnitude auto M, Unit U>
struct scaled_unit;
@ -145,7 +144,7 @@ inline constexpr bool is_specialization_of_prefixed_unit<prefixed_unit<Symbol, M
template<typename T>
requires requires(T* t) { is_unit_impl(t); } && (!is_specialization_of_named_unit<T>) &&
(!is_specialization_of_prefixed_unit<T>)
struct is_unit<T> : std::true_type {};
inline constexpr bool is_unit<T> = true;
template<Unit U>
[[nodiscard]] consteval bool has_associated_quantity(U);
@ -197,7 +196,7 @@ concept UnitOf =
namespace detail {
[[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);
}
@ -210,7 +209,7 @@ namespace detail {
MP_UNITS_EXPORT template<typename U, auto U2, auto QS>
concept UnitCompatibleWith =
Unit<U> && Unit<MP_UNITS_REMOVE_CONST(decltype(U2))> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> &&
(!AssociatedUnit<U> || UnitOf<U, QS>)&&detail::have_same_canonical_reference_unit(U{}, U2);
(!AssociatedUnit<U> || UnitOf<U, QS>)&&decltype(detail::have_same_canonical_reference_unit(U{}, U2))::value;
} // namespace mp_units

View File

@ -116,8 +116,7 @@ struct quantity_point_like_traits<std::chrono::time_point<C, std::chrono::durati
template<QuantityOf<isq::time> Q>
[[nodiscard]] constexpr auto to_chrono_duration(const Q& q)
{
constexpr auto canonical = get_canonical_unit(Q::unit);
constexpr detail::ratio r = detail::as_ratio(canonical.mag);
constexpr detail::ratio r = detail::as_ratio(decltype(get_canonical_unit(Q::unit))::mag);
return std::chrono::duration<typename Q::rep, std::ratio<r.num, r.den>>{q};
}
@ -127,8 +126,7 @@ template<QuantityPointOf<isq::time> QP>
{
using clock = MP_UNITS_TYPENAME decltype(QP::absolute_point_origin)::clock;
using rep = MP_UNITS_TYPENAME QP::rep;
constexpr auto canonical = get_canonical_unit(QP::unit);
constexpr detail::ratio r = detail::as_ratio(canonical.mag);
constexpr detail::ratio r = detail::as_ratio(decltype(get_canonical_unit(QP::unit))::mag);
using ret_type = std::chrono::time_point<clock, std::chrono::duration<rep, std::ratio<r.num, r.den>>>;
return ret_type(to_chrono_duration(qp - qp.absolute_point_origin));
}