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>
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.
*
* Satisfied when both argument satisfy a `Dimension` concept and when they compare equal.
*/
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

View File

@@ -56,12 +56,15 @@ template<Unit UFrom, Unit UTo>
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>
concept QuantityConvertibleTo =
Quantity<QFrom> && Quantity<QTo> && implicitly_convertible(QFrom::quantity_spec, QTo::quantity_spec) &&
convertible(QFrom::unit, QTo::unit) &&
(treat_as_floating_point<typename QTo::rep> ||
(!treat_as_floating_point<typename QFrom::rep> && (integral_conversion_factor(QFrom::unit, QTo::unit)))) &&
Quantity<QFrom> && Quantity<QTo> && detail::QuantitySpecConvertibleTo<QFrom::quantity_spec, QTo::quantity_spec> &&
detail::UnitConvertibleTo<QFrom::unit, QTo::unit> &&
(IsFloatingPoint<typename QTo::rep> ||
(!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
// deduced thus the function is evaluated here and may emit truncating conversion or other warnings)
requires(QFrom q) { detail::sudo_cast<QTo>(q); };
@@ -75,23 +78,28 @@ template<typename Func, typename Q1, typename Q2,
concept InvocableQuantities =
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>
concept CommonlyInvocableQuantities =
Quantity<Q1> && Quantity<Q2> &&
// (Q1::quantity_spec.character == Q2::quantity_spec.character) && // TODO enable when vector quantities are handled
// correctly
requires { common_reference(Q1::reference, Q2::reference); } &&
Quantity<Q1> && Quantity<Q2> && HaveCommonReference<Q1::reference, Q2::reference> &&
InvocableQuantities<Func, Q1, Q2, common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character>;
template<typename Func, Quantity Q1, Quantity Q2>
requires detail::CommonlyInvocableQuantities<Func, Q1, Q2>
using common_quantity_for = quantity<common_reference(Q1::reference, Q2::reference),
std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>;
template<auto T, auto R>
concept SameOriginalReferenceAs = DeltaReference<MP_UNITS_REMOVE_CONST(decltype(T))> &&
Reference<MP_UNITS_REMOVE_CONST(decltype(R))> && (get_original_reference(T) == R);
concept SameOriginalReferenceAs =
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>
concept SameValueAs = detail::SameOriginalReferenceAs<R1, R2> && std::same_as<Rep1, Rep2>;
@@ -238,14 +246,14 @@ public:
#endif
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
{
return (*this).in(U{}).numerical_value_is_an_implementation_detail_;
}
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
{
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
*/
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)
{
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
*/
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)
{
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
template<UnitCompatibleWith<unit, quantity_spec> U>
requires detail::QuantityConvertibleTo<quantity_type, quantity<detail::make_reference(quantity_spec, U{}), Rep>>
[[nodiscard]] constexpr QuantityPointOf<quantity_spec> auto in(U) const
template<UnitCompatibleWith<unit, quantity_spec> ToU>
requires detail::QuantityConvertibleTo<quantity_type, quantity<detail::make_reference(quantity_spec, ToU{}), Rep>>
[[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>
requires requires(quantity_type q) { value_cast<U{}>(q); }
[[nodiscard]] constexpr QuantityPointOf<quantity_spec> auto force_in(U) const
template<UnitCompatibleWith<unit, quantity_spec> ToU>
requires requires(quantity_type q) { value_cast<ToU{}>(q); }
[[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

View File

@@ -126,7 +126,7 @@ struct quantity_spec_interface {
template<typename Self, typename Q>
[[nodiscard]] constexpr Quantity auto operator()(this Self self, Q&& 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_,
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>
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
{
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>
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
{
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
template<detail::NamedQuantitySpec auto QS, detail::DerivedQuantitySpec auto Eq,
one_of<quantity_character, struct is_kind> auto... Args>
requires(!requires { QS._equation_; } || (requires {
QS._equation_;
} && (explicitly_convertible(Eq, QS._equation_)))) && (... && !QuantitySpec<decltype(Args)>)
requires(!requires { QS._equation_; } ||
((requires { QS._equation_; }) && detail::QuantitySpecExplicitlyConvertibleTo<Eq, QS._equation_>)) &&
(... && !QuantitySpec<decltype(Args)>)
struct quantity_spec<QS, Eq, Args...> : detail::quantity_spec_interface {
#else
template<typename Self, detail::NamedQuantitySpec auto QS, detail::DerivedQuantitySpec auto Eq,
one_of<quantity_character, struct is_kind> auto... Args>
requires(!requires { QS._equation_; } || (requires {
QS._equation_;
} && (explicitly_convertible(Eq, QS._equation_)))) && (... && !QuantitySpec<decltype(Args)>)
requires(!requires { QS._equation_; } ||
((requires { QS._equation_; }) && detail::QuantitySpecExplicitlyConvertibleTo<Eq, QS._equation_>)) &&
(... && !QuantitySpec<decltype(Args)>)
struct quantity_spec<Self, QS, Eq, Args...> : detail::quantity_spec_interface<Self> {
#endif
using _base_type_ = quantity_spec;
@@ -498,7 +498,7 @@ template<QuantitySpec Q>
#ifdef MP_UNITS_API_NO_CRTP
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_ {
using _base_type_ = kind_of_;
static constexpr auto _quantity_spec_ = Q{};
@@ -507,10 +507,10 @@ struct kind_of_<Q> final : Q::_base_type_ {
#if MP_UNITS_COMP_CLANG
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
template<detail::QuantitySpecWithNoSpecifiers Q>
requires(detail::get_kind_tree_root(Q{}) == Q{})
requires detail::SameQuantitySpec<detail::get_kind_tree_root(Q{}), Q{}>
#endif
struct kind_of_<Q> final : quantity_spec<kind_of_<Q>, Q{}>::_base_type_ {
using _base_type_ = kind_of_;
@@ -519,7 +519,7 @@ struct kind_of_<Q> final : quantity_spec<kind_of_<Q>, Q{}>::_base_type_ {
#endif
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;
namespace detail {
@@ -1531,8 +1531,8 @@ template<QuantitySpec Q>
template<QuantitySpec Q1, QuantitySpec 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)) ||
implicitly_convertible(get_kind_tree_root(q2), get_kind_tree_root(q1)))
requires detail::QuantitySpecConvertibleTo<get_kind_tree_root(q1), get_kind_tree_root(q2)> ||
detail::QuantitySpecConvertibleTo<get_kind_tree_root(q2), get_kind_tree_root(q1)>
{
using QQ1 = decltype(detail::remove_kind(q1));
using QQ2 = decltype(detail::remove_kind(q2));

View File

@@ -125,18 +125,41 @@ MP_UNITS_EXPORT template<QuantitySpec Q>
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>
[[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>
concept NestedQuantityKindSpecOf = QuantitySpec<decltype(From)> && QuantitySpec<decltype(To)> &&
get_kind(From) != get_kind(To) && is_child_of(To, get_kind(From)._quantity_spec_);
concept NestedQuantityKindSpecOf =
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
MP_UNITS_EXPORT template<typename T, auto QS>
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
// static_assert(ReferenceOf<si::radian, isq::angular_measure>);
// static_assert(!ReferenceOf<si::radian, dimensionless>);

View File

@@ -108,4 +108,12 @@ concept AbsoluteReference = is_specialization_of<T, absolute_>;
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

View File

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

View File

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

View File

@@ -182,18 +182,22 @@ concept AssociatedUnit = Unit<U> && detail::has_associated_quantity(U{});
* the provided quantity_spec type.
*/
MP_UNITS_EXPORT template<typename U, auto QS>
concept UnitOf =
AssociatedUnit<U> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> &&
implicitly_convertible(get_quantity_spec(U{}), QS) &&
concept UnitOf = AssociatedUnit<U> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> &&
detail::QuantitySpecConvertibleTo<get_quantity_spec(U{}), QS> &&
// 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 {
template<Unit U1, Unit U2>
[[nodiscard]] consteval bool have_same_canonical_reference_unit(U1 u1, U2 u2);
template<auto From, auto To>
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
@@ -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
* 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 =
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));
Unit<U> && Unit<MP_UNITS_REMOVE_CONST(decltype(FromU))> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> &&
(!AssociatedUnit<U> || UnitOf<U, QS>)&&detail::UnitConvertibleTo<FromU, U{}>;
} // namespace mp_units