mirror of
https://github.com/mpusz/mp-units.git
synced 2025-06-25 01:01:33 +02:00
feat: might_store_converted_common_value
added to detect conversions overflowing rep
Resolves #670, resolves #671, resolves #658, resolves #303
This commit is contained in:
@ -35,6 +35,7 @@
|
||||
#include <mp-units/framework/reference_concepts.h>
|
||||
#include <mp-units/framework/representation_concepts.h>
|
||||
#include <mp-units/framework/unit_concepts.h>
|
||||
#include <mp-units/framework/value_cast.h>
|
||||
|
||||
#ifndef MP_UNITS_IN_MODULE_INTERFACE
|
||||
#include <mp-units/ext/contracts.h>
|
||||
@ -67,7 +68,8 @@ concept ValuePreservingTo = Representation<std::remove_cvref_t<FromRep>> && Repr
|
||||
Unit<MP_UNITS_REMOVE_CONST(decltype(FromUnit))> &&
|
||||
Unit<MP_UNITS_REMOVE_CONST(decltype(ToUnit))> && std::assignable_from<ToRep&, FromRep> &&
|
||||
(IsFloatingPoint<ToRep> || (!IsFloatingPoint<std::remove_cvref_t<FromRep>> &&
|
||||
(integral_conversion_factor(FromUnit, ToUnit))));
|
||||
integral_conversion_factor(FromUnit, ToUnit) &&
|
||||
might_store_converted_value<ToRep>(FromUnit, ToUnit)));
|
||||
|
||||
template<typename QFrom, typename QTo>
|
||||
concept QuantityConvertibleTo =
|
||||
@ -100,12 +102,21 @@ template<typename Func, Quantity Q1, Quantity Q2>
|
||||
using common_quantity_for = quantity<get_common_reference(Q1::reference, Q2::reference),
|
||||
std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>;
|
||||
|
||||
template<Representation Rep, Unit U1, Unit U2>
|
||||
[[nodiscard]] consteval bool might_store_converted_common_value(U1 u1, U2 u2)
|
||||
{
|
||||
constexpr Unit auto cu = get_common_unit(u1, u2);
|
||||
return might_store_converted_value<Rep>(u1, cu) && might_store_converted_value<Rep>(u2, cu);
|
||||
}
|
||||
|
||||
template<typename Func, typename Q1, typename Q2>
|
||||
concept CommonlyInvocableQuantities =
|
||||
Quantity<Q1> && Quantity<Q2> && HaveCommonReference<Q1::reference, Q2::reference> &&
|
||||
std::convertible_to<Q1, common_quantity_for<Func, Q1, Q2>> &&
|
||||
std::convertible_to<Q2, common_quantity_for<Func, Q1, Q2>> &&
|
||||
InvocableQuantities<Func, Q1, Q2, get_common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec)>;
|
||||
InvocableQuantities<Func, Q1, Q2, get_common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec)> &&
|
||||
might_store_converted_common_value<std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>(Q1::unit,
|
||||
Q2::unit);
|
||||
|
||||
template<auto R1, auto R2, typename Rep1, typename Rep2>
|
||||
concept SameValueAs = (equivalent(get_unit(R1), get_unit(R2))) && std::convertible_to<Rep1, Rep2>;
|
||||
@ -639,6 +650,8 @@ template<mp_units::Quantity Q1, mp_units::Quantity Q2>
|
||||
requires requires {
|
||||
{ mp_units::get_common_reference(Q1::reference, Q2::reference) } -> mp_units::Reference;
|
||||
typename std::common_type_t<typename Q1::rep, typename Q2::rep>;
|
||||
requires(
|
||||
might_store_converted_common_value<std::common_type_t<typename Q1::rep, typename Q2::rep>>(Q1::unit, Q2::unit));
|
||||
requires mp_units::RepresentationOf<std::common_type_t<typename Q1::rep, typename Q2::rep>,
|
||||
mp_units::get_common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec)>;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
// IWYU pragma: private, include <mp-units/framework.h>
|
||||
#include <mp-units/bits/module_macros.h>
|
||||
#include <mp-units/bits/sudo_cast.h>
|
||||
#include <mp-units/framework/customization_points.h>
|
||||
#include <mp-units/framework/quantity_concepts.h>
|
||||
#include <mp-units/framework/quantity_point_concepts.h>
|
||||
#include <mp-units/framework/reference.h>
|
||||
@ -34,6 +35,29 @@
|
||||
MP_UNITS_EXPORT
|
||||
namespace mp_units {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<Representation Rep, Unit UFrom, Unit UTo>
|
||||
[[nodiscard]] consteval bool might_store_converted_value(UFrom from, UTo to)
|
||||
{
|
||||
if constexpr (is_same_v<UFrom, UTo> || treat_as_floating_point<Rep>)
|
||||
return true;
|
||||
else if constexpr (std::totally_ordered_with<Rep, std::uintmax_t> &&
|
||||
requires(Rep v) { representation_values<Rep>::max(); }) {
|
||||
constexpr auto factor =
|
||||
get_value<std::uintmax_t>(numerator(get_canonical_unit(from).mag / get_canonical_unit(to).mag));
|
||||
if constexpr (std::is_integral_v<Rep>)
|
||||
return std::in_range<Rep>(factor);
|
||||
else
|
||||
return factor <= representation_values<Rep>::max();
|
||||
} else
|
||||
// if the representation is not totally ordered with std::uintmax_t or does not have max() defined
|
||||
// then we assume that it might store any value
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity's unit
|
||||
*
|
||||
@ -45,7 +69,8 @@ namespace mp_units {
|
||||
* @tparam ToU a unit to use for a target quantity
|
||||
*/
|
||||
template<auto ToU, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>>
|
||||
requires detail::UnitCompatibleWith<MP_UNITS_REMOVE_CONST(decltype(ToU)), Q::unit, Q::quantity_spec>
|
||||
requires detail::UnitCompatibleWith<MP_UNITS_REMOVE_CONST(decltype(ToU)), Q::unit, Q::quantity_spec> &&
|
||||
(detail::might_store_converted_value<typename Q::rep>(Q::unit, ToU))
|
||||
[[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q)
|
||||
{
|
||||
return detail::sudo_cast<quantity<detail::make_reference(Q::quantity_spec, ToU), typename Q::rep>>(
|
||||
@ -82,6 +107,7 @@ template<Representation ToRep, typename FwdQ, Quantity Q = std::remove_cvref_t<F
|
||||
*/
|
||||
template<Unit auto ToU, Representation ToRep, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>>
|
||||
requires detail::UnitCompatibleWith<MP_UNITS_REMOVE_CONST(decltype(ToU)), Q::unit, Q::quantity_spec> &&
|
||||
(detail::might_store_converted_value<ToRep>(Q::unit, ToU)) &&
|
||||
RepresentationOf<ToRep, Q::quantity_spec> && std::constructible_from<ToRep, typename Q::rep>
|
||||
[[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q)
|
||||
{
|
||||
@ -90,6 +116,7 @@ template<Unit auto ToU, Representation ToRep, typename FwdQ, Quantity Q = std::r
|
||||
|
||||
template<Representation ToRep, Unit auto ToU, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>>
|
||||
requires detail::UnitCompatibleWith<MP_UNITS_REMOVE_CONST(decltype(ToU)), Q::unit, Q::quantity_spec> &&
|
||||
(detail::might_store_converted_value<ToRep>(Q::unit, ToU)) &&
|
||||
RepresentationOf<ToRep, Q::quantity_spec> && std::constructible_from<ToRep, typename Q::rep>
|
||||
[[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q)
|
||||
{
|
||||
@ -113,6 +140,7 @@ template<Representation ToRep, Unit auto ToU, typename FwdQ, Quantity Q = std::r
|
||||
*/
|
||||
template<Quantity ToQ, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>>
|
||||
requires detail::UnitCompatibleWith<MP_UNITS_NONCONST_TYPE(ToQ::unit), Q::unit, Q::quantity_spec> &&
|
||||
(detail::might_store_converted_value<typename ToQ::rep>(Q::unit, ToQ::unit)) &&
|
||||
(ToQ::quantity_spec == Q::quantity_spec) && std::constructible_from<typename ToQ::rep, typename Q::rep>
|
||||
[[nodiscard]] constexpr Quantity auto value_cast(FwdQ&& q)
|
||||
{
|
||||
@ -130,7 +158,8 @@ template<Quantity ToQ, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>>
|
||||
* @tparam ToU a unit to use for a target quantity point
|
||||
*/
|
||||
template<Unit auto ToU, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>>
|
||||
requires detail::UnitCompatibleWith<MP_UNITS_REMOVE_CONST(decltype(ToU)), QP::unit, QP::quantity_spec>
|
||||
requires detail::UnitCompatibleWith<MP_UNITS_REMOVE_CONST(decltype(ToU)), QP::unit, QP::quantity_spec> &&
|
||||
(detail::might_store_converted_value<typename QP::rep>(QP::unit, ToU))
|
||||
[[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp)
|
||||
{
|
||||
return quantity_point{value_cast<ToU>(std::forward<FwdQP>(qp).quantity_from_origin_is_an_implementation_detail_),
|
||||
@ -168,6 +197,7 @@ template<Representation ToRep, typename FwdQP, QuantityPoint QP = std::remove_cv
|
||||
*/
|
||||
template<Unit auto ToU, Representation ToRep, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>>
|
||||
requires detail::UnitCompatibleWith<MP_UNITS_REMOVE_CONST(decltype(ToU)), QP::unit, QP::quantity_spec> &&
|
||||
(detail::might_store_converted_value<ToRep>(QP::unit, ToU)) &&
|
||||
RepresentationOf<ToRep, QP::quantity_spec> && std::constructible_from<ToRep, typename QP::rep>
|
||||
[[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp)
|
||||
{
|
||||
@ -178,6 +208,7 @@ template<Unit auto ToU, Representation ToRep, typename FwdQP, QuantityPoint QP =
|
||||
|
||||
template<Representation ToRep, Unit auto ToU, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>>
|
||||
requires detail::UnitCompatibleWith<MP_UNITS_REMOVE_CONST(decltype(ToU)), QP::unit, QP::quantity_spec> &&
|
||||
(detail::might_store_converted_value<ToRep>(QP::unit, ToU)) &&
|
||||
RepresentationOf<ToRep, QP::quantity_spec> && std::constructible_from<ToRep, typename QP::rep>
|
||||
[[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp)
|
||||
{
|
||||
@ -202,6 +233,7 @@ template<Representation ToRep, Unit auto ToU, typename FwdQP, QuantityPoint QP =
|
||||
*/
|
||||
template<Quantity ToQ, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>>
|
||||
requires detail::UnitCompatibleWith<MP_UNITS_NONCONST_TYPE(ToQ::unit), QP::unit, QP::quantity_spec> &&
|
||||
(detail::might_store_converted_value<typename ToQ::rep>(QP::unit, ToQ::unit)) &&
|
||||
(ToQ::quantity_spec == QP::quantity_spec) && std::constructible_from<typename ToQ::rep, typename QP::rep>
|
||||
[[nodiscard]] constexpr QuantityPoint auto value_cast(FwdQP&& qp)
|
||||
{
|
||||
@ -239,6 +271,7 @@ template<Quantity ToQ, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<Fw
|
||||
*/
|
||||
template<QuantityPoint ToQP, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>>
|
||||
requires detail::UnitCompatibleWith<MP_UNITS_NONCONST_TYPE(ToQP::unit), QP::unit, QP::quantity_spec> &&
|
||||
(detail::might_store_converted_value<typename ToQP::rep>(QP::unit, ToQP::unit)) &&
|
||||
(ToQP::quantity_spec == QP::quantity_spec) &&
|
||||
(detail::same_absolute_point_origins(ToQP::point_origin, QP::point_origin)) &&
|
||||
std::constructible_from<typename ToQP::rep, typename QP::rep>
|
||||
|
@ -405,16 +405,16 @@ template<Unit auto To, auto R, typename Rep>
|
||||
*/
|
||||
template<Unit auto To, auto R, typename Rep>
|
||||
[[nodiscard]] constexpr Quantity auto inverse(const quantity<R, Rep>& q)
|
||||
requires requires {
|
||||
requires(detail::might_store_converted_value<Rep>(one / get_unit(R), To)) && requires {
|
||||
representation_values<Rep>::one();
|
||||
value_cast<To>(representation_values<Rep>::one() / q);
|
||||
}
|
||||
{
|
||||
if constexpr (AssociatedUnit<MP_UNITS_REMOVE_CONST(decltype(To))>) {
|
||||
constexpr QuantitySpec auto qs = get_quantity_spec(To) * quantity<R, Rep>::quantity_spec;
|
||||
return qs(representation_values<Rep>::one() * one).force_in(To * quantity<R, Rep>::unit) / q;
|
||||
return qs(representation_values<Rep>::one() * one).force_in(To * q.unit) / q;
|
||||
} else
|
||||
return (representation_values<Rep>::one() * one).force_in(To * quantity<R, Rep>::unit) / q;
|
||||
return (representation_values<Rep>::one() * one).force_in(To * q.unit) / q;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -278,4 +278,9 @@ static_assert(compare(kind_of<isq::time>(inverse<s>(1. * kHz)), 0.001 * s));
|
||||
// check if constraints work properly for a derived unit of a narrowed kind
|
||||
static_assert(compare(kind_of<isq::frequency>(inverse<Hz>(1 * s)), 1 * Hz));
|
||||
|
||||
// overflow in conversion
|
||||
template<auto Q>
|
||||
concept overflowing_inverse = requires { requires !requires { inverse<si::hertz>(Q); }; };
|
||||
static_assert(overflowing_inverse<10'000'000 * si::femto<si::second>>);
|
||||
|
||||
} // namespace
|
||||
|
@ -1364,4 +1364,20 @@ static_assert(!QuantityOf<decltype(10 * isq::width[m]), isq::height>);
|
||||
static_assert(QuantityOf<decltype(10 * isq::speed[m / s]), isq::speed>);
|
||||
static_assert(QuantityOf<decltype(20 * isq::length[m] / (2 * isq::time[s])), isq::speed>); // derived unnamed quantity
|
||||
|
||||
// overflowing unit conversions
|
||||
template<auto Q>
|
||||
concept overflowing_unit_conversion = requires {
|
||||
requires !requires { quantity<si::metre, std::int8_t>(Q); };
|
||||
requires !requires { quantity<si::milli<si::metre>, std::int16_t>(Q); };
|
||||
requires !requires { Q.in(si::metre); };
|
||||
requires !requires { Q.force_in(si::metre); };
|
||||
requires !requires { Q + std::int8_t(1) * nm; }; // promotion to int
|
||||
requires !requires { Q - std::int8_t(1) * nm; }; // promotion to int
|
||||
requires !requires { Q % std::int8_t(1) * m; };
|
||||
requires !requires { Q == std::int8_t(1) * m; };
|
||||
requires !requires { Q < std::int8_t(1) * m; };
|
||||
requires !requires { typename std::common_type_t<decltype(Q), quantity<si::metre, std::int8_t>>; };
|
||||
};
|
||||
static_assert(overflowing_unit_conversion<std::int8_t(1) * km>);
|
||||
|
||||
} // namespace
|
||||
|
Reference in New Issue
Block a user