diff --git a/src/core/include/units/bits/quantity_cast.h b/src/core/include/units/bits/quantity_cast.h index a3bbe7e3..6424d87a 100644 --- a/src/core/include/units/bits/quantity_cast.h +++ b/src/core/include/units/bits/quantity_cast.h @@ -36,18 +36,12 @@ UNITS_DIAGNOSTIC_IGNORE_LOSS_OF_DATA namespace units { -template +template Rep> class quantity; // template U, Representation Rep> // class quantity_point; -// template U, Representation Rep> -// class quantity_kind; - -// template U, Representation Rep> -// class quantity_point_kind; - /** * @brief Explicit cast of a quantity * @@ -180,7 +174,7 @@ template * @tparam ToRep a representation type to use for a target quantity */ template - requires std::constructible_from + requires RepresentationOf && std::constructible_from [[nodiscard]] constexpr auto quantity_cast(const quantity& q) { return quantity_cast>(q); @@ -240,128 +234,6 @@ template // return quantity_point_cast, ToU, Rep>>(q); // } -// /** -// * @brief Explicit cast of a quantity kind -// * -// * Implicit conversions between quantity kinds of different types are allowed only for "safe" -// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used. -// * -// * This cast gets the target (quantity) kind type to cast to or anything that works for quantity_cast. For example: -// * -// * auto q1 = units::quantity_kind_cast(quantity_kind{ns::width{1 * mm}); -// * auto q1 = units::quantity_kind_cast(ns::width{1 * m}); -// * auto q1 = units::quantity_kind_cast>(ns::width{1 * mm}); -// * auto q1 = units::quantity_kind_cast(ns::rate_of_climb{200 * Gal}); -// * auto q1 = units::quantity_kind_cast(ns::width{1 * mm}); -// * auto q1 = units::quantity_kind_cast(ns::width{1.0 * mm}); -// * -// * @tparam CastSpec a target (quantity) kind type to cast to or anything that works for quantity_cast -// */ -// template -// [[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind& qk) -// requires requires { -// requires is_specialization_of; -// requires requires { quantity_cast(qk.common()); }; -// } || requires { -// requires Kind; -// requires UnitOf; -// } || requires { quantity_cast(qk.common()); } // TODO: Simplify when Clang catches up. -// { -// if constexpr (is_specialization_of) -// return CastSpec(quantity_cast(qk.common())); -// else if constexpr (Kind) -// return quantity_kind(qk.common()); -// else { -// auto q{quantity_cast(qk.common())}; -// using Q = decltype(q); -// return quantity_kind(static_cast(q)); -// } -// } - -// /** -// * @brief Explicit cast of a quantity kind -// * -// * Implicit conversions between quantity kinds of different types are allowed only for "safe" -// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used. -// * -// * This cast gets both the target kind and unit to cast to. For example: -// * -// * auto q1 = units::quantity_kind_cast(w); -// * -// * @note This cast is especially useful when working with quantity kinds of unknown kind. -// * -// * @tparam ToK the kind type to use for the target quantity -// * @tparam ToU the unit type to use for the target quantity -// */ -// template -// requires equivalent && UnitOf -// [[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind& qk) -// { -// return quantity_kind_cast>(qk); -// } - -// /** -// * @brief Explicit cast of a quantity point kind -// * -// * Implicit conversions between quantity point kinds of different types are allowed only for "safe" -// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used. -// * -// * This cast gets the target (quantity) point kind type to cast to or anything that works for quantity_kind_cast. -// For -// * example: -// * -// * auto q1 = units::quantity_point_kind_cast(ns::x_coordinate{1 * mm}); -// * auto q1 = units::quantity_point_kind_cast(ns::x_coordinate{1 * mm}); -// * auto q1 = units::quantity_point_kind_cast(ns::x_coordinate{1 * m}); -// * auto q1 = units::quantity_point_kind_cast(ns::x_coordinate{1 * m}); -// * auto q1 = units::quantity_point_kind_cast>(ns::x_coordinate{1 * -// mm}); -// * auto q1 = -// units::quantity_point_kind_cast(quantity_point_kind(ns::rate_of_climb{200 -// * * Gal})); auto q1 = units::quantity_point_kind_cast(ns::x_coordinate{1 * mm}); auto q1 = -// * units::quantity_point_kind_cast(ns::x_coordinate{1.0 * mm}); -// * -// * @tparam CastSpec a target (quantity) point kind type to cast to or anything that works for quantity_kind_cast -// */ -// template -// [[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind& qpk) -// requires requires { -// requires is_specialization_of; -// requires requires { quantity_kind_cast(qpk.relative()); }; -// requires equivalent; -// } || requires { requires PointKind && UnitOf; } || -// requires { quantity_kind_cast(qpk.relative()); } // TODO: Simplify when Clang catches up. -// { -// if constexpr (is_specialization_of) -// return CastSpec(quantity_kind_cast(qpk.relative())); -// else if constexpr (PointKind) -// return quantity_point_kind(quantity_kind_cast(qpk.relative())); -// else -// return quantity_point_kind(quantity_kind_cast(qpk.relative())); -// } - -// /** -// * @brief Explicit cast of a quantity point kind -// * -// * Implicit conversions between quantity point kinds of different types are allowed only for "safe" -// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used. -// * -// * This cast gets both the target point kind and unit to cast to. For example: -// * -// * auto q1 = units::quantity_point_kind_cast(x); -// * -// * @note This cast is especially useful when working with quantity point kinds of unknown point kind. -// * -// * @tparam ToPK the point kind type to use for the target quantity -// * @tparam ToU the unit type to use for the target quantity -// */ -// template -// requires equivalent && UnitOf -// [[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind& qpk) -// { -// return quantity_point_kind_cast>(qpk); -// } - } // namespace units UNITS_DIAGNOSTIC_POP diff --git a/src/core/include/units/bits/quantity_concepts.h b/src/core/include/units/bits/quantity_concepts.h index f6896341..b4341305 100644 --- a/src/core/include/units/bits/quantity_concepts.h +++ b/src/core/include/units/bits/quantity_concepts.h @@ -126,4 +126,17 @@ concept weak_quantity_of = Quantity && (Reference> && Q::dimension == V.dimension && Q::unit == V.unit)); +template +concept quantity_like = requires(T q) { + quantity_like_traits::dimension; + quantity_like_traits::unit; + typename quantity_like_traits::rep; + requires Dimension::dimension)>>; + requires Unit::unit)>>; + requires Representation::rep>; + { + quantity_like_traits::number(q) + } -> std::convertible_to::rep>; +}; + } // namespace units diff --git a/src/core/include/units/quantity.h b/src/core/include/units/quantity.h index d8e7f379..cddcb05c 100644 --- a/src/core/include/units/quantity.h +++ b/src/core/include/units/quantity.h @@ -47,18 +47,13 @@ inline constexpr auto make_quantity = [](Representation auto&& v) { }; template -concept quantity_one = Quantity && (T::dimension == dimension_one) && (T::unit == one); +concept quantity_one = quantity_of; } // namespace detail -template -concept floating_point_ = // exposition only - (Quantity && treat_as_floating_point) || (!Quantity && treat_as_floating_point); - template concept rep_safe_constructible_from_ = // exposition only - (!Quantity>) && std::constructible_from && - (floating_point_ || (!floating_point_)); + std::constructible_from && (treat_as_floating_point || !treat_as_floating_point); // QFrom ratio is an exact multiple of QTo template @@ -69,32 +64,14 @@ concept harmonic_ = // exposition only template concept quantity_convertible_to_ = // exposition only Quantity && Quantity && requires(QFrom q) { quantity_cast(q); } && - (floating_point_ || (!floating_point_ && harmonic_)); + (treat_as_floating_point || + (!treat_as_floating_point && harmonic_)); -template -concept quantity_value_for_ = std::regular_invocable && Representation>; +template +concept invoke_result_of_ = + std::regular_invocable && RepresentationOf, Ch>; -template -concept invoke_result_convertible_to_ = Representation && quantity_value_for_ && - rep_safe_constructible_from_, T>; - -template -concept have_quantity_for_ = Quantity && (!Quantity) && quantity_value_for_; - -template -concept QuantityLike = requires(T q) { - quantity_like_traits::dimension; - quantity_like_traits::unit; - typename quantity_like_traits::rep; - requires Dimension::dimension)>>; - requires Unit::unit)>>; - requires Representation::rep>; - { - quantity_like_traits::number(q) - } -> std::convertible_to::rep>; -}; - -template +template using quantity_like_type = quantity::dimension, quantity_like_traits::unit>{}, typename quantity_like_traits::rep>; @@ -108,7 +85,7 @@ using quantity_like_type = quantity::dimension * @tparam U a measurement unit of the quantity * @tparam Rep a type to be used to represent values of a quantity */ -template +template Rep = double> class quantity { Rep number_; public: @@ -160,7 +137,7 @@ public: { } - template + template requires quantity_convertible_to_, quantity> constexpr explicit quantity(const Q& q) : quantity(quantity_like_type(quantity_like_traits::number(q))) { @@ -315,7 +292,7 @@ public: template constexpr quantity& operator%=(const Rep2& rhs) - requires(!floating_point_) && (!floating_point_) && requires(rep a, const Rep2 b) { + requires(!treat_as_floating_point) && (!treat_as_floating_point) && requires(rep a, const Rep2 b) { { a %= b } -> std::same_as; @@ -328,11 +305,12 @@ public: template constexpr quantity& operator%=(const Q& rhs) - requires(!floating_point_) && (!floating_point_) && requires(rep a, const typename Q::rep b) { - { - a %= b - } -> std::same_as; - } + requires(!treat_as_floating_point) && (!treat_as_floating_point) && + requires(rep a, const typename Q::rep b) { + { + a %= b + } -> std::same_as; + } { gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); number_ %= rhs.number(); @@ -340,7 +318,7 @@ public: } constexpr quantity& operator%=(const quantity& q) - requires(!floating_point_) && requires(rep a, rep b) { + requires(!treat_as_floating_point) && requires(rep a, rep b) { { a %= b } -> std::same_as; @@ -358,17 +336,18 @@ public: requires requires { // TODO: Simplify when Clang catches up. requires !Quantity; requires unit == ::units::one; - requires invoke_result_convertible_to_, rep, Value>; + requires invoke_result_of_, rep, Value>; } { return ::units::quantity(lhs.number() + rhs); } + template [[nodiscard]] friend constexpr Quantity auto operator+(const Value& lhs, const quantity& rhs) requires requires { // TODO: Simplify when Clang catches up. requires !Quantity; requires unit == ::units::one; - requires invoke_result_convertible_to_, Value, rep>; + requires invoke_result_of_, Value, rep>; } { return ::units::quantity(lhs + rhs.number()); @@ -379,24 +358,25 @@ public: requires requires { // TODO: Simplify when Clang catches up. requires !Quantity; requires unit == ::units::one; - requires invoke_result_convertible_to_, rep, Value>; + requires invoke_result_of_, rep, Value>; } { return ::units::quantity(lhs.number() - rhs); } + template [[nodiscard]] friend constexpr Quantity auto operator-(const Value& lhs, const quantity& rhs) requires requires { // TODO: Simplify when Clang catches up. requires !Quantity; requires unit == ::units::one; - requires invoke_result_convertible_to_, Value, rep>; + requires invoke_result_of_, Value, rep>; } { return ::units::quantity(lhs - rhs.number()); } template - requires invoke_result_convertible_to_, rep, const Value&> + requires invoke_result_of_, rep, const Value&> [[nodiscard]] friend constexpr Quantity auto operator*(const quantity& q, const Value& v) { using ret = quantity, rep, Value>>; @@ -404,7 +384,7 @@ public: } template - requires invoke_result_convertible_to_, const Value&, rep> + requires invoke_result_of_, const Value&, rep> [[nodiscard]] friend constexpr Quantity auto operator*(const Value& v, const quantity& q) { using ret = quantity, Value, rep>>; @@ -412,7 +392,7 @@ public: } template - requires(!Quantity) && invoke_result_convertible_to_, rep, const Value&> + requires(!Quantity) && invoke_result_of_, rep, const Value&> [[nodiscard]] friend constexpr Quantity auto operator/(const quantity& q, const Value& v) { gsl_ExpectsAudit(v != quantity_values::zero()); @@ -421,15 +401,15 @@ public: } template - requires(!Quantity) && invoke_result_convertible_to_, const Value&, rep> + requires(!Quantity) && invoke_result_of_, const Value&, rep> [[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q) { return detail::make_quantity(v / q.number()); } template - requires(!Quantity) && (!floating_point_) && - (!floating_point_) && invoke_result_convertible_to_, rep, const Value&> + requires(!Quantity) && (!treat_as_floating_point) && (!treat_as_floating_point) && + invoke_result_of_, rep, const Value&> [[nodiscard]] friend constexpr Quantity auto operator%(const quantity& q, const Value& v) { gsl_ExpectsAudit(v != quantity_values::zero()); @@ -438,7 +418,7 @@ public: } [[nodiscard]] friend constexpr Quantity auto operator%(const quantity& lhs, const quantity& rhs) - requires(!floating_point_) && invoke_result_convertible_to_, rep, rep> + requires(!treat_as_floating_point) && invoke_result_of_, rep, rep> { gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); using ret = quantity, rep, rep>>; @@ -447,7 +427,7 @@ public: [[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity& rhs) requires std::three_way_comparable -#if UNITS_COMP_GCC == 10 && UNITS_COMP_GCC_MINOR >= 2 +#if __cpp_impl_three_way_comparison = default; #else { @@ -462,17 +442,23 @@ public: template explicit(false) quantity(quantity) -> quantity; -template -explicit(false) quantity(Rep)->quantity; +template Rep> +explicit(false) quantity(Rep) -> quantity; -template +#if !UNITS_COMP_CLANG || UNITS_COMP_CLANG > 16 +template Rep> +explicit(false) quantity(Rep&&) -> quantity; +#endif + +template explicit quantity(Q) -> quantity::dimension, quantity_like_traits::unit>{}, typename quantity_like_traits::rep>; // non-member binary operators template requires(interconvertible(Q1::reference, Q2::reference)) && - quantity_value_for_, typename Q1::rep, typename Q2::rep> + invoke_result_of_, + typename Q1::rep, typename Q2::rep> [[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs) { constexpr auto ref = common_reference(Q1::reference, Q2::reference); @@ -482,7 +468,8 @@ template template requires(interconvertible(Q1::reference, Q2::reference)) && - quantity_value_for_, typename Q1::rep, typename Q2::rep> + invoke_result_of_, + typename Q1::rep, typename Q2::rep> [[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs) { constexpr auto ref = common_reference(Q1::reference, Q2::reference); @@ -491,14 +478,16 @@ template } template - requires quantity_value_for_, typename Q1::rep, typename Q2::rep> + requires invoke_result_of_<(Q1::quantity_spec * Q2::quantity_spec).character, std::multiplies<>, typename Q1::rep, + typename Q2::rep> [[nodiscard]] constexpr Quantity auto operator*(const Q1& lhs, const Q2& rhs) { return detail::make_quantity(lhs.number() * rhs.number()); } template - requires quantity_value_for_, typename Q1::rep, typename Q2::rep> + requires invoke_result_of_<(Q1::quantity_spec / Q2::quantity_spec).character, std::divides<>, typename Q1::rep, + typename Q2::rep> [[nodiscard]] constexpr Quantity auto operator/(const Q1& lhs, const Q2& rhs) { gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); @@ -506,14 +495,13 @@ template } template - requires(!floating_point_) && (!floating_point_) && - (interconvertible(Q1::reference, Q2::reference) || quantity_of) && - quantity_value_for_, typename Q1::rep, typename Q2::rep> + requires(!treat_as_floating_point) && (!treat_as_floating_point) && + (interconvertible(Q1::reference, Q2::reference) || quantity_of) && + invoke_result_of_, typename Q1::rep, typename Q2::rep> [[nodiscard]] constexpr Quantity auto operator%(const Q1& lhs, const Q2& rhs) { gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); - using ret = quantity{}, - std::invoke_result_t, typename Q1::rep, typename Q2::rep>>; + using ret = quantity, typename Q1::rep, typename Q2::rep>>; return ret(lhs.number() % rhs.number()); }