fix: inconsistencies in get_common_quantity_spec() fixed

This commit is contained in:
Mateusz Pusz
2024-12-28 14:02:22 +01:00
parent c887b11e6f
commit a9525fa301
2 changed files with 65 additions and 50 deletions

View File

@ -641,10 +641,6 @@ MP_UNITS_EXPORT_END
} // namespace mp_units
// This specialization overrides `std` defaults for quantities
template<mp_units::Quantity Q1, mp_units::Quantity Q2>
struct std::common_type<Q1, Q2> {};
template<mp_units::Quantity Q1, mp_units::Quantity Q2>
requires requires {
{ mp_units::get_common_reference(Q1::reference, Q2::reference) } -> mp_units::Reference;

View File

@ -1180,15 +1180,70 @@ MP_UNITS_EXPORT template<QuantitySpec Q>
namespace detail {
template<QuantitySpec QS1, QuantitySpec QS2>
[[nodiscard]] consteval bool have_common_quantity_spec(QS1, QS2)
struct no_common_quantity_spec {};
template<QuantitySpec Q1, QuantitySpec Q2>
[[nodiscard]] consteval auto get_common_quantity_spec_impl(Q1 q1, Q2 q2)
{
constexpr auto qs1 = detail::get_kind_tree_root(QS1{});
constexpr auto qs2 = detail::get_kind_tree_root(QS2{});
return (!detail::have_common_base(qs1, qs2) &&
(mp_units::implicitly_convertible(qs1, qs2) || mp_units::implicitly_convertible(qs2, qs1))) ||
(qs1 == qs2 || (detail::is_child_of(qs2, qs1) && QuantityKindSpec<QS1>) ||
(detail::is_child_of(qs1, qs2) && QuantityKindSpec<QS2>));
if constexpr (is_same_v<Q1, Q2>)
return q1;
else {
constexpr bool q1q2 = mp_units::implicitly_convertible(Q1{}, Q2{});
constexpr bool q2q1 = mp_units::implicitly_convertible(Q2{}, Q1{});
// in case of interconvertible quantities we try to figure out which one is a better choice
if constexpr (q1q2 && q2q1) {
// quantity kinds have lower priority
if constexpr (detail::QuantityKindSpec<Q1> && !detail::QuantityKindSpec<Q2>)
return q2;
else if constexpr (!detail::QuantityKindSpec<Q1> && detail::QuantityKindSpec<Q2>)
return q1;
else {
using q1_type = decltype(detail::remove_kind(q1));
using q2_type = decltype(detail::remove_kind(q2));
// derived quantities have a lower priority
if constexpr (detail::NamedQuantitySpec<q1_type> && detail::DerivedQuantitySpec<q2_type>)
return q1;
else if constexpr (detail::DerivedQuantitySpec<q1_type> && detail::NamedQuantitySpec<q2_type>)
return q2;
else
// TODO Check if there is a better choice here
return detail::better_type_name(q1, q2);
}
}
// we prefer a quantity to which we can convert the other quantity
else if constexpr (q1q2)
return q2;
else if constexpr (q2q1)
return q1;
// if quantities can't be converted in any direction check if they have a common base in the tree
else if constexpr (detail::have_common_base(Q1{}, Q2{})) {
constexpr auto base = detail::get_common_base(Q1{}, Q2{});
if constexpr (mp_units::implicitly_convertible(q1, base) && mp_units::implicitly_convertible(q2, base))
return base;
else
return no_common_quantity_spec{};
} else {
// verify kind tree roots as the last resort check
constexpr auto q1_root = detail::get_kind_tree_root(Q1{});
constexpr auto q2_root = detail::get_kind_tree_root(Q2{});
if constexpr (mp_units::implicitly_convertible(q1_root, q2_root))
return q2_root;
else if constexpr (mp_units::implicitly_convertible(q2_root, q1_root))
return q1_root;
else
return no_common_quantity_spec{};
}
}
}
template<QuantitySpec Q1, QuantitySpec Q2>
constexpr auto get_common_quantity_spec_result = detail::get_common_quantity_spec_impl(Q1{}, Q2{});
template<QuantitySpec Q1, QuantitySpec Q2>
[[nodiscard]] consteval bool have_common_quantity_spec(Q1, Q2)
{
return !is_same_v<decltype(detail::get_common_quantity_spec_result<Q1, Q2>), const no_common_quantity_spec>;
}
} // namespace detail
@ -1199,45 +1254,9 @@ MP_UNITS_EXPORT_BEGIN
template<QuantitySpec Q1, QuantitySpec Q2>
requires(detail::have_common_quantity_spec(Q1{}, Q2{}))
[[nodiscard]] consteval QuantitySpec auto get_common_quantity_spec(Q1 q1, Q2 q2)
[[nodiscard]] consteval QuantitySpec auto get_common_quantity_spec(Q1, Q2)
{
// NOLINTBEGIN(bugprone-branch-clone)
if constexpr (is_same_v<Q1, Q2>) return q1;
// quantity kinds have lower priority
else if constexpr (detail::QuantityKindSpec<Q1> && !detail::QuantityKindSpec<Q2>)
return q2;
else if constexpr (!detail::QuantityKindSpec<Q1> && detail::QuantityKindSpec<Q2>)
return q1;
else {
constexpr bool q1q2 = mp_units::implicitly_convertible(Q1{}, Q2{});
constexpr bool q2q1 = mp_units::implicitly_convertible(Q2{}, Q1{});
// in case of interconvertible quantities we treat derived quantities with a lower priority
if constexpr (q1q2 && q2q1) {
if constexpr (detail::DerivedQuantitySpec<decltype(detail::remove_kind(q1))>)
return q2;
else
return q1;
}
// we prefer a quantity to which we can convert the other quantity
else if constexpr (q1q2)
return q2;
else if constexpr (q2q1)
return q1;
// if quantities can't be converted in any direction check if they have a common base in the tree
else if constexpr (detail::have_common_base(Q1{}, Q2{}))
return detail::get_common_base(q1, q2);
else {
// verify kind tree roots as the last resort check
constexpr auto q1_root = detail::get_kind_tree_root(Q1{});
constexpr auto q2_root = detail::get_kind_tree_root(Q2{});
if constexpr (mp_units::implicitly_convertible(q1_root, q2_root))
return q2_root;
else if constexpr (mp_units::implicitly_convertible(q2_root, q1_root))
return q1_root;
}
}
// NOLINTEND(bugprone-branch-clone)
return detail::get_common_quantity_spec_result<Q1, Q2>;
}
[[nodiscard]] consteval QuantitySpec auto get_common_quantity_spec(QuantitySpec auto q1, QuantitySpec auto q2,