diff --git a/src/core/include/mp-units/bits/quantity_point_concepts.h b/src/core/include/mp-units/bits/quantity_point_concepts.h index 5de5c944..0b8af05f 100644 --- a/src/core/include/mp-units/bits/quantity_point_concepts.h +++ b/src/core/include/mp-units/bits/quantity_point_concepts.h @@ -116,8 +116,34 @@ template requires is_derived_from_specialization_of_quantity_point inline constexpr bool is_quantity_point = true; +// the below was introduced to workaround gcc-12 bug that produced an error +// "error: ‘const struct mp_units::isq::time’ has no member named ‘absolute_point_origin’" +template +constexpr bool same_absolute_point_origins_lazy(PO1, PO2) +{ + if constexpr (is_derived_from_specialization_of_relative_point_origin && + is_derived_from_specialization_of_relative_point_origin) + return std::same_as, + std::remove_const_t>; + else if constexpr (is_derived_from_specialization_of_relative_point_origin) + return std::same_as, PO2>; + else if constexpr (is_derived_from_specialization_of_relative_point_origin) + return std::same_as>; + else + return false; +} + } // namespace detail +template +concept PointOriginOf = + PointOrigin && PointOrigin> && + (std::same_as> || detail::same_absolute_point_origins_lazy(PO1{}, PO2) || + (detail::is_specialization_of_absolute_point_origin && + detail::is_specialization_of_absolute_point_origin> && + implicitly_convertible(PO1::quantity_spec, PO2.quantity_spec) && + !detail::NestedQuantityKindSpecOf)); + /** * @brief A concept matching all quantity points with provided dimension or quantity spec * @@ -127,15 +153,8 @@ inline constexpr bool is_quantity_point = true; */ template concept QuantityPointOf = - QuantityPoint && - (ReferenceOf, V> || - (PointOrigin> && - (std::same_as, std::remove_const_t> || - std::same_as, std::remove_const_t> || - (detail::is_specialization_of_absolute_point_origin> && - detail::is_specialization_of_absolute_point_origin> && - implicitly_convertible(QP::absolute_point_origin.quantity_spec, V.quantity_spec) && - !detail::DerivedFromQuantityKindSpecOf)))); + QuantityPoint && (ReferenceOf, V> || + PointOriginOf, V>); /** * @brief A concept matching all external quantity point like types diff --git a/src/core/include/mp-units/quantity_point.h b/src/core/include/mp-units/quantity_point.h index fb906dda..0e801d2d 100644 --- a/src/core/include/mp-units/quantity_point.h +++ b/src/core/include/mp-units/quantity_point.h @@ -37,7 +37,9 @@ struct absolute_point_origin { template struct relative_point_origin { static constexpr QuantityPoint auto quantity_point = QP; - static constexpr QuantitySpec auto quantity_spec = QP.quantity_spec; + static constexpr QuantitySpec auto quantity_spec = + common_quantity_spec(QP.quantity_spec, QP.point_origin.quantity_spec); + static constexpr PointOrigin auto absolute_point_origin = QP.absolute_point_origin; }; namespace detail { @@ -114,16 +116,17 @@ public: template QP> requires std::constructible_from + // TODO add perfect forwarding constexpr explicit(!std::convertible_to) quantity_point(const QP& qp) : q_([&] { if constexpr (is_same_v, std::remove_const_t>) { return qp.relative(); - } else if constexpr (detail::is_derived_from_specialization_of_relative_point_origin< + } else if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin< std::remove_const_t>) { - return qp.absolute() - zero().absolute(); - } else { return qp.absolute(); + } else { + return qp.absolute() - zero().absolute(); } }()) { @@ -146,11 +149,11 @@ public: { if constexpr (is_same_v>) { return *this; - } else if constexpr (detail::is_derived_from_specialization_of_relative_point_origin) { - auto q = absolute() - origin.quantity_point.absolute(); + } else if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin) { + auto q = absolute(); return quantity_point(std::move(q)); } else { - auto q = absolute(); + auto q = absolute() - origin.quantity_point.absolute(); return quantity_point(std::move(q)); } } @@ -242,35 +245,57 @@ explicit quantity_point(QP) template // TODO simplify when gcc catches up requires ReferenceOf, get_quantity_spec(R1)> -[[nodiscard]] constexpr QuantityPoint auto operator+(const quantity_point& lhs, - const quantity& rhs) - requires requires { lhs.relative() + rhs; } +[[nodiscard]] constexpr QuantityPoint auto operator+(const quantity_point& qp, + const quantity& q) + requires requires { qp.relative() + q; } { - const auto q = lhs.relative() + rhs; - using q_type = decltype(q); - return quantity_point(q); + auto temp = qp.relative() + q; + using q_type = decltype(temp); + return quantity_point(std::move(temp)); } template // TODO simplify when gcc catches up requires ReferenceOf, get_quantity_spec(R2)> -[[nodiscard]] constexpr QuantityPoint auto operator+(const quantity& lhs, - const quantity_point& rhs) - requires requires { rhs + lhs; } +[[nodiscard]] constexpr QuantityPoint auto operator+(const quantity& q, + const quantity_point& qp) + requires requires { q + qp.relative(); } { - return rhs + lhs; + return qp + q; +} + +template + requires ReferenceOf, PO::quantity_spec> +[[nodiscard]] constexpr quantity_point operator+(PO, Q&& q) +{ + return quantity_point(std::forward(q)); +} + +template + requires ReferenceOf, PO::quantity_spec> +[[nodiscard]] constexpr quantity_point operator+(Q&& q, PO po) +{ + return po + std::forward(q); } template // TODO simplify when gcc catches up requires ReferenceOf, get_quantity_spec(R1)> -[[nodiscard]] constexpr QuantityPoint auto operator-(const quantity_point& lhs, - const quantity& rhs) - requires requires { lhs.relative() - rhs; } +[[nodiscard]] constexpr QuantityPoint auto operator-(const quantity_point& qp, + const quantity& q) + requires requires { qp.relative() - q; } { - const auto q = lhs.relative() - rhs; - using q_type = decltype(q); - return quantity_point(q); + const auto temp = qp.relative() - q; + using q_type = decltype(temp); + return quantity_point(std::move(temp)); +} + +template + requires ReferenceOf, PO::quantity_spec> +[[nodiscard]] constexpr QuantityPoint auto operator-(PO po, const Q& q) + requires requires { -q; } +{ + return po + (-q); } template QP2> @@ -278,12 +303,55 @@ template QP2> requires requires { lhs.absolute() - rhs.absolute(); } { if constexpr (is_same_v, - std::remove_const_t>) - return lhs.relative() - rhs.relative(); - else + std::remove_const_t>) { + constexpr auto common_qs = common_quantity_spec(QP1::quantity_spec, QP1::point_origin.quantity_spec, + QP2::quantity_spec, QP2::point_origin.quantity_spec); + return quantity_cast(lhs.relative() - rhs.relative()); + } else return lhs.absolute() - rhs.absolute(); } +template QP> + requires ReferenceOf, PO::quantity_spec> +[[nodiscard]] constexpr Quantity auto operator-(const QP& qp, PO) +{ + constexpr auto common_qs = common_quantity_spec(PO::quantity_spec, QP::quantity_spec, QP::point_origin.quantity_spec); + if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin) { + if constexpr (is_same_v, std::remove_const_t>) + return quantity_cast(qp.relative()); + else + return quantity_cast(qp.absolute()); + } else { + if constexpr (is_same_v, std::remove_const_t>) + return quantity_cast(qp.relative()); + else + return quantity_cast(qp.absolute() - PO::quantity_point.absolute()); + } +} + +template QP> + requires ReferenceOf, PO::quantity_spec> +[[nodiscard]] constexpr Quantity auto operator-(PO po, const QP& qp) +{ + return -(qp - po); +} + +template PO2> + requires QuantitySpecOf, PO2::quantity_spec> && + (detail::is_derived_from_specialization_of_relative_point_origin || + detail::is_derived_from_specialization_of_relative_point_origin) +[[nodiscard]] constexpr Quantity auto operator-(PO1 po1, PO2 po2) +{ + constexpr auto common_qs = common_quantity_spec(PO1::quantity_spec, PO2::quantity_spec); + if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin) { + return quantity_cast(-po2.quantity_point.absolute()); + } else if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin) { + return quantity_cast(po1.quantity_point.absolute()); + } else { + return quantity_cast(po1.quantity_point - po2.quantity_point); + } +} + template QP2> requires std::three_way_comparable_with [[nodiscard]] constexpr auto operator<=>(const QP1& lhs, const QP2& rhs) diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index a4016307..87f0b388 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -420,6 +420,8 @@ static_assert(quantity_point(42 * m).point_from(towe static_assert(quantity_point(42 * m).point_from(tower_peak).relative() == -42 * m); static_assert(quantity_point(84 * m).point_from(tower_peak).relative() == 42 * m); +static_assert(is_of_type(42 * m).point_from(mean_sea_level), + quantity_point>); /////////////////////////////////// // obtaining an absolute quantity @@ -593,7 +595,7 @@ static_assert(invalid_compound_assignments); // binary operators //////////////////// -template typename QP> +template typename QP, auto Origin> concept invalid_binary_operations = requires { // can't add two quantity points requires !requires { QP(1 * m) + QP(1 * m); }; @@ -601,24 +603,34 @@ concept invalid_binary_operations = requires { // can't add more generic quantity (violates point_origin quantity_spec) requires !requires { QP(1 * m) + isq::length(1 * m); }; requires !requires { isq::length(1 * m) + QP(1 * m); }; + requires !requires { Origin + isq::length(1 * m); }; + requires !requires { isq::length(1 * m) + Origin; }; // can't subtract more generic quantity (violates point_origin quantity_spec) requires !requires { QP(1 * m) - isq::length(1 * m); }; + requires !requires { Origin - isq::length(1 * m); }; // quantity point can't be subtracted from a quantity requires !requires { 1 * m - QP(1 * m); }; + requires !requires { 1 * m - Origin; }; // no crossdimensional addition and subtraction requires !requires { QP(1 * m) + 1 * s; }; requires !requires { QP(1 * m) - 1 * s; }; + requires !requires { Origin + 1 * s; }; + requires !requires { Origin - 1 * s; }; // unit constants requires !requires { QP(1) + m; }; requires !requires { QP(1) - m; }; + requires !requires { Origin + m; }; + requires !requires { Origin - m; }; requires !requires { m + QP(1); }; requires !requires { m - QP(1); }; + requires !requires { m + Origin; }; + requires !requires { m - Origin; }; }; -static_assert(invalid_binary_operations); +static_assert(invalid_binary_operations); template typename QP> concept invalid_binary_operations_with_origins = requires { @@ -638,6 +650,20 @@ concept invalid_binary_operations_with_origins = requires { }; static_assert(invalid_binary_operations_with_origins); +template +concept invalid_subtraction_of_point_origins = requires { + requires !requires { QP1 - QP2; }; + requires !requires { QP2 - QP1; }; +}; + +// cant'subtract two unrelated quantity points +static_assert(invalid_subtraction_of_point_origins{}>); + +// cant'subtract two absolute quantity points as we do not know the unit for the resulting quantity +static_assert( + invalid_subtraction_of_point_origins{}, absolute_point_origin{}>); +static_assert(invalid_subtraction_of_point_origins); + // same representation type static_assert(is_of_type>{}, int>>); @@ -695,6 +721,58 @@ static_assert(is_of_type{}, int>>); +static_assert(is_of_type{} + 1 * m, + quantity_point{}, int>>); +static_assert(is_of_type{} + 1 * km, + quantity_point, absolute_point_origin{}, int>>); +static_assert(is_of_type{} + isq::length(1 * m), + quantity_point{}, int>>); +static_assert(is_of_type{} + isq::distance(1 * m), + quantity_point{}, int>>); +static_assert( + is_of_type{} + isq::distance(1 * km), + quantity_point], absolute_point_origin{}, int>>); +static_assert(is_of_type<1 * m + absolute_point_origin{}, + quantity_point{}, int>>); +static_assert(is_of_type<1 * km + absolute_point_origin{}, + quantity_point, absolute_point_origin{}, int>>); +static_assert(is_of_type{}, + quantity_point{}, int>>); +static_assert(is_of_type{}, + quantity_point{}, int>>); +static_assert( + is_of_type{}, + quantity_point], absolute_point_origin{}, int>>); + +static_assert(is_of_type>{} + 1 * m, + quantity_point>{}, int>>); +static_assert(is_of_type>{} + 1 * km, + quantity_point, absolute_point_origin>{}, int>>); +static_assert(is_of_type>{} + isq::distance(1 * m), + quantity_point>{}, int>>); +static_assert( + is_of_type>{} + isq::distance(1 * km), + quantity_point], absolute_point_origin>{}, int>>); +static_assert(is_of_type<1 * m + absolute_point_origin>{}, + quantity_point>{}, int>>); +static_assert(is_of_type<1 * km + absolute_point_origin>{}, + quantity_point, absolute_point_origin>{}, int>>); +static_assert(is_of_type>{}, + quantity_point>{}, int>>); +static_assert( + is_of_type>{}, + quantity_point], absolute_point_origin>{}, int>>); + +static_assert(is_of_type>); +static_assert(is_of_type, mean_sea_level, int>>); +static_assert(is_of_type<1 * m + mean_sea_level, quantity_point>); +static_assert(is_of_type<1 * km + mean_sea_level, quantity_point, mean_sea_level, int>>); + +static_assert(is_of_type>); +static_assert(is_of_type, ground_level, int>>); +static_assert(is_of_type<1 * m + ground_level, quantity_point>); +static_assert(is_of_type<1 * km + ground_level, quantity_point, ground_level, int>>); + static_assert(is_of_type>{}, int>>); static_assert(is_of_type{}, int>>); +static_assert(is_of_type{} - 1 * m, + quantity_point{}, int>>); +static_assert(is_of_type{} - 1 * km, + quantity_point, absolute_point_origin{}, int>>); +static_assert(is_of_type{} - isq::distance(1 * m), + quantity_point{}, int>>); +static_assert( + is_of_type{} - isq::distance(1 * km), + quantity_point], absolute_point_origin{}, int>>); + +static_assert(is_of_type>{} - 1 * m, + quantity_point>{}, int>>); +static_assert(is_of_type>{} - 1 * km, + quantity_point, absolute_point_origin>{}, int>>); +static_assert(is_of_type>{} - isq::distance(1 * m), + quantity_point>{}, int>>); +static_assert( + is_of_type>{} - isq::distance(1 * km), + quantity_point], absolute_point_origin>{}, int>>); + +static_assert(is_of_type>); +static_assert(is_of_type, mean_sea_level, int>>); + +static_assert(is_of_type>); +static_assert(is_of_type, ground_level, int>>); + static_assert(is_of_type>); static_assert(is_of_type>); static_assert(is_of_type>); @@ -742,27 +846,107 @@ static_assert( static_assert( is_of_type>); -static_assert(is_of_type(1 * m) - + quantity_point(1 * m), quantity>); -static_assert(is_of_type(1 * m) - quantity_point(1 * m), + quantity>); +static_assert( + is_of_type(1 * m) - quantity_point(1 * m), + quantity>); +static_assert(is_of_type(1 * m) - + quantity_point(1 * m), quantity>); -static_assert(is_of_type(1 * m) - + quantity_point(1 * m), quantity>); -static_assert(is_of_type(1 * m) - quantity_point(1 * m), + quantity>); +static_assert( + is_of_type(1 * m) - quantity_point(1 * m), + quantity>); +static_assert( + is_of_type(1 * m) - quantity_point(1 * m), + quantity>); +static_assert( + is_of_type(1 * m) - quantity_point(1 * m), + quantity>); +static_assert(is_of_type(1 * m) - + quantity_point(1 * m), quantity>); -static_assert(is_of_type(1 * m) - + quantity_point(1 * m), quantity>); -static_assert(is_of_type(1 * m) - + quantity_point(1 * m), quantity>); -static_assert(is_of_type>); -static_assert(is_of_type>); -static_assert(is_of_type>); -static_assert(is_of_type(1 * m) - + quantity_point(1 * m), quantity>); +static_assert(is_of_type{} - quantity_point(1 * m), quantity>); +static_assert( + is_of_type{} - quantity_point(1 * km), quantity>); +static_assert( + is_of_type{} - quantity_point(isq::height(1 * m)), quantity>); +static_assert(is_of_type{} - quantity_point(isq::height(1 * km)), + quantity>); + +static_assert( + is_of_type>{} - quantity_point(1 * m), quantity>); +static_assert(is_of_type>{} - quantity_point(1 * km), + quantity, int>>); +static_assert(is_of_type>{} - quantity_point(isq::height(1 * m)), + quantity>); +static_assert(is_of_type>{} - quantity_point(isq::height(1 * km)), + quantity>); + +static_assert(is_of_type>); +static_assert(is_of_type>); +static_assert(is_of_type>); +static_assert(is_of_type>); + +static_assert(is_of_type>); +static_assert(is_of_type>); +static_assert(is_of_type>); +static_assert(is_of_type>); + +static_assert(is_of_type>); +static_assert(is_of_type>); +static_assert(is_of_type>); +static_assert(is_of_type>); + +static_assert(is_of_type>); +static_assert(is_of_type>); +static_assert(is_of_type>); +static_assert(is_of_type>); + +static_assert(is_of_type>); +static_assert(is_of_type>); +static_assert(is_of_type>); +static_assert(is_of_type>); +static_assert(is_of_type>); + +static_assert( + is_of_type>); +static_assert( + is_of_type>); +static_assert( + is_of_type>); + +static_assert(is_of_type<(1 * m + mean_sea_level) - (1 * m + ground_level), quantity>); +static_assert(is_of_type<(1 * m + ground_level) - (1 * m + mean_sea_level), quantity>); +static_assert(is_of_type<(1 * m + tower_peak) - (1 * m + ground_level), quantity>); +static_assert(is_of_type<(1 * m + ground_level) - (1 * m + tower_peak), quantity>); +static_assert(is_of_type<(1 * m + tower_peak) - (1 * m + mean_sea_level), quantity>); +static_assert(is_of_type<(1 * m + mean_sea_level) - (1 * m + tower_peak), quantity>); +static_assert(is_of_type<(1 * m + other_ground_level) - (1 * m + ground_level), quantity>); +static_assert(is_of_type<(1 * m + ground_level) - (1 * m + other_ground_level), quantity>); +static_assert(is_of_type<(1 * m + other_ground_level) - (1 * m + tower_peak), quantity>); +static_assert(is_of_type<(1 * m + tower_peak) - (1 * m + other_ground_level), quantity>); + // check for integral types promotion static_assert(