refactor: compile-time optimizations reverted

This commit is contained in:
Mateusz Pusz
2024-06-14 13:30:57 +02:00
parent b7f09dbf34
commit f49b4c6f5f
15 changed files with 621 additions and 680 deletions

View File

@@ -34,25 +34,24 @@ template<AssociatedUnit U>
template<typename U, auto... Vs> template<typename U, auto... Vs>
[[nodiscard]] consteval auto all_are_kinds(power<U, Vs...>) [[nodiscard]] consteval auto all_are_kinds(power<U, Vs...>)
{ {
return decltype(all_are_kinds(U{})){}; return all_are_kinds(U{});
} }
template<typename... Nums, typename... Dens> template<typename... Nums, typename... Dens>
[[nodiscard]] consteval auto all_are_kinds(type_list<Nums...>, type_list<Dens...>) [[nodiscard]] consteval bool all_are_kinds(type_list<Nums...>, type_list<Dens...>)
{ {
return std::bool_constant<((... && decltype(all_are_kinds(Nums{}))::value) && return (... && all_are_kinds(Nums{})) && (... && all_are_kinds(Dens{}));
(... && decltype(all_are_kinds(Dens{}))::value))>{};
} }
template<AssociatedUnit U> template<AssociatedUnit U>
[[nodiscard]] consteval auto all_are_kinds(U) [[nodiscard]] consteval auto all_are_kinds(U)
{ {
if constexpr (requires { U::quantity_spec; }) if constexpr (requires { U::quantity_spec; })
return std::bool_constant<QuantityKindSpec<std::remove_const_t<decltype(U::quantity_spec)>>>{}; return QuantityKindSpec<std::remove_const_t<decltype(U::quantity_spec)>>;
else if constexpr (requires { U::reference_unit; }) else if constexpr (requires { U::reference_unit; })
return decltype(all_are_kinds(U::reference_unit)){}; return all_are_kinds(U::reference_unit);
else if constexpr (requires { typename U::_num_; }) { else if constexpr (requires { typename U::_num_; }) {
return decltype(all_are_kinds(typename U::_num_{}, typename U::_den_{})){}; return all_are_kinds(typename U::_num_{}, typename U::_den_{});
} }
} }
@@ -68,21 +67,20 @@ template<AssociatedUnit U>
if constexpr (requires { U::quantity_spec; }) if constexpr (requires { U::quantity_spec; })
return remove_kind(U::quantity_spec); return remove_kind(U::quantity_spec);
else if constexpr (requires { U::reference_unit; }) else if constexpr (requires { U::reference_unit; })
return decltype(get_associated_quantity_impl(U::reference_unit)){}; return get_associated_quantity_impl(U::reference_unit);
else if constexpr (requires { typename U::_num_; }) { else if constexpr (requires { typename U::_num_; }) {
return decltype(expr_map<to_quantity_spec, derived_quantity_spec, struct dimensionless, return expr_map<to_quantity_spec, derived_quantity_spec, struct dimensionless, type_list_of_quantity_spec_less>(u);
type_list_of_quantity_spec_less>(u)){};
} }
} }
template<AssociatedUnit U> template<AssociatedUnit U>
[[nodiscard]] consteval auto get_associated_quantity(U u) [[nodiscard]] consteval auto get_associated_quantity(U u)
{ {
constexpr bool all_kinds = decltype(all_are_kinds(u)){}; constexpr bool all_kinds = all_are_kinds(u);
if constexpr (all_kinds) if constexpr (all_kinds)
return kind_of<decltype(get_associated_quantity_impl(u)){}>; return kind_of<get_associated_quantity_impl(u)>;
else else
return decltype(get_associated_quantity_impl(u)){}; return get_associated_quantity_impl(u);
} }
} // namespace mp_units::detail } // namespace mp_units::detail

View File

@@ -26,12 +26,12 @@
namespace mp_units::detail { namespace mp_units::detail {
[[nodiscard]] consteval auto hierarchy_path_length(QuantitySpec auto q) [[nodiscard]] consteval std::size_t hierarchy_path_length(QuantitySpec auto q)
{ {
if constexpr (requires { q._parent_; }) if constexpr (requires { q._parent_; })
return std::integral_constant<std::size_t, decltype(hierarchy_path_length(q._parent_))::value + 1>{}; return hierarchy_path_length(q._parent_) + 1;
else else
return std::integral_constant<std::size_t, 1>{}; return 1;
} }
template<std::size_t Offset> template<std::size_t Offset>
@@ -41,67 +41,65 @@ template<std::size_t Offset>
if constexpr (Offset == 0) if constexpr (Offset == 0)
return q; return q;
else if constexpr (requires { q._parent_; }) else if constexpr (requires { q._parent_; })
return decltype(hierarchy_path_advance<Offset - 1>(q._parent_)){}; return hierarchy_path_advance<Offset - 1>(q._parent_);
} }
template<QuantitySpec A, QuantitySpec B> template<QuantitySpec A, QuantitySpec B>
[[nodiscard]] consteval auto have_common_base_in_hierarchy_of_equal_length(A a, B b) [[nodiscard]] consteval bool have_common_base_in_hierarchy_of_equal_length(A a, B b)
{ {
if constexpr (is_same_v<A, B>) if constexpr (is_same_v<A, B>)
return std::true_type{}; return true;
else if constexpr (requires { a._parent_; }) else if constexpr (requires { a._parent_; })
return decltype(have_common_base_in_hierarchy_of_equal_length(a._parent_, b._parent_)){}; return have_common_base_in_hierarchy_of_equal_length(a._parent_, b._parent_);
else else
return std::false_type{}; return false;
} }
template<QuantitySpec A, QuantitySpec B> template<QuantitySpec A, QuantitySpec B>
[[nodiscard]] consteval auto have_common_base(A a, B b) [[nodiscard]] consteval bool have_common_base(A a, B b)
{ {
constexpr std::size_t a_length = decltype(hierarchy_path_length(A{}))::value; constexpr std::size_t a_length = hierarchy_path_length(A{});
constexpr std::size_t b_length = decltype(hierarchy_path_length(B{}))::value; constexpr std::size_t b_length = hierarchy_path_length(B{});
if constexpr (a_length > b_length) if constexpr (a_length > b_length)
return decltype(have_common_base_in_hierarchy_of_equal_length( return have_common_base_in_hierarchy_of_equal_length(hierarchy_path_advance<a_length - b_length>(a), b);
decltype(hierarchy_path_advance<a_length - b_length>(a)){}, b)){};
else else
return decltype(have_common_base_in_hierarchy_of_equal_length( return have_common_base_in_hierarchy_of_equal_length(a, hierarchy_path_advance<b_length - a_length>(b));
a, decltype(hierarchy_path_advance<b_length - a_length>(b)){})){};
} }
template<QuantitySpec A, QuantitySpec B> template<QuantitySpec A, QuantitySpec B>
requires(decltype(have_common_base_in_hierarchy_of_equal_length(A{}, B{}))::value) requires(have_common_base_in_hierarchy_of_equal_length(A{}, B{}))
[[nodiscard]] consteval QuantitySpec auto get_common_base_for_hierarchy_of_equal_length(A a, B b) [[nodiscard]] consteval QuantitySpec auto get_common_base_for_hierarchy_of_equal_length(A a, B b)
{ {
if constexpr (is_same_v<A, B>) if constexpr (is_same_v<A, B>)
return a; return a;
else else
return decltype(get_common_base_for_hierarchy_of_equal_length(a._parent_, b._parent_)){}; return get_common_base_for_hierarchy_of_equal_length(a._parent_, b._parent_);
} }
template<QuantitySpec A, QuantitySpec B> template<QuantitySpec A, QuantitySpec B>
requires(decltype(have_common_base(A{}, B{}))::value) requires(have_common_base(A{}, B{}))
[[nodiscard]] consteval QuantitySpec auto get_common_base(A a, B b) [[nodiscard]] consteval QuantitySpec auto get_common_base(A a, B b)
{ {
constexpr std::size_t a_length = decltype(hierarchy_path_length(A{}))::value; constexpr std::size_t a_length = hierarchy_path_length(A{});
constexpr std::size_t b_length = decltype(hierarchy_path_length(B{}))::value; constexpr std::size_t b_length = hierarchy_path_length(B{});
if constexpr (a_length > b_length) if constexpr (a_length > b_length)
return decltype(get_common_base_for_hierarchy_of_equal_length(hierarchy_path_advance<a_length - b_length>(a), b)){}; return get_common_base_for_hierarchy_of_equal_length(hierarchy_path_advance<a_length - b_length>(a), b);
else else
return decltype(get_common_base_for_hierarchy_of_equal_length(a, hierarchy_path_advance<b_length - a_length>(b))){}; return get_common_base_for_hierarchy_of_equal_length(a, hierarchy_path_advance<b_length - a_length>(b));
} }
template<QuantitySpec Child, QuantitySpec Parent> template<QuantitySpec Child, QuantitySpec Parent>
[[nodiscard]] consteval auto is_child_of(Child ch, Parent) [[nodiscard]] consteval bool is_child_of(Child ch, Parent p)
{ {
if constexpr (Child{} == Parent{}) if constexpr (Child{} == Parent{})
return std::true_type{}; return std::true_type{};
else { else {
constexpr std::size_t child_length = decltype(hierarchy_path_length(Child{}))::value; constexpr std::size_t child_length = hierarchy_path_length(Child{});
constexpr std::size_t parent_length = decltype(hierarchy_path_length(Parent{}))::value; constexpr std::size_t parent_length = hierarchy_path_length(Parent{});
if constexpr (parent_length > child_length) if constexpr (parent_length > child_length)
return std::false_type{}; return false;
else else
return std::bool_constant<hierarchy_path_advance<child_length - parent_length>(ch) == Parent{}>{}; return hierarchy_path_advance<child_length - parent_length>(ch) == p;
} }
} }

View File

@@ -64,11 +64,10 @@ template<Quantity To, typename From>
// warnings on conversions // warnings on conversions
} else { } else {
// scale the number // scale the number
constexpr Magnitude auto c_mag = constexpr Magnitude auto c_mag = get_canonical_unit(q_unit).mag / get_canonical_unit(To::unit).mag;
decltype(decltype(get_canonical_unit(q_unit))::mag / decltype(get_canonical_unit(To::unit))::mag){}; constexpr Magnitude auto num = numerator(c_mag);
constexpr Magnitude auto num = decltype(numerator(c_mag)){}; constexpr Magnitude auto den = denominator(c_mag);
constexpr Magnitude auto den = decltype(denominator(c_mag)){}; constexpr Magnitude auto irr = c_mag * (den / num);
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_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 c_mag_type = common_magnitude_type<c_mag>;
using multiplier_type = conditional< using multiplier_type = conditional<

View File

@@ -157,30 +157,22 @@ struct wheel_factorizer {
static constexpr auto coprimes_in_first_wheel = static constexpr auto coprimes_in_first_wheel =
coprimes_up_to<num_coprimes_up_to(wheel_size, basis)>(wheel_size, basis); coprimes_up_to<num_coprimes_up_to(wheel_size, basis)>(wheel_size, basis);
template<std::size_t N> [[nodiscard]] static consteval std::uintmax_t find_first_factor(std::uintmax_t n)
[[nodiscard]] static consteval auto find_first_factor()
{ {
constexpr std::uintmax_t res = [] { if (const auto k = detail::get_first_of(basis, [&](auto p) { return first_factor_maybe(n, p); })) return *k;
if (const auto k = detail::get_first_of(basis, [&](auto p) { return first_factor_maybe(N, p); })) return *k;
if (const auto k = detail::get_first_of(std::next(begin(coprimes_in_first_wheel)), end(coprimes_in_first_wheel), if (const auto k = detail::get_first_of(std::next(begin(coprimes_in_first_wheel)), end(coprimes_in_first_wheel),
[&](auto p) { return first_factor_maybe(N, p); })) [&](auto p) { return first_factor_maybe(n, p); }))
return *k; return *k;
for (std::size_t wheel = wheel_size; wheel < N; wheel += wheel_size) for (std::size_t wheel = wheel_size; wheel < n; wheel += wheel_size)
if (const auto k = if (const auto k =
detail::get_first_of(coprimes_in_first_wheel, [&](auto p) { return first_factor_maybe(N, wheel + p); })) detail::get_first_of(coprimes_in_first_wheel, [&](auto p) { return first_factor_maybe(n, wheel + p); }))
return *k; return *k;
return N; return n;
}();
return std::integral_constant<std::uintmax_t, res>{};
} }
template<std::size_t N> [[nodiscard]] static consteval bool is_prime(std::size_t n) { return (n > 1) && find_first_factor(n) == n; }
[[nodiscard]] static consteval auto is_prime()
{
return std::bool_constant<(N > 1 && decltype(find_first_factor<N>())::value == N)>{};
}
}; };
} // namespace mp_units::detail } // namespace mp_units::detail

View File

@@ -159,15 +159,15 @@ struct is_dimension_one<struct dimension_one> : std::true_type {};
MP_UNITS_EXPORT template<Dimension Lhs, Dimension Rhs> MP_UNITS_EXPORT template<Dimension Lhs, Dimension Rhs>
[[nodiscard]] consteval Dimension auto operator*(Lhs, Rhs) [[nodiscard]] consteval Dimension auto operator*(Lhs, Rhs)
{ {
return decltype(detail::expr_multiply<derived_dimension, struct dimension_one, return detail::expr_multiply<derived_dimension, struct dimension_one, detail::type_list_of_base_dimension_less>(
detail::type_list_of_base_dimension_less>(Lhs{}, Rhs{})){}; Lhs{}, Rhs{});
} }
MP_UNITS_EXPORT template<Dimension Lhs, Dimension Rhs> MP_UNITS_EXPORT template<Dimension Lhs, Dimension Rhs>
[[nodiscard]] consteval Dimension auto operator/(Lhs, Rhs) [[nodiscard]] consteval Dimension auto operator/(Lhs, Rhs)
{ {
return decltype(detail::expr_divide<derived_dimension, struct dimension_one, return detail::expr_divide<derived_dimension, struct dimension_one, detail::type_list_of_base_dimension_less>(Lhs{},
detail::type_list_of_base_dimension_less>(Lhs{}, Rhs{})){}; Rhs{});
} }
namespace detail { namespace detail {
@@ -191,7 +191,7 @@ template<Dimension Lhs, Dimension Rhs>
return is_same_v<Lhs, Rhs> || detail::derived_from_the_same_base_dimension(lhs, rhs); return is_same_v<Lhs, Rhs> || detail::derived_from_the_same_base_dimension(lhs, rhs);
} }
[[nodiscard]] consteval Dimension auto inverse(Dimension auto d) { return decltype(dimension_one / d){}; } [[nodiscard]] consteval Dimension auto inverse(Dimension auto d) { return dimension_one / d; }
/** /**
* @brief Computes the value of a dimension raised to the `Num/Den` power * @brief Computes the value of a dimension raised to the `Num/Den` power
@@ -212,8 +212,8 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Dimension D>
else else
return derived_dimension<power<D, Num, Den>>{}; return derived_dimension<power<D, Num, Den>>{};
} else } else
return decltype(detail::expr_pow<Num, Den, derived_dimension, struct dimension_one, return detail::expr_pow<Num, Den, derived_dimension, struct dimension_one,
detail::type_list_of_base_dimension_less>(d)){}; detail::type_list_of_base_dimension_less>(d);
} }
/** /**
@@ -223,7 +223,7 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Dimension D>
* *
* @return Dimension The result of computation * @return Dimension The result of computation
*/ */
[[nodiscard]] consteval Dimension auto sqrt(Dimension auto d) { return decltype(pow<1, 2>(d)){}; } [[nodiscard]] consteval Dimension auto sqrt(Dimension auto d) { return pow<1, 2>(d); }
/** /**
* @brief Computes the cubic root of a dimension * @brief Computes the cubic root of a dimension
@@ -232,7 +232,7 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Dimension D>
* *
* @return Dimension The result of computation * @return Dimension The result of computation
*/ */
[[nodiscard]] consteval Dimension auto cbrt(Dimension auto d) { return decltype(pow<1, 3>(d)){}; } [[nodiscard]] consteval Dimension auto cbrt(Dimension auto d) { return pow<1, 3>(d); }
struct dimension_symbol_formatting { struct dimension_symbol_formatting {

View File

@@ -362,7 +362,7 @@ template<typename NumList, typename DenList, typename OneType, template<typename
using simple = expr_simplify<num_list, den_list, Pred>; using simple = expr_simplify<num_list, den_list, Pred>;
// the usage of `std::identity` below helps to resolve an using alias identifier to the actual // the usage of `std::identity` below helps to resolve an using alias identifier to the actual
// type identifier in the clang compile-time errors // type identifier in the clang compile-time errors
return std::identity{}(decltype(expr_make_spec_impl<typename simple::num, typename simple::den, OneType, To>()){}); return std::identity{}(expr_make_spec_impl<typename simple::num, typename simple::den, OneType, To>());
} }
/** /**
@@ -384,19 +384,19 @@ template<template<typename...> typename To, typename OneType, template<typename,
return Lhs{}; return Lhs{};
} else if constexpr (is_specialization_of<Lhs, To> && is_specialization_of<Rhs, To>) { } else if constexpr (is_specialization_of<Lhs, To> && is_specialization_of<Rhs, To>) {
// two derived dimensions // two derived dimensions
return decltype(get_optimized_expression<type_list_merge_sorted<typename Lhs::_num_, typename Rhs::_num_, Pred>, return get_optimized_expression<type_list_merge_sorted<typename Lhs::_num_, typename Rhs::_num_, Pred>,
type_list_merge_sorted<typename Lhs::_den_, typename Rhs::_den_, Pred>, type_list_merge_sorted<typename Lhs::_den_, typename Rhs::_den_, Pred>, OneType,
OneType, Pred, To>()){}; Pred, To>();
} else if constexpr (is_specialization_of<Lhs, To>) { } else if constexpr (is_specialization_of<Lhs, To>) {
return decltype(get_optimized_expression<type_list_merge_sorted<typename Lhs::_num_, type_list<Rhs>, Pred>, return get_optimized_expression<type_list_merge_sorted<typename Lhs::_num_, type_list<Rhs>, Pred>,
typename Lhs::_den_, OneType, Pred, To>()){}; typename Lhs::_den_, OneType, Pred, To>();
} else if constexpr (is_specialization_of<Rhs, To>) { } else if constexpr (is_specialization_of<Rhs, To>) {
return decltype(get_optimized_expression<type_list_merge_sorted<typename Rhs::_num_, type_list<Lhs>, Pred>, return get_optimized_expression<type_list_merge_sorted<typename Rhs::_num_, type_list<Lhs>, Pred>,
typename Rhs::_den_, OneType, Pred, To>()){}; typename Rhs::_den_, OneType, Pred, To>();
} else { } else {
// two base dimensions // two base dimensions
return decltype(get_optimized_expression<type_list_merge_sorted<type_list<Lhs>, type_list<Rhs>, Pred>, type_list<>, return get_optimized_expression<type_list_merge_sorted<type_list<Lhs>, type_list<Rhs>, Pred>, type_list<>, OneType,
OneType, Pred, To>()){}; Pred, To>();
} }
} }
@@ -418,19 +418,18 @@ template<template<typename...> typename To, typename OneType, template<typename,
} else if constexpr (is_same_v<Rhs, OneType>) { } else if constexpr (is_same_v<Rhs, OneType>) {
return lhs; return lhs;
} else if constexpr (is_same_v<Lhs, OneType>) { } else if constexpr (is_same_v<Lhs, OneType>) {
return decltype(expr_divide<To, OneType, Pred>(To<>{}, rhs)){}; return expr_divide<To, OneType, Pred>(To<>{}, rhs);
} else if constexpr (is_specialization_of<Lhs, To> && is_specialization_of<Rhs, To>) { } else if constexpr (is_specialization_of<Lhs, To> && is_specialization_of<Rhs, To>) {
// two derived entities // two derived entities
return decltype(get_optimized_expression<type_list_merge_sorted<typename Lhs::_num_, typename Rhs::_den_, Pred>, return get_optimized_expression<type_list_merge_sorted<typename Lhs::_num_, typename Rhs::_den_, Pred>,
type_list_merge_sorted<typename Lhs::_den_, typename Rhs::_num_, Pred>, type_list_merge_sorted<typename Lhs::_den_, typename Rhs::_num_, Pred>, OneType,
OneType, Pred, To>()){}; Pred, To>();
} else if constexpr (is_specialization_of<Lhs, To>) { } else if constexpr (is_specialization_of<Lhs, To>) {
return decltype(get_optimized_expression<typename Lhs::_num_, return get_optimized_expression<
type_list_merge_sorted<typename Lhs::_den_, type_list<Rhs>, Pred>, OneType, typename Lhs::_num_, type_list_merge_sorted<typename Lhs::_den_, type_list<Rhs>, Pred>, OneType, Pred, To>();
Pred, To>()){};
} else if constexpr (is_specialization_of<Rhs, To>) { } else if constexpr (is_specialization_of<Rhs, To>) {
return decltype(get_optimized_expression<type_list_merge_sorted<typename Rhs::_den_, type_list<Lhs>, Pred>, return get_optimized_expression<type_list_merge_sorted<typename Rhs::_den_, type_list<Lhs>, Pred>,
typename Rhs::_num_, OneType, Pred, To>()){}; typename Rhs::_num_, OneType, Pred, To>();
} else { } else {
// two named entities // two named entities
return To<Lhs, per<Rhs>>{}; return To<Lhs, per<Rhs>>{};
@@ -450,7 +449,7 @@ template<template<typename...> typename To, typename OneType, typename T>
if constexpr (is_specialization_of<T, To>) if constexpr (is_specialization_of<T, To>)
// the usage of `std::identity` below helps to resolve an using alias identifier to the actual // the usage of `std::identity` below helps to resolve an using alias identifier to the actual
// type identifier in the clang compile-time errors // type identifier in the clang compile-time errors
return std::identity{}(decltype(expr_make_spec_impl<typename T::_den_, typename T::_num_, OneType, To>()){}); return std::identity{}(expr_make_spec_impl<typename T::_den_, typename T::_num_, OneType, To>());
else else
return To<OneType, per<T>>{}; return To<OneType, per<T>>{};
} }
@@ -461,9 +460,8 @@ template<std::intmax_t Num, std::intmax_t Den, template<typename...> typename To
requires detail::non_zero<Den> requires detail::non_zero<Den>
[[nodiscard]] consteval auto expr_pow_impl(type_list<Nums...>, type_list<Dens...>) [[nodiscard]] consteval auto expr_pow_impl(type_list<Nums...>, type_list<Dens...>)
{ {
return decltype(detail::get_optimized_expression<type_list<power_or_T<Nums, ratio{Num, Den}>...>, return detail::get_optimized_expression<type_list<power_or_T<Nums, ratio{Num, Den}>...>,
type_list<power_or_T<Dens, ratio{Num, Den}>...>, OneType, Pred, type_list<power_or_T<Dens, ratio{Num, Den}>...>, OneType, Pred, To>();
To>()){};
} }
@@ -482,7 +480,7 @@ template<std::intmax_t Num, std::intmax_t Den, template<typename...> typename To
requires detail::non_zero<Den> requires detail::non_zero<Den>
[[nodiscard]] consteval auto expr_pow(T) [[nodiscard]] consteval auto expr_pow(T)
{ {
return decltype(expr_pow_impl<Num, Den, To, OneType, Pred>(typename T::_num_{}, typename T::_den_{})){}; return expr_pow_impl<Num, Den, To, OneType, Pred>(typename T::_num_{}, typename T::_den_{});
} }
@@ -529,7 +527,7 @@ template<typename T>
template<typename T, auto... Ints> template<typename T, auto... Ints>
[[nodiscard]] consteval auto map_power(power<T, Ints...>) [[nodiscard]] consteval auto map_power(power<T, Ints...>)
{ {
return decltype(pow<Ints...>(T{})){}; return pow<Ints...>(T{});
} }
template<template<typename> typename Proj, template<typename...> typename To, typename OneType, template<template<typename> typename Proj, template<typename...> typename To, typename OneType,
@@ -537,8 +535,8 @@ template<template<typename> typename Proj, template<typename...> typename To, ty
expr_type_projectable<Proj>... Dens> expr_type_projectable<Proj>... Dens>
[[nodiscard]] consteval auto expr_map_impl(type_list<Nums...>, type_list<Dens...>) [[nodiscard]] consteval auto expr_map_impl(type_list<Nums...>, type_list<Dens...>)
{ {
return decltype(decltype((OneType{} * ... * map_power(typename expr_type_map<Nums, Proj>::type{}))){} / return (OneType{} * ... * map_power(typename expr_type_map<Nums, Proj>::type{})) /
decltype((OneType{} * ... * map_power(typename expr_type_map<Dens, Proj>::type{}))){}){}; (OneType{} * ... * map_power(typename expr_type_map<Dens, Proj>::type{}));
} }
/** /**
@@ -557,7 +555,7 @@ template<template<typename> typename Proj, template<typename...> typename To, ty
if constexpr (type_list_size<typename T::_num_> + type_list_size<typename T::_den_> == 0) if constexpr (type_list_size<typename T::_num_> + type_list_size<typename T::_den_> == 0)
return OneType{}; return OneType{};
else else
return decltype(expr_map_impl<Proj, To, OneType, Pred>(typename T::_num_{}, typename T::_den_{})){}; return expr_map_impl<Proj, To, OneType, Pred>(typename T::_num_{}, typename T::_den_{});
} }
} // namespace detail } // namespace detail

View File

@@ -476,16 +476,16 @@ namespace detail {
template<MagnitudeSpec auto... Ms> template<MagnitudeSpec auto... Ms>
// requires detail::is_element_pack_valid<Ms...> // requires detail::is_element_pack_valid<Ms...>
struct magnitude { struct magnitude {
[[nodiscard]] friend consteval auto is_integral(const magnitude&) [[nodiscard]] friend consteval bool is_integral(const magnitude&)
{ {
using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec
return std::bool_constant<(is_integral(Ms) && ...)>{}; return (is_integral(Ms) && ...);
} }
[[nodiscard]] friend consteval auto is_rational(const magnitude&) [[nodiscard]] friend consteval bool is_rational(const magnitude&)
{ {
using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec
return std::bool_constant<(is_rational(Ms) && ...)>{}; return (is_rational(Ms) && ...);
} }
}; };
@@ -508,13 +508,12 @@ inline constexpr bool is_specialization_of_magnitude<magnitude<Ms...>> = true;
} // namespace detail } // namespace detail
MP_UNITS_EXPORT_BEGIN
/** /**
* @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, auto... Ms> template<typename T, auto... Ms>
requires(decltype(is_integral(magnitude<Ms...>{}))::value) || treat_as_floating_point<T> requires(is_integral(magnitude<Ms...>{})) || treat_as_floating_point<T>
constexpr T get_value(const magnitude<Ms...>&) constexpr T get_value(const magnitude<Ms...>&)
{ {
// 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.
@@ -522,6 +521,8 @@ constexpr T get_value(const magnitude<Ms...>&)
return result; return result;
} }
MP_UNITS_EXPORT_BEGIN
/** /**
* @brief A convenient Magnitude constant for pi, which we can manipulate like a regular number. * @brief A convenient Magnitude constant for pi, which we can manipulate like a regular number.
*/ */
@@ -556,21 +557,21 @@ template<std::intmax_t Num, std::intmax_t Den = 1, auto... Ms>
if constexpr (Num == 0) { if constexpr (Num == 0) {
return magnitude<>{}; return magnitude<>{};
} else { } else {
return decltype(magnitude<detail::power_v_or_T<detail::get_base(Ms), return magnitude<
detail::get_exponent(Ms) * detail::ratio{Num, Den}>()...>{}){}; detail::power_v_or_T<detail::get_base(Ms), detail::get_exponent(Ms) * detail::ratio{Num, Den}>()...>{};
} }
} }
template<auto... Ms> template<auto... Ms>
[[nodiscard]] consteval auto sqrt(magnitude<Ms...> m) [[nodiscard]] consteval auto sqrt(magnitude<Ms...> m)
{ {
return decltype(pow<1, 2>(m)){}; return pow<1, 2>(m);
} }
template<auto... Ms> template<auto... Ms>
[[nodiscard]] consteval auto cbrt(magnitude<Ms...> m) [[nodiscard]] consteval auto cbrt(magnitude<Ms...> m)
{ {
return decltype(pow<1, 3>(m)){}; return pow<1, 3>(m);
} }
MP_UNITS_EXPORT_END MP_UNITS_EXPORT_END
@@ -613,13 +614,13 @@ template<auto H1, auto... T1, auto H2, auto... T2>
// 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<H1, H2, T2...>{}; return magnitude<H1, H2, T2...>{};
} else { } else {
return decltype(magnitude<H1>{} * (decltype(magnitude<T1...>{} * magnitude<H2, T2...>{}){})){}; return magnitude<H1>{} * (magnitude<T1...>{} * magnitude<H2, T2...>{});
} }
} else if constexpr (less(H2, H1)) { } else if constexpr (less(H2, H1)) {
return decltype(magnitude<H2>{} * (decltype(magnitude<H1, T1...>{} * magnitude<T2...>{}){})){}; return magnitude<H2>{} * (magnitude<H1, T1...>{} * magnitude<T2...>{});
} else { } else {
if constexpr (is_same_v<decltype(get_base(H1)), decltype(get_base(H2))>) { if constexpr (is_same_v<decltype(get_base(H1)), decltype(get_base(H2))>) {
constexpr auto partial_product = decltype(magnitude<T1...>{} * magnitude<T2...>{}){}; constexpr auto partial_product = magnitude<T1...>{} * magnitude<T2...>{};
if constexpr (get_exponent(H1) + get_exponent(H2) == 0) { if constexpr (get_exponent(H1) + get_exponent(H2) == 0) {
return partial_product; return partial_product;
} else { } else {
@@ -629,13 +630,13 @@ template<auto H1, auto... T1, auto H2, auto... T2>
if constexpr (get_exponent(new_head) == 0) { if constexpr (get_exponent(new_head) == 0) {
return partial_product; return partial_product;
} else { } else {
return decltype(magnitude<new_head>{} * partial_product){}; return magnitude<new_head>{} * partial_product;
} }
} }
} else if constexpr (is_named_magnitude<decltype(get_base(H1))>) { } else if constexpr (is_named_magnitude<decltype(get_base(H1))>) {
return decltype(magnitude<H1>{} * decltype((magnitude<T1...>{} * magnitude<H2, T2...>{})){}){}; return magnitude<H1>{} * (magnitude<T1...>{} * magnitude<H2, T2...>{});
} else { } else {
return decltype(magnitude<H2>{} * decltype((magnitude<H1, T1...>{} * magnitude<T2...>{})){}){}; return magnitude<H2>{} * (magnitude<H1, T1...>{} * magnitude<T2...>{});
} }
} }
} }
@@ -643,7 +644,7 @@ template<auto H1, auto... T1, auto H2, auto... T2>
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude quotient implementation. // Magnitude quotient implementation.
[[nodiscard]] consteval auto operator/(Magnitude auto l, Magnitude auto r) { return decltype(l * pow<-1>(r)){}; } [[nodiscard]] consteval auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1>(r); }
MP_UNITS_EXPORT_END MP_UNITS_EXPORT_END
@@ -670,10 +671,10 @@ template<auto M>
template<auto... Ms> template<auto... Ms>
[[nodiscard]] consteval auto numerator(magnitude<Ms...>) [[nodiscard]] consteval auto numerator(magnitude<Ms...>)
{ {
return decltype((decltype(detail::integer_part(magnitude<Ms>{})){} * ... * magnitude<>{})){}; return (detail::integer_part(magnitude<Ms>{}) * ... * magnitude<>{});
} }
[[nodiscard]] consteval auto denominator(Magnitude auto m) { return decltype(numerator(pow<-1>(m))){}; } [[nodiscard]] consteval auto denominator(Magnitude auto m) { return numerator(pow<-1>(m)); }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Common Magnitude. // Common Magnitude.
@@ -713,11 +714,11 @@ template<auto... Ms>
[[nodiscard]] consteval auto common_magnitude(magnitude<>, magnitude<>) { return magnitude<>{}; } [[nodiscard]] consteval auto common_magnitude(magnitude<>, magnitude<>) { return magnitude<>{}; }
[[nodiscard]] consteval auto common_magnitude(magnitude<>, Magnitude auto m) [[nodiscard]] consteval auto common_magnitude(magnitude<>, Magnitude auto m)
{ {
return decltype(detail::remove_positive_powers(m)){}; return detail::remove_positive_powers(m);
} }
[[nodiscard]] consteval auto common_magnitude(Magnitude auto m, magnitude<>) [[nodiscard]] consteval auto common_magnitude(Magnitude auto m, magnitude<>)
{ {
return decltype(detail::remove_positive_powers(m)){}; return detail::remove_positive_powers(m);
} }
// Recursive case for the common Magnitude of any two non-identity Magnitudes. // Recursive case for the common Magnitude of any two non-identity Magnitudes.
@@ -728,19 +729,17 @@ template<auto H1, auto... T1, auto H2, auto... T2>
if constexpr (detail::get_base_value(H1) < detail::get_base_value(H2)) { if constexpr (detail::get_base_value(H1) < detail::get_base_value(H2)) {
// When H1 has the smaller base, prepend to result from recursion. // When H1 has the smaller base, prepend to result from recursion.
return decltype(decltype(remove_positive_power(magnitude<H1>{})){} * return remove_positive_power(magnitude<H1>{}) * common_magnitude(magnitude<T1...>{}, magnitude<H2, T2...>{});
decltype(common_magnitude(magnitude<T1...>{}, magnitude<H2, T2...>{})){}){};
} else if constexpr (detail::get_base_value(H2) < detail::get_base_value(H1)) { } else if constexpr (detail::get_base_value(H2) < detail::get_base_value(H1)) {
// When H2 has the smaller base, prepend to result from recursion. // When H2 has the smaller base, prepend to result from recursion.
return decltype(decltype(remove_positive_power(magnitude<H2>{})){} * return remove_positive_power(magnitude<H2>{}) * common_magnitude(magnitude<H1, T1...>{}, magnitude<T2...>{});
decltype(common_magnitude(magnitude<H1, T1...>{}, magnitude<T2...>{})){}){};
} else { } else {
// When the bases are equal, pick whichever has the lower power. // When the bases are equal, pick whichever has the lower power.
constexpr auto common_tail = decltype(common_magnitude(magnitude<T1...>{}, magnitude<T2...>{})){}; constexpr auto common_tail = common_magnitude(magnitude<T1...>{}, magnitude<T2...>{});
if constexpr (detail::get_exponent(H1) < detail::get_exponent(H2)) { if constexpr (detail::get_exponent(H1) < detail::get_exponent(H2)) {
return decltype(magnitude<H1>{} * common_tail){}; return magnitude<H1>{} * common_tail;
} else { } else {
return decltype(magnitude<H2>{} * common_tail){}; return magnitude<H2>{} * common_tail;
} }
} }
} }
@@ -781,7 +780,7 @@ struct prime_factorization {
if constexpr (opt.has_value()) { if constexpr (opt.has_value()) {
return opt.value(); // NOLINT(bugprone-unchecked-optional-access) return opt.value(); // NOLINT(bugprone-unchecked-optional-access)
} else { } else {
return static_cast<std::intmax_t>(decltype(factorizer::find_first_factor<N>())::value); return static_cast<std::intmax_t>(factorizer::find_first_factor(N));
} }
} }
@@ -816,15 +815,14 @@ inline constexpr Magnitude auto mag = detail::prime_factorization_v<V>;
MP_UNITS_EXPORT template<std::intmax_t N, std::intmax_t D> MP_UNITS_EXPORT template<std::intmax_t N, std::intmax_t D>
requires detail::gt_zero<N> requires detail::gt_zero<N>
inline constexpr Magnitude auto mag_ratio = inline constexpr Magnitude auto mag_ratio = detail::prime_factorization_v<N> / detail::prime_factorization_v<D>;
decltype(detail::prime_factorization_v<N> / detail::prime_factorization_v<D>){};
/** /**
* @brief Create a Magnitude which is some rational number raised to a rational power. * @brief Create a Magnitude which is some rational number raised to a rational power.
*/ */
MP_UNITS_EXPORT template<std::intmax_t Base, std::intmax_t Pow> MP_UNITS_EXPORT template<std::intmax_t Base, std::intmax_t Pow>
requires detail::gt_zero<Base> requires detail::gt_zero<Base>
inline constexpr Magnitude auto mag_power = decltype(pow<Pow>(mag<Base>)){}; inline constexpr Magnitude auto mag_power = pow<Pow>(mag<Base>);
namespace detail { namespace detail {
@@ -853,14 +851,14 @@ template<Magnitude auto M>
{ {
constexpr auto exp10 = extract_power_of_10(M); constexpr auto exp10 = extract_power_of_10(M);
using base = decltype(M / mag_power<10, exp10>); constexpr Magnitude auto base = M / mag_power<10, exp10>;
using num = decltype(numerator(base{})); constexpr Magnitude auto num = numerator(base);
using den = decltype(denominator(base{})); constexpr Magnitude auto den = denominator(base);
// TODO address the below // TODO address the below
static_assert(base{} == num{} / den{}, "Printing rational powers, or irrational bases, not yet supported"); static_assert(base == num / den, "Printing rational powers, or irrational bases, not yet supported");
constexpr auto num_value = get_value<std::intmax_t>(num{}); constexpr auto num_value = get_value<std::intmax_t>(num);
constexpr auto den_value = get_value<std::intmax_t>(den{}); constexpr auto den_value = get_value<std::intmax_t>(den);
if constexpr (num_value == 1 && den_value == 1 && exp10 != 0) { if constexpr (num_value == 1 && den_value == 1 && exp10 != 0) {
return base_multiplier + superscript<exp10>(); return base_multiplier + superscript<exp10>();

View File

@@ -52,8 +52,7 @@ template<Unit UFrom, Unit UTo>
if constexpr (is_same_v<UFrom, UTo>) if constexpr (is_same_v<UFrom, UTo>)
return true; return true;
else else
return decltype(is_integral( return is_integral(get_canonical_unit(from).mag / get_canonical_unit(to).mag);
decltype(decltype(get_canonical_unit(from))::mag / decltype(get_canonical_unit(to))::mag){}))::value;
} }
template<typename QFrom, typename QTo> template<typename QFrom, typename QTo>

File diff suppressed because it is too large Load Diff

View File

@@ -126,12 +126,11 @@ MP_UNITS_EXPORT template<QuantitySpec Q>
namespace detail { namespace detail {
template<QuantitySpec Child, QuantitySpec Parent> template<QuantitySpec Child, QuantitySpec Parent>
[[nodiscard]] consteval auto is_child_of(Child ch, Parent); [[nodiscard]] consteval bool is_child_of(Child ch, Parent p);
template<auto To, auto From> template<auto To, auto From>
concept NestedQuantityKindSpecOf = concept NestedQuantityKindSpecOf = QuantitySpec<decltype(From)> && QuantitySpec<decltype(To)> &&
QuantitySpec<decltype(From)> && QuantitySpec<decltype(To)> && get_kind(From) != get_kind(To) && get_kind(From) != get_kind(To) && is_child_of(To, get_kind(From)._quantity_spec_);
decltype(is_child_of(To, get_kind(From)._quantity_spec_))::value;
} // namespace detail } // namespace detail

View File

@@ -46,7 +46,7 @@ MP_UNITS_EXPORT_BEGIN
[[nodiscard]] consteval QuantitySpec auto get_quantity_spec(AssociatedUnit auto u) [[nodiscard]] consteval QuantitySpec auto get_quantity_spec(AssociatedUnit auto u)
{ {
return decltype(detail::get_associated_quantity(u)){}; return detail::get_associated_quantity(u);
} }
/** /**

View File

@@ -206,7 +206,7 @@ struct named_unit<Symbol, U, PO> : decltype(U)::_base_type_ {
* @tparam QuantitySpec a specification of a quantity to be measured with this unit * @tparam QuantitySpec a specification of a quantity to be measured with this unit
*/ */
template<symbol_text Symbol, AssociatedUnit auto U, detail::QuantityKindSpec auto QS> template<symbol_text Symbol, AssociatedUnit auto U, detail::QuantityKindSpec auto QS>
requires(!Symbol.empty()) && (QS.dimension == decltype(detail::get_associated_quantity(U))::dimension) requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension)
struct named_unit<Symbol, U, QS> : decltype(U)::_base_type_ { struct named_unit<Symbol, U, QS> : decltype(U)::_base_type_ {
using _base_type_ = named_unit; // exposition only using _base_type_ = named_unit; // exposition only
static constexpr auto symbol = Symbol; ///< Unique unit identifier static constexpr auto symbol = Symbol; ///< Unique unit identifier
@@ -214,7 +214,7 @@ struct named_unit<Symbol, U, QS> : decltype(U)::_base_type_ {
}; };
template<symbol_text Symbol, AssociatedUnit auto U, detail::QuantityKindSpec auto QS, PointOrigin auto PO> template<symbol_text Symbol, AssociatedUnit auto U, detail::QuantityKindSpec auto QS, PointOrigin auto PO>
requires(!Symbol.empty()) && (QS.dimension == decltype(detail::get_associated_quantity(U))::dimension) requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension)
struct named_unit<Symbol, U, QS, PO> : decltype(U)::_base_type_ { struct named_unit<Symbol, U, QS, PO> : decltype(U)::_base_type_ {
using _base_type_ = named_unit; // exposition only using _base_type_ = named_unit; // exposition only
static constexpr auto symbol = Symbol; ///< Unique unit identifier static constexpr auto symbol = Symbol; ///< Unique unit identifier
@@ -339,12 +339,19 @@ struct is_one<struct one> : std::true_type {};
* @tparam U a unit to use as a `reference_unit` * @tparam U a unit to use as a `reference_unit`
* @tparam M a Magnitude representing an absolute scaling factor of this unit * @tparam M a Magnitude representing an absolute scaling factor of this unit
*/ */
template<Magnitude auto M, Unit auto U> template<Magnitude M, Unit U>
struct canonical_unit { struct canonical_unit {
static constexpr auto mag = M; M mag;
static constexpr auto reference_unit = U; U reference_unit;
}; };
#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, auto... Args> 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 t, const named_unit<Symbol, Args...>&);
@@ -360,61 +367,58 @@ template<Unit T, typename... Expr>
template<Unit T, auto M, typename U> template<Unit T, auto M, typename U>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const scaled_unit_impl<M, U>&) [[nodiscard]] consteval auto get_canonical_unit_impl(T, const scaled_unit_impl<M, U>&)
{ {
using base = decltype(get_canonical_unit_impl(U{}, U{})); auto base = get_canonical_unit_impl(U{}, U{});
return canonical_unit<decltype(M * base::mag){}, base::reference_unit>{}; return canonical_unit{M * base.mag, base.reference_unit};
} }
template<Unit T, symbol_text Symbol, auto... Args> template<Unit T, symbol_text Symbol, auto... Args>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, Args...>&) [[nodiscard]] consteval auto get_canonical_unit_impl(T 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> 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...>&) [[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, U, Args...>&)
{ {
return decltype(get_canonical_unit_impl(U, U)){}; return get_canonical_unit_impl(U, U);
} }
template<typename F, int Num, int... Den, typename... Us> 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...>&) [[nodiscard]] consteval auto get_canonical_unit_impl(const power<F, Num, Den...>&, const type_list<Us...>&)
{ {
using mag = decltype((mp_units::mag<1> * ... * pow<Num, Den...>(decltype(get_canonical_unit_impl(Us{}, Us{}))::mag))); auto mag = (mp_units::mag<1> * ... * pow<Num, Den...>(get_canonical_unit_impl(Us{}, Us{}).mag));
using u = decltype((one * ... * pow<Num, Den...>(decltype(get_canonical_unit_impl(Us{}, Us{}))::reference_unit))); auto u = (one * ... * pow<Num, Den...>(get_canonical_unit_impl(Us{}, Us{}).reference_unit));
return canonical_unit<mag{}, u{}>{}; return canonical_unit{mag, u};
} }
template<typename T, typename F, int Num, int... Den> template<typename T, typename F, int Num, int... Den>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const power<F, Num, Den...>&) [[nodiscard]] consteval auto get_canonical_unit_impl(T, const power<F, Num, Den...>&)
{ {
using base = decltype(get_canonical_unit_impl(F{}, F{})); auto base = get_canonical_unit_impl(F{}, F{});
if constexpr (requires { typename decltype(base::reference_unit)::_num_; }) { if constexpr (requires { typename decltype(base.reference_unit)::_num_; }) {
using num = auto num = get_canonical_unit_impl(power<F, Num, Den...>{}, typename decltype(base.reference_unit)::_num_{});
decltype(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_{});
using den = return canonical_unit{pow<Num, Den...>(base.mag) * num.mag / den.mag, num.reference_unit / den.reference_unit};
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 { } else {
return canonical_unit<decltype(pow<Num, Den...>(base::mag)){}, return canonical_unit{pow<Num, Den...>(base.mag),
derived_unit<power<std::remove_const_t<decltype(base::reference_unit)>, Num, Den...>>{}>{}; derived_unit<power<decltype(base.reference_unit), Num, Den...>>{}};
} }
} }
template<typename... Us> template<typename... Us>
[[nodiscard]] consteval auto get_canonical_unit_impl(const type_list<Us...>&) [[nodiscard]] consteval auto get_canonical_unit_impl(const type_list<Us...>&)
{ {
using m = decltype((mp_units::mag<1> * ... * decltype(get_canonical_unit_impl(Us{}, Us{}))::mag)); auto m = (mp_units::mag<1> * ... * get_canonical_unit_impl(Us{}, Us{}).mag);
using u = decltype((one * ... * decltype(get_canonical_unit_impl(Us{}, Us{}))::reference_unit)); auto u = (one * ... * get_canonical_unit_impl(Us{}, Us{}).reference_unit);
return canonical_unit<m{}, u{}>{}; return canonical_unit{m, u};
} }
template<Unit T, typename... Expr> template<Unit T, typename... Expr>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit_impl<Expr...>&) [[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit_impl<Expr...>&)
{ {
using num = decltype(get_canonical_unit_impl(typename derived_unit_impl<Expr...>::_num_{})); auto num = get_canonical_unit_impl(typename derived_unit<Expr...>::_num_{});
using den = decltype(get_canonical_unit_impl(typename derived_unit_impl<Expr...>::_den_{})); auto den = get_canonical_unit_impl(typename derived_unit<Expr...>::_den_{});
return canonical_unit<decltype(num::mag / den::mag){}, decltype(num::reference_unit / den::reference_unit){}>{}; return canonical_unit{num.mag / den.mag, num.reference_unit / den.reference_unit};
} }
template<Unit Lhs, Unit Rhs> template<Unit Lhs, Unit Rhs>
@@ -427,10 +431,7 @@ 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) // 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 // Even though it is not exported, it is visible to the other module via ADL
[[nodiscard]] consteval auto get_canonical_unit(Unit auto u) [[nodiscard]] consteval auto get_canonical_unit(Unit auto u) { return detail::get_canonical_unit_impl(u, u); }
{
return decltype(detail::get_canonical_unit_impl(u, u)){};
}
MP_UNITS_EXPORT_BEGIN MP_UNITS_EXPORT_BEGIN
@@ -456,7 +457,7 @@ template<Magnitude M, Unit U>
template<Magnitude M, Unit U> template<Magnitude M, Unit U>
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator/(M mag, U u) [[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator/(M mag, U u)
{ {
return decltype(mag * inverse(u)){}; return mag * inverse(u);
} }
/** /**
@@ -468,13 +469,13 @@ template<Unit Lhs, Unit Rhs>
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator*(Lhs lhs, Rhs 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>) if constexpr (detail::is_specialization_of_scaled_unit<Lhs> && detail::is_specialization_of_scaled_unit<Rhs>)
return decltype(Lhs::mag * Rhs::mag){} * decltype(Lhs::reference_unit * Rhs::reference_unit){}; return (Lhs::mag * Rhs::mag) * (Lhs::reference_unit * Rhs::reference_unit);
else if constexpr (detail::is_specialization_of_scaled_unit<Lhs>) else if constexpr (detail::is_specialization_of_scaled_unit<Lhs>)
return decltype(Lhs::mag * decltype(Lhs::reference_unit * rhs){}){}; return Lhs::mag * (Lhs::reference_unit * rhs);
else if constexpr (detail::is_specialization_of_scaled_unit<Rhs>) else if constexpr (detail::is_specialization_of_scaled_unit<Rhs>)
return decltype(Rhs::mag * decltype(lhs * Rhs::reference_unit){}){}; return Rhs::mag * (lhs * Rhs::reference_unit);
else else
return decltype(detail::expr_multiply<derived_unit, struct one, detail::type_list_of_unit_less>(lhs, rhs)){}; return detail::expr_multiply<derived_unit, struct one, detail::type_list_of_unit_less>(lhs, rhs);
} }
/** /**
@@ -486,44 +487,39 @@ template<Unit Lhs, Unit Rhs>
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator/(Lhs lhs, Rhs 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>) if constexpr (detail::is_specialization_of_scaled_unit<Lhs> && detail::is_specialization_of_scaled_unit<Rhs>)
return decltype(Lhs::mag / Rhs::mag){} * decltype(Lhs::reference_unit / Rhs::reference_unit){}; return (Lhs::mag / Rhs::mag) * (Lhs::reference_unit / Rhs::reference_unit);
else if constexpr (detail::is_specialization_of_scaled_unit<Lhs>) else if constexpr (detail::is_specialization_of_scaled_unit<Lhs>)
return Lhs::mag * decltype(Lhs::reference_unit / rhs){}; return Lhs::mag * (Lhs::reference_unit / rhs);
else if constexpr (detail::is_specialization_of_scaled_unit<Rhs>) else if constexpr (detail::is_specialization_of_scaled_unit<Rhs>)
return decltype(mag<1> / Rhs::mag){} * decltype(lhs / Rhs::reference_unit){}; return mag<1> / Rhs::mag * (lhs / Rhs::reference_unit);
else else
return detail::expr_divide<derived_unit, struct one, detail::type_list_of_unit_less>(lhs, rhs); 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 decltype(one / u){}; } [[nodiscard]] MP_UNITS_CONSTEVAL Unit auto inverse(Unit auto u) { return one / u; }
MP_UNITS_EXPORT_END MP_UNITS_EXPORT_END
namespace detail { namespace detail {
template<Unit U1, Unit U2> template<Unit U1, Unit U2>
[[nodiscard]] consteval auto have_same_canonical_reference_unit(U1 u1, U2 u2) [[nodiscard]] consteval bool have_same_canonical_reference_unit(U1 u1, U2 u2)
{ {
if constexpr (is_same_v<U1, U2>) if constexpr (is_same_v<U1, U2>)
return std::true_type{}; return true;
else { else
using canonical_lhs = decltype(get_canonical_unit(u1)); return is_same_v<decltype(get_canonical_unit(u1).reference_unit), decltype(get_canonical_unit(u2).reference_unit)>;
using canonical_rhs = decltype(get_canonical_unit(u2));
return std::is_same<decltype(canonical_lhs::reference_unit), decltype(canonical_rhs::reference_unit)>{};
}
} }
} // namespace detail } // namespace detail
MP_UNITS_EXPORT template<Unit U1, Unit U2> MP_UNITS_EXPORT [[nodiscard]] consteval bool operator==(Unit auto lhs, Unit auto rhs)
[[nodiscard]] consteval bool operator==(U1 lhs, U2 rhs)
{ {
if constexpr (is_same_v<U1, U2>) auto canonical_lhs = get_canonical_unit(lhs);
return true; auto canonical_rhs = get_canonical_unit(rhs);
else return detail::have_same_canonical_reference_unit(canonical_lhs.reference_unit, canonical_rhs.reference_unit) &&
return decltype(detail::have_same_canonical_reference_unit(lhs, rhs))::value && canonical_lhs.mag == canonical_rhs.mag;
decltype(get_canonical_unit(lhs))::mag == decltype(get_canonical_unit(rhs))::mag;
} }
namespace detail { namespace detail {
@@ -558,7 +554,7 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Unit U>
else if constexpr (detail::is_specialization_of_scaled_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))>{}; 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>) else if constexpr (detail::is_specialization_of_derived_unit<U>)
return decltype(detail::expr_pow<Num, Den, derived_unit, struct one, detail::type_list_of_unit_less>(u)){}; return detail::expr_pow<Num, Den, derived_unit, struct one, detail::type_list_of_unit_less>(u);
else if constexpr (Den == 1) else if constexpr (Den == 1)
return derived_unit<power<U, Num>>{}; return derived_unit<power<U, Num>>{};
else else
@@ -572,7 +568,7 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Unit U>
* *
* @return Unit The result of computation * @return Unit The result of computation
*/ */
[[nodiscard]] consteval Unit auto sqrt(Unit auto u) { return decltype(pow<1, 2>(u)){}; } [[nodiscard]] consteval Unit auto sqrt(Unit auto u) { return pow<1, 2>(u); }
/** /**
* @brief Computes the cubic root of a unit * @brief Computes the cubic root of a unit
@@ -581,7 +577,7 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Unit U>
* *
* @return Unit The result of computation * @return Unit The result of computation
*/ */
[[nodiscard]] consteval Unit auto cbrt(Unit auto u) { return decltype(pow<1, 3>(u)){}; } [[nodiscard]] consteval Unit auto cbrt(Unit auto u) { return pow<1, 3>(u); }
/** /**
* @brief Computes the square power of a unit * @brief Computes the square power of a unit
@@ -590,7 +586,7 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Unit U>
* *
* @return Unit The result of computation * @return Unit The result of computation
*/ */
[[nodiscard]] consteval Unit auto square(Unit auto u) { return decltype(pow<2>(u)){}; } [[nodiscard]] consteval Unit auto square(Unit auto u) { return pow<2>(u); }
/** /**
* @brief Computes the cubic power of a unit * @brief Computes the cubic power of a unit
@@ -599,7 +595,7 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Unit U>
* *
* @return Unit The result of computation * @return Unit The result of computation
*/ */
[[nodiscard]] consteval Unit auto cubic(Unit auto u) { return decltype(pow<3>(u)){}; } [[nodiscard]] consteval Unit auto cubic(Unit auto u) { return pow<3>(u); }
// common dimensionless units // common dimensionless units
@@ -618,7 +614,7 @@ template<Unit U1, Unit U2>
if constexpr (is_same_v<U1, U2>) if constexpr (is_same_v<U1, U2>)
return true; return true;
else else
return decltype(detail::have_same_canonical_reference_unit(from, to))::value; return detail::have_same_canonical_reference_unit(from, to);
} }
// Common unit // Common unit
@@ -626,7 +622,7 @@ template<Unit U1, Unit U2>
template<Unit U1, Unit U2> template<Unit U1, Unit U2>
[[nodiscard]] consteval Unit auto common_unit(U1 u1, U2 u2) [[nodiscard]] consteval Unit auto common_unit(U1 u1, U2 u2)
requires(decltype(detail::have_same_canonical_reference_unit(u1, u2))::value) requires(detail::have_same_canonical_reference_unit(u1, u2))
{ {
if constexpr (is_same_v<U1, U2>) if constexpr (is_same_v<U1, U2>)
return u1; return u1;
@@ -637,18 +633,18 @@ template<Unit U1, Unit U2>
return u2; return u2;
else else
// TODO Check if there is a better choice here // TODO Check if there is a better choice here
return decltype(detail::better_type_name(u1, u2)){}; return detail::better_type_name(u1, u2);
} else { } else {
using canonical_lhs = decltype(get_canonical_unit(U1{})); constexpr auto canonical_lhs = get_canonical_unit(U1{});
using canonical_rhs = decltype(get_canonical_unit(U2{})); constexpr auto canonical_rhs = get_canonical_unit(U2{});
if constexpr (decltype(is_integral(decltype(canonical_lhs::mag / canonical_rhs::mag){}))::value) if constexpr (is_integral(canonical_lhs.mag / canonical_rhs.mag))
return u2; return u2;
else if constexpr (decltype(is_integral(decltype(canonical_rhs::mag / canonical_lhs::mag){}))::value) else if constexpr (is_integral(canonical_rhs.mag / canonical_lhs.mag))
return u1; return u1;
else { else {
constexpr auto cm = decltype(detail::common_magnitude(canonical_lhs::mag, canonical_rhs::mag)){}; constexpr auto cm = detail::common_magnitude(canonical_lhs.mag, canonical_rhs.mag);
return scaled_unit<cm, std::remove_const_t<decltype(canonical_lhs::reference_unit)>>{}; return scaled_unit<cm, decltype(canonical_lhs.reference_unit)>{};
} }
} }
} }
@@ -656,7 +652,7 @@ template<Unit U1, Unit U2>
[[nodiscard]] consteval Unit auto common_unit(Unit auto u1, Unit auto u2, Unit auto u3, Unit auto... rest) [[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...); } requires requires { common_unit(common_unit(u1, u2), u3, rest...); }
{ {
return decltype(common_unit(common_unit(u1, u2), u3, rest...)){}; return common_unit(common_unit(u1, u2), u3, rest...);
} }

View File

@@ -191,7 +191,7 @@ concept UnitOf =
namespace detail { namespace detail {
template<Unit U1, Unit U2> template<Unit U1, Unit U2>
[[nodiscard]] consteval auto have_same_canonical_reference_unit(U1 u1, U2 u2); [[nodiscard]] consteval bool have_same_canonical_reference_unit(U1 u1, U2 u2);
} }
@@ -204,7 +204,7 @@ template<Unit U1, Unit U2>
MP_UNITS_EXPORT template<typename U, auto U2, auto QS> MP_UNITS_EXPORT template<typename U, auto U2, auto QS>
concept UnitCompatibleWith = concept UnitCompatibleWith =
Unit<U> && Unit<MP_UNITS_REMOVE_CONST(decltype(U2))> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> && Unit<U> && Unit<MP_UNITS_REMOVE_CONST(decltype(U2))> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> &&
(!AssociatedUnit<U> || UnitOf<U, QS>)&&decltype(detail::have_same_canonical_reference_unit(U{}, U2))::value; (!AssociatedUnit<U> || UnitOf<U, QS>)&&(detail::have_same_canonical_reference_unit(U{}, U2));
} // namespace mp_units } // namespace mp_units

View File

@@ -115,8 +115,8 @@ struct quantity_point_like_traits<std::chrono::time_point<C, std::chrono::durati
namespace detail { namespace detail {
constexpr auto as_ratio(Magnitude auto m) [[nodiscard]] constexpr auto as_ratio(Magnitude auto m)
requires(decltype(is_rational(decltype(m){}))::value) requires(is_rational(m))
{ {
return std::ratio<get_value<std::intmax_t>(numerator(m)), get_value<std::intmax_t>(denominator(m))>{}; return std::ratio<get_value<std::intmax_t>(numerator(m)), get_value<std::intmax_t>(denominator(m))>{};
} }
@@ -126,8 +126,7 @@ constexpr auto as_ratio(Magnitude auto m)
template<QuantityOf<isq::time> Q> template<QuantityOf<isq::time> Q>
[[nodiscard]] constexpr auto to_chrono_duration(const Q& q) [[nodiscard]] constexpr auto to_chrono_duration(const Q& q)
{ {
return std::chrono::duration<typename Q::rep, decltype(detail::as_ratio(decltype(get_canonical_unit(Q::unit))::mag))>{ return std::chrono::duration<typename Q::rep, decltype(detail::as_ratio(get_canonical_unit(Q::unit).mag))>{q};
q};
} }
template<QuantityPointOf<isq::time> QP> template<QuantityPointOf<isq::time> QP>
@@ -136,8 +135,9 @@ template<QuantityPointOf<isq::time> QP>
{ {
using clock = MP_UNITS_TYPENAME decltype(QP::absolute_point_origin)::clock; using clock = MP_UNITS_TYPENAME decltype(QP::absolute_point_origin)::clock;
using rep = MP_UNITS_TYPENAME QP::rep; using rep = MP_UNITS_TYPENAME QP::rep;
using ret_type = std::chrono::time_point< using ret_type =
clock, std::chrono::duration<rep, decltype(detail::as_ratio(decltype(get_canonical_unit(QP::unit))::mag))>>; std::chrono::time_point<clock,
std::chrono::duration<rep, decltype(detail::as_ratio(get_canonical_unit(QP::unit).mag))>>;
return ret_type(to_chrono_duration(qp - qp.absolute_point_origin)); return ret_type(to_chrono_duration(qp - qp.absolute_point_origin));
} }

View File

@@ -32,7 +32,7 @@ namespace {
template<std::size_t BasisSize, std::size_t... Is> template<std::size_t BasisSize, std::size_t... Is>
constexpr bool check_primes(std::index_sequence<Is...>) constexpr bool check_primes(std::index_sequence<Is...>)
{ {
return ((Is < 2 || wheel_factorizer<BasisSize>::template is_prime<Is>() == is_prime_by_trial_division(Is)) && ...); return ((Is < 2 || wheel_factorizer<BasisSize>::is_prime(Is) == is_prime_by_trial_division(Is)) && ...);
} }
static_assert(check_primes<2>(std::make_index_sequence<122>{})); static_assert(check_primes<2>(std::make_index_sequence<122>{}));
@@ -45,7 +45,7 @@ static_assert(check_primes<2>(std::make_index_sequence<122>{}));
// using numbers of the form (210 * n + 121) as trial divisors, which is a problem if any are prime. For n = 1, we have // using numbers of the form (210 * n + 121) as trial divisors, which is a problem if any are prime. For n = 1, we have
// a divisor of (210 + 121 = 331), which happens to be prime but will not be used. Thus, (331 * 331 = 109561) is a // a divisor of (210 + 121 = 331), which happens to be prime but will not be used. Thus, (331 * 331 = 109561) is a
// composite number which could wrongly appear prime if we skip over 331. // composite number which could wrongly appear prime if we skip over 331.
static_assert(wheel_factorizer<4>::is_prime<109'561>() == is_prime_by_trial_division(109'561)); static_assert(wheel_factorizer<4>::is_prime(109'561) == is_prime_by_trial_division(109'561));
static_assert(wheel_factorizer<1>::coprimes_in_first_wheel.size() == 1); static_assert(wheel_factorizer<1>::coprimes_in_first_wheel.size() == 1);
static_assert(wheel_factorizer<2>::coprimes_in_first_wheel.size() == 2); static_assert(wheel_factorizer<2>::coprimes_in_first_wheel.size() == 2);
@@ -62,16 +62,16 @@ static_assert(wheel_factorizer<3>::coprimes_in_first_wheel[5] == 19);
static_assert(wheel_factorizer<3>::coprimes_in_first_wheel[6] == 23); static_assert(wheel_factorizer<3>::coprimes_in_first_wheel[6] == 23);
static_assert(wheel_factorizer<3>::coprimes_in_first_wheel[7] == 29); static_assert(wheel_factorizer<3>::coprimes_in_first_wheel[7] == 29);
static_assert(!wheel_factorizer<1>::is_prime<0>()); static_assert(!wheel_factorizer<1>::is_prime(0));
static_assert(!wheel_factorizer<1>::is_prime<1>()); static_assert(!wheel_factorizer<1>::is_prime(1));
static_assert(wheel_factorizer<1>::is_prime<2>()); static_assert(wheel_factorizer<1>::is_prime(2));
static_assert(!wheel_factorizer<2>::is_prime<0>()); static_assert(!wheel_factorizer<2>::is_prime(0));
static_assert(!wheel_factorizer<2>::is_prime<1>()); static_assert(!wheel_factorizer<2>::is_prime(1));
static_assert(wheel_factorizer<2>::is_prime<2>()); static_assert(wheel_factorizer<2>::is_prime(2));
static_assert(!wheel_factorizer<3>::is_prime<0>()); static_assert(!wheel_factorizer<3>::is_prime(0));
static_assert(!wheel_factorizer<3>::is_prime<1>()); static_assert(!wheel_factorizer<3>::is_prime(1));
static_assert(wheel_factorizer<3>::is_prime<2>()); static_assert(wheel_factorizer<3>::is_prime(2));
} // namespace } // namespace