From 5d6fee2585e3015d05fa971a4d76ffe60455a01b Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 21 Apr 2023 15:00:37 +0100 Subject: [PATCH] refactor: `quantity` refactored + `make_quantity()` added --- src/core/include/mp_units/quantity.h | 174 ++++++++++++++------------ src/core/include/mp_units/reference.h | 14 ++- src/utility/include/mp_units/math.h | 44 ++++--- 3 files changed, 126 insertions(+), 106 deletions(-) diff --git a/src/core/include/mp_units/quantity.h b/src/core/include/mp_units/quantity.h index 22434f24..ee6dabf4 100644 --- a/src/core/include/mp_units/quantity.h +++ b/src/core/include/mp_units/quantity.h @@ -64,8 +64,23 @@ concept QuantityConvertibleTo = // exposition only (!treat_as_floating_point && Harmonic)); template -concept InvokeResultIsRepresentationOf = - std::regular_invocable && RepresentationOf, Ch>; +concept InvokeResultOf = std::regular_invocable && RepresentationOf, Ch>; + +template +concept InvocableQuantities = + Quantity && Quantity && + InvokeResultOf && + requires { common_reference(Q1::reference, Q2::reference); } && + std::constructible_from>, Q1> && + std::constructible_from>, Q2>; + +template + requires detail::InvocableQuantities +using common_quantity_for = quantity>; } // namespace detail @@ -150,7 +165,7 @@ public: requires detail::QuantityConvertibleTo{}, Rep>> [[nodiscard]] constexpr quantity<::mp_units::reference{}, Rep> operator[](U) const { - return quantity<::mp_units::reference{}, Rep>{*this}; + return quantity{*this}; } // member unary operators @@ -161,13 +176,17 @@ public: } -> std::common_with; } { - return +number() * reference; + return make_quantity(+number()); } [[nodiscard]] constexpr Quantity auto operator-() const - requires std::regular_invocable, rep> + requires requires(rep v) { + { + -v + } -> std::common_with; + } { - return -number() * reference; + return make_quantity(-number()); } constexpr quantity& operator++() @@ -181,14 +200,14 @@ public: return *this; } - [[nodiscard]] constexpr quantity operator++(int) + [[nodiscard]] constexpr Quantity auto operator++(int) requires requires(rep v) { { v++ - } -> std::same_as; + } -> std::common_with; } { - return quantity(number_++); + return make_quantity(number_++); } constexpr quantity& operator--() @@ -202,14 +221,14 @@ public: return *this; } - [[nodiscard]] constexpr quantity operator--(int) + [[nodiscard]] constexpr Quantity auto operator--(int) requires requires(rep v) { { v-- - } -> std::same_as; + } -> std::common_with; } { - return quantity(number_--); + return make_quantity(number_--); } constexpr quantity& operator+=(const quantity& q) @@ -234,15 +253,15 @@ public: return *this; } - template - requires(!Quantity) && requires(rep a, const Rep2 b) { + template + requires(!Quantity) && requires(rep a, const Value b) { { a *= b } -> std::same_as; } - constexpr quantity& operator*=(const Rep2& rhs) + constexpr quantity& operator*=(const Value& v) { - number_ *= rhs; + number_ *= v; return *this; } @@ -258,16 +277,16 @@ public: return *this; } - template - requires(!Quantity) && requires(rep a, const Rep2 b) { + template + requires(!Quantity) && requires(rep a, const Value b) { { a /= b } -> std::same_as; } - constexpr quantity& operator/=(const Rep2& rhs) + constexpr quantity& operator/=(const Value& v) { - gsl_ExpectsAudit(rhs != quantity_values::zero()); - number_ /= rhs; + gsl_ExpectsAudit(v != quantity_values::zero()); + number_ /= v; return *this; } @@ -299,93 +318,79 @@ public: // Hidden Friends // Below friend functions are to be found via argument-dependent lookup only template - requires requires { common_reference(reference, Q::reference); } && - detail::InvokeResultIsRepresentationOf, rep, typename Q::rep> + requires detail::InvocableQuantities, quantity, Q> [[nodiscard]] friend constexpr Quantity auto operator+(const quantity& lhs, const Q& rhs) { - constexpr auto ref = common_reference(reference, Q::reference); - using ret = quantity; - return (ret(lhs).number() + ret(rhs).number()) * ref; + using ret = detail::common_quantity_for, quantity, Q>; + return make_quantity(ret(lhs).number() + ret(rhs).number()); } template - requires requires { common_reference(reference, Q::reference); } && - detail::InvokeResultIsRepresentationOf, rep, typename Q::rep> + requires detail::InvocableQuantities, quantity, Q> [[nodiscard]] friend constexpr Quantity auto operator-(const quantity& lhs, const Q& rhs) { - constexpr auto ref = common_reference(reference, Q::reference); - using ret = quantity; - return (ret(lhs).number() - ret(rhs).number()) * ref; + using ret = detail::common_quantity_for, quantity, Q>; + return make_quantity(ret(lhs).number() - ret(rhs).number()); } template - requires detail::InvokeResultIsRepresentationOf<(quantity_spec * Q::quantity_spec).character, std::multiplies<>, - rep, typename Q::rep> + requires detail::InvokeResultOf<(quantity_spec * Q::quantity_spec).character, std::multiplies<>, rep, + typename Q::rep> [[nodiscard]] friend constexpr Quantity auto operator*(const quantity& lhs, const Q& rhs) { - return lhs.number() * rhs.number() * (reference * Q::reference); + return make_quantity(lhs.number() * rhs.number()); } template - requires(!Quantity) && - detail::InvokeResultIsRepresentationOf, rep, const Value&> + requires(!Quantity) && detail::InvokeResultOf, rep, const Value&> [[nodiscard]] friend constexpr Quantity auto operator*(const quantity& q, const Value& v) { - return q.number() * v * reference; + return make_quantity(q.number() * v); } template - requires(!Quantity) && - detail::InvokeResultIsRepresentationOf, const Value&, rep> + requires(!Quantity) && detail::InvokeResultOf, const Value&, rep> [[nodiscard]] friend constexpr Quantity auto operator*(const Value& v, const quantity& q) { - return v * q.number() * reference; + return make_quantity(v * q.number()); } template - requires detail::InvokeResultIsRepresentationOf<(quantity_spec / Q::quantity_spec).character, std::divides<>, rep, - typename Q::rep> + requires detail::InvokeResultOf<(quantity_spec / Q::quantity_spec).character, std::divides<>, rep, typename Q::rep> [[nodiscard]] friend constexpr Quantity auto operator/(const quantity& lhs, const Q& rhs) { gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); - return lhs.number() / rhs.number() * (reference / Q::reference); + return make_quantity(lhs.number() / rhs.number()); } template - requires(!Quantity) && - detail::InvokeResultIsRepresentationOf, rep, const Value&> + requires(!Quantity) && detail::InvokeResultOf, rep, const Value&> [[nodiscard]] friend constexpr Quantity auto operator/(const quantity& q, const Value& v) { gsl_ExpectsAudit(v != quantity_values::zero()); - return q.number() / v * reference; + return make_quantity(q.number() / v); } template - requires(!Quantity) && - detail::InvokeResultIsRepresentationOf, const Value&, rep> + requires(!Quantity) && detail::InvokeResultOf, const Value&, rep> [[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q) { - return v / q.number() * (::mp_units::one / reference); + return make_quantity<::mp_units::one / reference>(v / q.number()); } template - requires(!treat_as_floating_point) && (!treat_as_floating_point) && - requires { - common_reference(reference, Q::reference); - } && detail::InvokeResultIsRepresentationOf, rep, typename Q::rep> + requires(!treat_as_floating_point) && + (!treat_as_floating_point) && detail::InvocableQuantities, quantity, Q> [[nodiscard]] friend constexpr Quantity auto operator%(const quantity& lhs, const Q& rhs) { gsl_ExpectsAudit(rhs.number() != quantity_values::zero()); - constexpr auto ref = common_reference(reference, Q::reference); - using ret = quantity; - return (ret(lhs).number() % ret(rhs).number()) * ref; + using ret = detail::common_quantity_for, quantity, Q>; + return make_quantity(ret(lhs).number() % ret(rhs).number()); } template - requires requires { common_reference(reference, Q::reference); } && - std::equality_comparable_with + requires requires { typename std::common_type_t; } && + std::equality_comparable::rep> [[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const Q& rhs) { using ct = std::common_type_t; @@ -393,8 +398,8 @@ public: } template - requires requires { common_reference(reference, Q::reference); } && - std::three_way_comparable_with + requires requires { typename std::common_type_t; } && + std::three_way_comparable::rep> [[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const Q& rhs) { using ct = std::common_type_t; @@ -411,65 +416,65 @@ public: } template - requires(!Quantity) && (dimension == dimension_one) && - detail::InvokeResultIsRepresentationOf, rep, Value> + requires(!Quantity) && + (dimension == dimension_one) && detail::InvokeResultOf, rep, Value> [[nodiscard]] friend constexpr Quantity auto operator+(const quantity& q, const Value& v) { - return q + v * ::mp_units::one; + return q + make_quantity<::mp_units::one>(v); } template - requires(!Quantity) && (dimension == dimension_one) && - detail::InvokeResultIsRepresentationOf, Value, rep> + requires(!Quantity) && + (dimension == dimension_one) && detail::InvokeResultOf, Value, rep> [[nodiscard]] friend constexpr Quantity auto operator+(const Value& v, const quantity& q) { - return v * ::mp_units::one + q; + return make_quantity<::mp_units::one>(v) + q; } template - requires(!Quantity) && (dimension == dimension_one) && - detail::InvokeResultIsRepresentationOf, rep, Value> + requires(!Quantity) && + (dimension == dimension_one) && detail::InvokeResultOf, rep, Value> [[nodiscard]] friend constexpr Quantity auto operator-(const quantity& q, const Value& v) { - return q - v * ::mp_units::one; + return q - make_quantity<::mp_units::one>(v); } template - requires(!Quantity) && (dimension == dimension_one) && - detail::InvokeResultIsRepresentationOf, Value, rep> + requires(!Quantity) && + (dimension == dimension_one) && detail::InvokeResultOf, Value, rep> [[nodiscard]] friend constexpr Quantity auto operator-(const Value& v, const quantity& q) { - return v * ::mp_units::one - q; + return make_quantity<::mp_units::one>(v) - q; } template requires(!Quantity) && (dimension == dimension_one) && (unit == ::mp_units::one) && (!treat_as_floating_point) && (!treat_as_floating_point) && - detail::InvokeResultIsRepresentationOf, Value, rep> + detail::InvokeResultOf, Value, rep> [[nodiscard]] friend constexpr Quantity auto operator%(const Value& v, const quantity& q) { gsl_ExpectsAudit(q.number() != quantity_values::zero()); - return (v % q.number()) * reference; + return make_quantity(v % q.number()); } template requires(!Quantity) && (dimension == dimension_one) && std::equality_comparable_with [[nodiscard]] friend constexpr bool operator==(const quantity& q, const Value& v) { - return q == v * ::mp_units::one; + return q == make_quantity<::mp_units::one>(v); } template requires(!Quantity) && (dimension == dimension_one) && std::three_way_comparable_with [[nodiscard]] friend constexpr auto operator<=>(const quantity& q, const Value& v) { - return q <=> v * ::mp_units::one; + return q <=> make_quantity<::mp_units::one>(v); } private: - template - requires RepresentationOf, get_quantity_spec(R2{}).character> - friend constexpr quantity> operator*(Rep2&& lhs, R2); + template + requires RepresentationOf, get_quantity_spec(R2).character> + friend constexpr quantity> make_quantity(Rep2&& v); template requires detail::RepSafeConstructibleFrom> @@ -482,6 +487,13 @@ private: template explicit quantity(Q) -> quantity::reference, typename quantity_like_traits::rep>; +template + requires RepresentationOf, get_quantity_spec(R).character> +[[nodiscard]] constexpr quantity> make_quantity(Rep&& v) +{ + return quantity>(std::forward(v)); +} + } // namespace mp_units namespace std { diff --git a/src/core/include/mp_units/reference.h b/src/core/include/mp_units/reference.h index 9de50612..5573c686 100644 --- a/src/core/include/mp_units/reference.h +++ b/src/core/include/mp_units/reference.h @@ -48,9 +48,6 @@ template return U; } -template Rep> -class quantity; - /** * @brief Quantity reference type * @@ -138,11 +135,18 @@ struct reference { } }; -template +template Rep> +class quantity; + +template + requires RepresentationOf, get_quantity_spec(R).character> +[[nodiscard]] constexpr quantity> make_quantity(Rep&& v); + +template requires RepresentationOf, get_quantity_spec(R{}).character> [[nodiscard]] constexpr quantity> operator*(Rep&& lhs, R) { - return quantity>(std::forward(lhs)); + return make_quantity(std::forward(lhs)); } void /*Use `q * (1 * r)` rather than `q * r`.*/ operator*(Quantity auto, Reference auto) = delete; diff --git a/src/utility/include/mp_units/math.h b/src/utility/include/mp_units/math.h index 4cb24dc3..8967e2cc 100644 --- a/src/utility/include/mp_units/math.h +++ b/src/utility/include/mp_units/math.h @@ -61,8 +61,8 @@ template return q; } else { using std::pow; - return static_cast(pow(q.number(), static_cast(Num) / static_cast(Den))) * - reference(Q::quantity_spec), pow(Q::unit)>{}; + return make_quantity(Q::quantity_spec), pow(Q::unit)>{}>( + static_cast(pow(q.number(), static_cast(Num) / static_cast(Den)))); } } @@ -80,7 +80,8 @@ template { using rep = TYPENAME Q::rep; using std::sqrt; - return static_cast(sqrt(q.number())) * reference(Q::quantity_spec), pow<1, 2>(Q::unit)>{}; + return make_quantity(Q::quantity_spec), pow<1, 2>(Q::unit)>{}>( + static_cast(sqrt(q.number()))); } /** @@ -97,7 +98,8 @@ template { using rep = TYPENAME Q::rep; using std::cbrt; - return static_cast(cbrt(q.number())) * reference(Q::quantity_spec), pow<1, 3>(Q::unit)>{}; + return make_quantity(Q::quantity_spec), pow<1, 3>(Q::unit)>{}>( + static_cast(cbrt(q.number()))); } /** @@ -113,7 +115,7 @@ template Q> requires requires { exp(q.number()); } || requires { std::exp(q.number()); } { using std::exp; - return value_cast(exp(value_cast(q).number()) * dimensionless[one]); + return value_cast(make_quantity(exp(value_cast(q).number()))); } /** @@ -127,7 +129,7 @@ template requires requires { abs(q.number()); } || requires { std::abs(q.number()); } { using std::abs; - return abs(q.number()) * Q::reference; + return make_quantity(abs(q.number())); } /** @@ -142,7 +144,7 @@ template requires requires { std::numeric_limits::epsilon(); } [[nodiscard]] constexpr Quantity auto epsilon(R r) noexcept { - return std::numeric_limits::epsilon() * r; + return make_quantity(std::numeric_limits::epsilon()); } /** @@ -169,9 +171,10 @@ template if constexpr (treat_as_floating_point) { using std::floor; if constexpr (To == get_unit(R)) { - return floor(q.number()) * reference{}; + return make_quantity{}>(floor(q.number())); } else { - return handle_signed_results(floor(value_cast(q).number()) * reference{}); + return handle_signed_results( + make_quantity{}>(floor(value_cast(q).number()))); } } else { if constexpr (To == get_unit(R)) { @@ -205,9 +208,10 @@ template if constexpr (treat_as_floating_point) { using std::ceil; if constexpr (To == get_unit(R)) { - return ceil(q.number()) * reference{}; + return make_quantity{}>(ceil(q.number())); } else { - return handle_signed_results(ceil(value_cast(q).number()) * reference{}); + return handle_signed_results( + make_quantity{}>(ceil(value_cast(q).number()))); } } else { if constexpr (To == get_unit(R)) { @@ -238,7 +242,7 @@ template if constexpr (To == get_unit(R)) { if constexpr (treat_as_floating_point) { using std::round; - return round(q.number()) * reference{}; + return make_quantity{}>(round(q.number())); } else { return value_cast(q); } @@ -273,7 +277,7 @@ template { using std::hypot; using type = quantity; - return hypot(type{x}.number(), type{y}.number()) * type::reference; + return make_quantity(hypot(type{x}.number(), type{y}.number())); } /** @@ -291,7 +295,7 @@ hypot(const Q1& x, const Q2& y, const Q3& z) noexcept using std::hypot; using type = quantity; - return hypot(type{x}.number(), type{y}.number(), type{z}.number()) * type::reference; + return make_quantity(hypot(type{x}.number(), type{y}.number(), type{z}.number())); } namespace isq { @@ -302,7 +306,7 @@ template Q> requires requires { sin(q.number()); } || requires { std::sin(q.number()); } { using std::sin; - return sin(q[si::radian].number()) * one; + return make_quantity(sin(q[si::radian].number())); } template Q> @@ -311,7 +315,7 @@ template Q> requires requires { cos(q.number()); } || requires { std::cos(q.number()); } { using std::cos; - return cos(q[si::radian].number()) * one; + return make_quantity(cos(q[si::radian].number())); } template Q> @@ -320,7 +324,7 @@ template Q> requires requires { tan(q.number()); } || requires { std::tan(q.number()); } { using std::tan; - return tan(q[si::radian].number()) * one; + return make_quantity(tan(q[si::radian].number())); } template Q> @@ -329,7 +333,7 @@ template Q> requires requires { asin(q.number()); } || requires { std::asin(q.number()); } { using std::asin; - return asin(value_cast(q).number()) * angular_measure[si::radian]; + return make_quantity(asin(value_cast(q).number())); } template Q> @@ -338,7 +342,7 @@ template Q> requires requires { acos(q.number()); } || requires { std::acos(q.number()); } { using std::acos; - return acos(value_cast(q).number()) * angular_measure[si::radian]; + return make_quantity(acos(value_cast(q).number())); } template Q> @@ -347,7 +351,7 @@ template Q> requires requires { atan(q.number()); } || requires { std::atan(q.number()); } { using std::atan; - return atan(value_cast(q).number()) * angular_measure[si::radian]; + return make_quantity(atan(value_cast(q).number())); } } // namespace isq