refactor: error messages-related improvements

This commit is contained in:
Mateusz Pusz
2024-06-25 11:36:39 -05:00
parent b2307daeb6
commit 786c1bf7da
10 changed files with 113 additions and 62 deletions

View File

@@ -97,12 +97,20 @@ concept DerivedDimension =
MP_UNITS_EXPORT template<typename T> MP_UNITS_EXPORT template<typename T>
concept Dimension = detail::BaseDimension<T> || detail::DerivedDimension<T>; concept Dimension = detail::BaseDimension<T> || detail::DerivedDimension<T>;
namespace detail {
template<auto D1, auto D2>
concept SameDimension =
Dimension<MP_UNITS_REMOVE_CONST(decltype(D1))> && Dimension<MP_UNITS_REMOVE_CONST(decltype(D2))> && (D1 == D2);
}
/** /**
* @brief A concept checking if the argument is of the same dimension. * @brief A concept checking if the argument is of the same dimension.
* *
* Satisfied when both argument satisfy a `Dimension` concept and when they compare equal. * Satisfied when both argument satisfy a `Dimension` concept and when they compare equal.
*/ */
MP_UNITS_EXPORT template<typename T, auto D> MP_UNITS_EXPORT template<typename T, auto D>
concept DimensionOf = Dimension<T> && Dimension<MP_UNITS_REMOVE_CONST(decltype(D))> && (T{} == D); concept DimensionOf = Dimension<T> && Dimension<MP_UNITS_REMOVE_CONST(decltype(D))> && detail::SameDimension<T{}, D>;
} // namespace mp_units } // namespace mp_units

View File

@@ -56,12 +56,15 @@ template<Unit UFrom, Unit UTo>
return is_integral(get_canonical_unit(from).mag / get_canonical_unit(to).mag); return is_integral(get_canonical_unit(from).mag / get_canonical_unit(to).mag);
} }
template<typename T>
concept IsFloatingPoint = treat_as_floating_point<T>;
template<typename QFrom, typename QTo> template<typename QFrom, typename QTo>
concept QuantityConvertibleTo = concept QuantityConvertibleTo =
Quantity<QFrom> && Quantity<QTo> && implicitly_convertible(QFrom::quantity_spec, QTo::quantity_spec) && Quantity<QFrom> && Quantity<QTo> && detail::QuantitySpecConvertibleTo<QFrom::quantity_spec, QTo::quantity_spec> &&
convertible(QFrom::unit, QTo::unit) && detail::UnitConvertibleTo<QFrom::unit, QTo::unit> &&
(treat_as_floating_point<typename QTo::rep> || (IsFloatingPoint<typename QTo::rep> ||
(!treat_as_floating_point<typename QFrom::rep> && (integral_conversion_factor(QFrom::unit, QTo::unit)))) && (!IsFloatingPoint<typename QFrom::rep> && (integral_conversion_factor(QFrom::unit, QTo::unit)))) &&
// TODO consider providing constraints of sudo_cast here rather than testing if it can be called (its return type is // TODO consider providing constraints of sudo_cast here rather than testing if it can be called (its return type is
// deduced thus the function is evaluated here and may emit truncating conversion or other warnings) // deduced thus the function is evaluated here and may emit truncating conversion or other warnings)
requires(QFrom q) { detail::sudo_cast<QTo>(q); }; requires(QFrom q) { detail::sudo_cast<QTo>(q); };
@@ -75,23 +78,28 @@ template<typename Func, typename Q1, typename Q2,
concept InvocableQuantities = concept InvocableQuantities =
Quantity<Q1> && Quantity<Q2> && InvokeResultOf<Ch, Func, typename Q1::rep, typename Q2::rep>; Quantity<Q1> && Quantity<Q2> && InvokeResultOf<Ch, Func, typename Q1::rep, typename Q2::rep>;
// TODO remove the following when clang diagnostics improve
// https://github.com/llvm/llvm-project/issues/96660
template<auto R1, auto R2>
concept HaveCommonReferenceImpl = requires { common_reference(R1, R2); };
template<auto R1, auto R2>
concept HaveCommonReference = HaveCommonReferenceImpl<R1, R2>;
template<typename Func, typename Q1, typename Q2> template<typename Func, typename Q1, typename Q2>
concept CommonlyInvocableQuantities = concept CommonlyInvocableQuantities =
Quantity<Q1> && Quantity<Q2> && Quantity<Q1> && Quantity<Q2> && HaveCommonReference<Q1::reference, Q2::reference> &&
// (Q1::quantity_spec.character == Q2::quantity_spec.character) && // TODO enable when vector quantities are handled
// correctly
requires { common_reference(Q1::reference, Q2::reference); } &&
InvocableQuantities<Func, Q1, Q2, common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character>; InvocableQuantities<Func, Q1, Q2, common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character>;
template<typename Func, Quantity Q1, Quantity Q2> template<typename Func, Quantity Q1, Quantity Q2>
requires detail::CommonlyInvocableQuantities<Func, Q1, Q2> requires detail::CommonlyInvocableQuantities<Func, Q1, Q2>
using common_quantity_for = quantity<common_reference(Q1::reference, Q2::reference), using common_quantity_for = quantity<common_reference(Q1::reference, Q2::reference),
std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>; std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>;
template<auto T, auto R> template<auto T, auto R>
concept SameOriginalReferenceAs = DeltaReference<MP_UNITS_REMOVE_CONST(decltype(T))> && concept SameOriginalReferenceAs =
Reference<MP_UNITS_REMOVE_CONST(decltype(R))> && (get_original_reference(T) == R); DeltaReference<MP_UNITS_REMOVE_CONST(decltype(T))> && Reference<MP_UNITS_REMOVE_CONST(decltype(R))> &&
detail::SameReference<remove_reference_specifier(T), R>;
template<auto R1, auto R2, typename Rep1, typename Rep2> template<auto R1, auto R2, typename Rep1, typename Rep2>
concept SameValueAs = detail::SameOriginalReferenceAs<R1, R2> && std::same_as<Rep1, Rep2>; concept SameValueAs = detail::SameOriginalReferenceAs<R1, R2> && std::same_as<Rep1, Rep2>;
@@ -238,14 +246,14 @@ public:
#endif #endif
template<UnitCompatibleWith<unit, quantity_spec> U> template<UnitCompatibleWith<unit, quantity_spec> U>
requires requires(quantity q) { q.in(U{}); } requires detail::QuantityConvertibleTo<quantity, quantity<detail::make_reference(quantity_spec, U{}), Rep>>
[[nodiscard]] constexpr rep numerical_value_in(U) const noexcept [[nodiscard]] constexpr rep numerical_value_in(U) const noexcept
{ {
return (*this).in(U{}).numerical_value_is_an_implementation_detail_; return (*this).in(U{}).numerical_value_is_an_implementation_detail_;
} }
template<UnitCompatibleWith<unit, quantity_spec> U> template<UnitCompatibleWith<unit, quantity_spec> U>
requires requires(quantity q) { q.force_in(U{}); } requires requires(quantity q) { value_cast<U{}>(q); }
[[nodiscard]] constexpr rep force_numerical_value_in(U) const noexcept [[nodiscard]] constexpr rep force_numerical_value_in(U) const noexcept
{ {
return (*this).force_in(U{}).numerical_value_is_an_implementation_detail_; return (*this).force_in(U{}).numerical_value_is_an_implementation_detail_;

View File

@@ -53,7 +53,8 @@ namespace mp_units {
* @tparam ToQS a quantity specification to use for a target quantity * @tparam ToQS a quantity specification to use for a target quantity
*/ */
template<QuantitySpec auto ToQS, typename Q> template<QuantitySpec auto ToQS, typename Q>
requires Quantity<std::remove_cvref_t<Q>> && (castable(std::remove_reference_t<Q>::quantity_spec, ToQS)) requires Quantity<std::remove_cvref_t<Q>> &&
detail::QuantitySpecCastableTo<std::remove_reference_t<Q>::quantity_spec, ToQS>
[[nodiscard]] constexpr Quantity auto quantity_cast(Q&& q) [[nodiscard]] constexpr Quantity auto quantity_cast(Q&& q)
{ {
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_, return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
@@ -78,7 +79,8 @@ template<QuantitySpec auto ToQS, typename Q>
* @tparam ToQS a quantity specification to use for a target quantity point * @tparam ToQS a quantity specification to use for a target quantity point
*/ */
template<QuantitySpec auto ToQS, typename QP> template<QuantitySpec auto ToQS, typename QP>
requires QuantityPoint<std::remove_cvref_t<QP>> && (castable(std::remove_reference_t<QP>::quantity_spec, ToQS)) requires QuantityPoint<std::remove_cvref_t<QP>> &&
detail::QuantitySpecCastableTo<std::remove_reference_t<QP>::quantity_spec, ToQS>
[[nodiscard]] constexpr QuantityPoint auto quantity_cast(QP&& qp) [[nodiscard]] constexpr QuantityPoint auto quantity_cast(QP&& qp)
{ {
return QP{quantity_cast<ToQS>(std::forward<QP>(qp).quantity_from_origin_is_an_implementation_detail_), return QP{quantity_cast<ToQS>(std::forward<QP>(qp).quantity_from_origin_is_an_implementation_detail_),

View File

@@ -271,18 +271,18 @@ public:
} }
// unit conversions // unit conversions
template<UnitCompatibleWith<unit, quantity_spec> U> template<UnitCompatibleWith<unit, quantity_spec> ToU>
requires detail::QuantityConvertibleTo<quantity_type, quantity<detail::make_reference(quantity_spec, U{}), Rep>> requires detail::QuantityConvertibleTo<quantity_type, quantity<detail::make_reference(quantity_spec, ToU{}), Rep>>
[[nodiscard]] constexpr QuantityPointOf<quantity_spec> auto in(U) const [[nodiscard]] constexpr QuantityPointOf<quantity_spec> auto in(ToU) const
{ {
return ::mp_units::quantity_point{quantity_ref_from(PO).in(U{}), PO}; return ::mp_units::quantity_point{quantity_ref_from(PO).in(ToU{}), PO};
} }
template<UnitCompatibleWith<unit, quantity_spec> U> template<UnitCompatibleWith<unit, quantity_spec> ToU>
requires requires(quantity_type q) { value_cast<U{}>(q); } requires requires(quantity_type q) { value_cast<ToU{}>(q); }
[[nodiscard]] constexpr QuantityPointOf<quantity_spec> auto force_in(U) const [[nodiscard]] constexpr QuantityPointOf<quantity_spec> auto force_in(ToU) const
{ {
return ::mp_units::quantity_point{quantity_ref_from(PO).force_in(U{}), PO}; return ::mp_units::quantity_point{quantity_ref_from(PO).force_in(ToU{}), PO};
} }
// conversion operators // conversion operators

View File

@@ -126,7 +126,7 @@ struct quantity_spec_interface {
template<typename Self, typename Q> template<typename Self, typename Q>
[[nodiscard]] constexpr Quantity auto operator()(this Self self, Q&& q) [[nodiscard]] constexpr Quantity auto operator()(this Self self, Q&& q)
requires Quantity<std::remove_cvref_t<Q>> && requires Quantity<std::remove_cvref_t<Q>> &&
(explicitly_convertible(std::remove_reference_t<Q>::quantity_spec, self)) detail::QuantitySpecExplicitlyConvertibleTo<std::remove_reference_t<Q>::quantity_spec, self>
{ {
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_, return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
delta<detail::make_reference(self, std::remove_cvref_t<Q>::unit)>}; delta<detail::make_reference(self, std::remove_cvref_t<Q>::unit)>};
@@ -140,7 +140,7 @@ struct quantity_spec_interface {
template<typename Q, typename Self_ = Self> template<typename Q, typename Self_ = Self>
requires Quantity<std::remove_cvref_t<Q>> && requires Quantity<std::remove_cvref_t<Q>> &&
(explicitly_convertible(std::remove_reference_t<Q>::quantity_spec, Self_{})) detail::QuantitySpecExplicitlyConvertibleTo<std::remove_reference_t<Q>::quantity_spec, Self_{}>
[[nodiscard]] constexpr Quantity auto operator()(Q&& q) const [[nodiscard]] constexpr Quantity auto operator()(Q&& q) const
{ {
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_, return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
@@ -337,7 +337,7 @@ struct quantity_spec<Self, QS, Args...> : detail::propagate_equation<QS>, detail
template<typename Q, typename Self_ = Self> template<typename Q, typename Self_ = Self>
requires Quantity<std::remove_cvref_t<Q>> && requires Quantity<std::remove_cvref_t<Q>> &&
(explicitly_convertible(std::remove_reference_t<Q>::quantity_spec, Self_{})) detail::QuantitySpecExplicitlyConvertibleTo<std::remove_reference_t<Q>::quantity_spec, Self_{}>
[[nodiscard]] constexpr Quantity auto operator()(Q&& q) const [[nodiscard]] constexpr Quantity auto operator()(Q&& q) const
{ {
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_, return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
@@ -379,16 +379,16 @@ struct quantity_spec<Self, QS, Args...> : detail::propagate_equation<QS>, detail
#ifdef MP_UNITS_API_NO_CRTP #ifdef MP_UNITS_API_NO_CRTP
template<detail::NamedQuantitySpec auto QS, detail::DerivedQuantitySpec auto Eq, template<detail::NamedQuantitySpec auto QS, detail::DerivedQuantitySpec auto Eq,
one_of<quantity_character, struct is_kind> auto... Args> one_of<quantity_character, struct is_kind> auto... Args>
requires(!requires { QS._equation_; } || (requires { requires(!requires { QS._equation_; } ||
QS._equation_; ((requires { QS._equation_; }) && detail::QuantitySpecExplicitlyConvertibleTo<Eq, QS._equation_>)) &&
} && (explicitly_convertible(Eq, QS._equation_)))) && (... && !QuantitySpec<decltype(Args)>) (... && !QuantitySpec<decltype(Args)>)
struct quantity_spec<QS, Eq, Args...> : detail::quantity_spec_interface { struct quantity_spec<QS, Eq, Args...> : detail::quantity_spec_interface {
#else #else
template<typename Self, detail::NamedQuantitySpec auto QS, detail::DerivedQuantitySpec auto Eq, template<typename Self, detail::NamedQuantitySpec auto QS, detail::DerivedQuantitySpec auto Eq,
one_of<quantity_character, struct is_kind> auto... Args> one_of<quantity_character, struct is_kind> auto... Args>
requires(!requires { QS._equation_; } || (requires { requires(!requires { QS._equation_; } ||
QS._equation_; ((requires { QS._equation_; }) && detail::QuantitySpecExplicitlyConvertibleTo<Eq, QS._equation_>)) &&
} && (explicitly_convertible(Eq, QS._equation_)))) && (... && !QuantitySpec<decltype(Args)>) (... && !QuantitySpec<decltype(Args)>)
struct quantity_spec<Self, QS, Eq, Args...> : detail::quantity_spec_interface<Self> { struct quantity_spec<Self, QS, Eq, Args...> : detail::quantity_spec_interface<Self> {
#endif #endif
using _base_type_ = quantity_spec; using _base_type_ = quantity_spec;
@@ -498,7 +498,7 @@ template<QuantitySpec Q>
#ifdef MP_UNITS_API_NO_CRTP #ifdef MP_UNITS_API_NO_CRTP
template<typename Q> template<typename Q>
requires detail::QuantitySpecWithNoSpecifiers<Q> && (detail::get_kind_tree_root(Q{}) == Q{}) requires detail::QuantitySpecWithNoSpecifiers<Q> && detail::SameQuantitySpec<detail::get_kind_tree_root(Q{}), Q{}>
struct kind_of_<Q> final : Q::_base_type_ { struct kind_of_<Q> final : Q::_base_type_ {
using _base_type_ = kind_of_; using _base_type_ = kind_of_;
static constexpr auto _quantity_spec_ = Q{}; static constexpr auto _quantity_spec_ = Q{};
@@ -507,10 +507,10 @@ struct kind_of_<Q> final : Q::_base_type_ {
#if MP_UNITS_COMP_CLANG #if MP_UNITS_COMP_CLANG
template<typename Q> template<typename Q>
requires detail::QuantitySpecWithNoSpecifiers<Q> && (detail::get_kind_tree_root(Q{}) == Q{}) requires detail::QuantitySpecWithNoSpecifiers<Q> && detail::SameQuantitySpec<detail::get_kind_tree_root(Q{}), Q{}>
#else #else
template<detail::QuantitySpecWithNoSpecifiers Q> template<detail::QuantitySpecWithNoSpecifiers Q>
requires(detail::get_kind_tree_root(Q{}) == Q{}) requires detail::SameQuantitySpec<detail::get_kind_tree_root(Q{}), Q{}>
#endif #endif
struct kind_of_<Q> final : quantity_spec<kind_of_<Q>, Q{}>::_base_type_ { struct kind_of_<Q> final : quantity_spec<kind_of_<Q>, Q{}>::_base_type_ {
using _base_type_ = kind_of_; using _base_type_ = kind_of_;
@@ -519,7 +519,7 @@ struct kind_of_<Q> final : quantity_spec<kind_of_<Q>, Q{}>::_base_type_ {
#endif #endif
MP_UNITS_EXPORT template<detail::QuantitySpecWithNoSpecifiers auto Q> MP_UNITS_EXPORT template<detail::QuantitySpecWithNoSpecifiers auto Q>
requires(detail::get_kind_tree_root(Q) == Q) requires detail::SameQuantitySpec<detail::get_kind_tree_root(Q), Q>
inline constexpr kind_of_<MP_UNITS_REMOVE_CONST(decltype(Q))> kind_of; inline constexpr kind_of_<MP_UNITS_REMOVE_CONST(decltype(Q))> kind_of;
namespace detail { namespace detail {
@@ -1531,8 +1531,8 @@ template<QuantitySpec Q>
template<QuantitySpec Q1, QuantitySpec Q2> template<QuantitySpec Q1, QuantitySpec Q2>
[[nodiscard]] consteval QuantitySpec auto common_quantity_spec(Q1 q1, Q2 q2) [[nodiscard]] consteval QuantitySpec auto common_quantity_spec(Q1 q1, Q2 q2)
requires(implicitly_convertible(get_kind_tree_root(q1), get_kind_tree_root(q2)) || requires detail::QuantitySpecConvertibleTo<get_kind_tree_root(q1), get_kind_tree_root(q2)> ||
implicitly_convertible(get_kind_tree_root(q2), get_kind_tree_root(q1))) detail::QuantitySpecConvertibleTo<get_kind_tree_root(q2), get_kind_tree_root(q1)>
{ {
using QQ1 = decltype(detail::remove_kind(q1)); using QQ1 = decltype(detail::remove_kind(q1));
using QQ2 = decltype(detail::remove_kind(q2)); using QQ2 = decltype(detail::remove_kind(q2));

View File

@@ -125,18 +125,41 @@ MP_UNITS_EXPORT template<QuantitySpec Q>
namespace detail { namespace detail {
template<auto QS1, auto QS2>
concept SameQuantitySpec = QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS1))> &&
QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS2))> && (QS1 == QS2);
template<QuantitySpec Child, QuantitySpec Parent> template<QuantitySpec Child, QuantitySpec Parent>
[[nodiscard]] consteval bool is_child_of(Child ch, Parent p); [[nodiscard]] consteval bool is_child_of(Child ch, Parent p);
template<auto Child, auto Parent>
concept ChildQuantitySpecOf = (is_child_of(Child, Parent));
template<auto To, auto From> template<auto To, auto From>
concept NestedQuantityKindSpecOf = QuantitySpec<decltype(From)> && QuantitySpec<decltype(To)> && concept NestedQuantityKindSpecOf =
get_kind(From) != get_kind(To) && is_child_of(To, get_kind(From)._quantity_spec_); QuantitySpec<decltype(From)> && QuantitySpec<decltype(To)> &&
(!SameQuantitySpec<get_kind(From), get_kind(To)>)&&ChildQuantitySpecOf<To, get_kind(From)._quantity_spec_>;
template<auto From, auto To>
concept QuantitySpecConvertibleTo =
QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(From))> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(To))> &&
implicitly_convertible(From, To);
template<auto From, auto To>
concept QuantitySpecExplicitlyConvertibleTo =
QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(From))> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(To))> &&
explicitly_convertible(From, To);
template<auto From, auto To>
concept QuantitySpecCastableTo =
QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(From))> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(To))> &&
castable(From, To);
} // namespace detail } // namespace detail
MP_UNITS_EXPORT template<typename T, auto QS> MP_UNITS_EXPORT template<typename T, auto QS>
concept QuantitySpecOf = concept QuantitySpecOf =
QuantitySpec<T> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> && implicitly_convertible(T{}, QS) && QuantitySpec<T> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> && detail::QuantitySpecConvertibleTo<T{}, QS> &&
// the below is to make the following work // the below is to make the following work
// static_assert(ReferenceOf<si::radian, isq::angular_measure>); // static_assert(ReferenceOf<si::radian, isq::angular_measure>);
// static_assert(!ReferenceOf<si::radian, dimensionless>); // static_assert(!ReferenceOf<si::radian, dimensionless>);

View File

@@ -108,4 +108,12 @@ concept AbsoluteReference = is_specialization_of<T, absolute_>;
MP_UNITS_EXPORT_END MP_UNITS_EXPORT_END
namespace detail {
template<auto R1, auto R2>
concept SameReference =
Reference<MP_UNITS_REMOVE_CONST(decltype(R1))> && Reference<MP_UNITS_REMOVE_CONST(decltype(R2))> && (R1 == R2);
} // namespace detail
} // namespace mp_units } // namespace mp_units

View File

@@ -66,7 +66,7 @@ struct system_reference {
static constexpr auto coherent_unit = CoU; static constexpr auto coherent_unit = CoU;
template<Unit U> template<Unit U>
requires(convertible(coherent_unit, U{})) requires detail::UnitConvertibleTo<coherent_unit, U{}>
#if MP_UNITS_COMP_MSVC #if MP_UNITS_COMP_MSVC
[[nodiscard]] constexpr decltype(reference<MP_UNITS_REMOVE_CONST(decltype(Q)), U>{}) operator[](U) const [[nodiscard]] constexpr decltype(reference<MP_UNITS_REMOVE_CONST(decltype(Q)), U>{}) operator[](U) const
#else #else

View File

@@ -518,12 +518,11 @@ template<Unit U1, Unit U2>
} // namespace detail } // namespace detail
MP_UNITS_EXPORT [[nodiscard]] consteval bool operator==(Unit auto lhs, Unit auto rhs) MP_UNITS_EXPORT [[nodiscard]] consteval bool operator==(Unit auto lhs, Unit auto rhs)
{ {
auto canonical_lhs = get_canonical_unit(lhs); auto canonical_lhs = get_canonical_unit(lhs);
auto canonical_rhs = get_canonical_unit(rhs); auto canonical_rhs = get_canonical_unit(rhs);
return detail::have_same_canonical_reference_unit(canonical_lhs.reference_unit, canonical_rhs.reference_unit) && return convertible(canonical_lhs.reference_unit, canonical_rhs.reference_unit) &&
canonical_lhs.mag == canonical_rhs.mag; canonical_lhs.mag == canonical_rhs.mag;
} }
@@ -612,11 +611,11 @@ inline constexpr auto ppm = parts_per_million;
// clang-format on // clang-format on
// convertible_to // convertible
template<Unit U1, Unit U2> template<Unit From, Unit To>
[[nodiscard]] consteval bool convertible(U1 from, U2 to) [[nodiscard]] consteval bool convertible(From from, To to)
{ {
if constexpr (is_same_v<U1, U2>) if constexpr (is_same_v<From, To>)
return true; return true;
else else
return detail::have_same_canonical_reference_unit(from, to); return detail::have_same_canonical_reference_unit(from, to);
@@ -627,7 +626,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(detail::have_same_canonical_reference_unit(u1, u2)) requires(convertible(u1, u2))
{ {
if constexpr (is_same_v<U1, U2>) if constexpr (is_same_v<U1, U2>)
return u1; return u1;

View File

@@ -182,18 +182,22 @@ concept AssociatedUnit = Unit<U> && detail::has_associated_quantity(U{});
* the provided quantity_spec type. * the provided quantity_spec type.
*/ */
MP_UNITS_EXPORT template<typename U, auto QS> MP_UNITS_EXPORT template<typename U, auto QS>
concept UnitOf = concept UnitOf = AssociatedUnit<U> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> &&
AssociatedUnit<U> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> && detail::QuantitySpecConvertibleTo<get_quantity_spec(U{}), QS> &&
implicitly_convertible(get_quantity_spec(U{}), QS) &&
// the below is to make `dimensionless[radian]` invalid // the below is to make `dimensionless[radian]` invalid
(get_kind(QS) == get_kind(get_quantity_spec(U{})) || !detail::NestedQuantityKindSpecOf<get_quantity_spec(U{}), QS>); (detail::SameQuantitySpec<get_kind(QS), get_kind(get_quantity_spec(U{}))> ||
!detail::NestedQuantityKindSpecOf<get_quantity_spec(U{}), QS>);
MP_UNITS_EXPORT template<Unit From, Unit To>
[[nodiscard]] consteval bool convertible(From from, To to);
namespace detail { namespace detail {
template<Unit U1, Unit U2> template<auto From, auto To>
[[nodiscard]] consteval bool have_same_canonical_reference_unit(U1 u1, U2 u2); concept UnitConvertibleTo =
Unit<MP_UNITS_REMOVE_CONST(decltype(From))> && Unit<MP_UNITS_REMOVE_CONST(decltype(To))> && (convertible(From, To));
} } // namespace detail
/** /**
* @brief A concept matching all units compatible with the provided unit and quantity spec * @brief A concept matching all units compatible with the provided unit and quantity spec
@@ -201,10 +205,9 @@ template<Unit U1, Unit U2>
* Satisfied by all units that have the same canonical reference as `U2` and in case they * Satisfied by all units that have the same canonical reference as `U2` and in case they
* have associated quantity specification it should satisfy `UnitOf<QS>`. * have associated quantity specification it should satisfy `UnitOf<QS>`.
*/ */
MP_UNITS_EXPORT template<typename U, auto U2, auto QS> MP_UNITS_EXPORT template<typename U, auto FromU, 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(FromU))> && 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>)&&detail::UnitConvertibleTo<FromU, U{}>;
} // namespace mp_units } // namespace mp_units