mirror of
https://github.com/mpusz/mp-units.git
synced 2025-08-04 12:54:25 +02:00
refactor: quantity_cast
refactored
This commit is contained in:
@@ -81,10 +81,6 @@ concept scalable_ = // exposition only
|
|||||||
castable_number_<T> || (requires { typename T::value_type; } && castable_number_<typename T::value_type> &&
|
castable_number_<T> || (requires { typename T::value_type; } && castable_number_<typename T::value_type> &&
|
||||||
scalable_number_<T, std::common_type_t<typename T::value_type, std::intmax_t>>);
|
scalable_number_<T, std::common_type_t<typename T::value_type, std::intmax_t>>);
|
||||||
|
|
||||||
template<typename T, typename U>
|
|
||||||
concept scalable_with_ = // exposition only
|
|
||||||
common_type_with_<T, U> && scalable_<std::common_type_t<T, U>>;
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
concept Representation = (!Quantity<T>) &&
|
concept Representation = (!Quantity<T>) &&
|
||||||
// (!QuantityLike<T>) && (!wrapped_quantity_<T>) &&
|
// (!QuantityLike<T>) && (!wrapped_quantity_<T>) &&
|
||||||
|
@@ -56,10 +56,10 @@ template<typename T>
|
|||||||
concept floating_point_ = // exposition only
|
concept floating_point_ = // exposition only
|
||||||
(Quantity<T> && treat_as_floating_point<typename T::rep>) || (!Quantity<T> && treat_as_floating_point<T>);
|
(Quantity<T> && treat_as_floating_point<typename T::rep>) || (!Quantity<T> && treat_as_floating_point<T>);
|
||||||
|
|
||||||
template<typename From, typename To>
|
template<typename T, typename Arg>
|
||||||
concept safe_convertible_to_ = // exposition only
|
concept rep_safe_constructible_from_ = // exposition only
|
||||||
(!Quantity<From>) && (!Quantity<To>) && std::convertible_to<From, To> &&
|
(!Quantity<std::remove_cvref_t<Arg>>) && std::constructible_from<T, Arg> &&
|
||||||
(floating_point_<To> || (!floating_point_<From>));
|
(floating_point_<T> || (!floating_point_<Arg>));
|
||||||
|
|
||||||
// QFrom ratio is an exact multiple of QTo
|
// QFrom ratio is an exact multiple of QTo
|
||||||
template<typename QFrom, typename QTo>
|
template<typename QFrom, typename QTo>
|
||||||
@@ -69,16 +69,15 @@ concept harmonic_ = // exposition only
|
|||||||
|
|
||||||
template<typename QFrom, typename QTo>
|
template<typename QFrom, typename QTo>
|
||||||
concept quantity_convertible_to_ = // exposition only
|
concept quantity_convertible_to_ = // exposition only
|
||||||
Quantity<QFrom> && Quantity<QTo> &&
|
Quantity<QFrom> && Quantity<QTo> && requires(QFrom q) { quantity_cast<QTo>(q); } &&
|
||||||
interconvertible(QFrom::reference, QTo::reference) && scalable_with_<typename QFrom::rep, typename QTo::rep> &&
|
|
||||||
(floating_point_<QTo> || (!floating_point_<QFrom> && harmonic_<QFrom, QTo>));
|
(floating_point_<QTo> || (!floating_point_<QFrom> && harmonic_<QFrom, QTo>));
|
||||||
|
|
||||||
template<typename Func, typename T, typename U>
|
template<typename Func, typename T, typename U>
|
||||||
concept quantity_value_for_ = std::regular_invocable<Func, T, U> && Representation<std::invoke_result_t<Func, T, U>>;
|
concept quantity_value_for_ = std::regular_invocable<Func, T, U> && Representation<std::invoke_result_t<Func, T, U>>;
|
||||||
|
|
||||||
template<typename T, typename Func, typename U, typename V>
|
template<typename T, typename Func, typename U, typename V>
|
||||||
concept invoke_result_convertible_to_ =
|
concept invoke_result_convertible_to_ = Representation<T> && quantity_value_for_<Func, U, V> &&
|
||||||
Representation<T> && quantity_value_for_<Func, U, V> && safe_convertible_to_<T, std::invoke_result_t<Func, U, V>>;
|
rep_safe_constructible_from_<std::invoke_result_t<Func, U, V>, T>;
|
||||||
|
|
||||||
template<typename Func, typename Q, typename V>
|
template<typename Func, typename Q, typename V>
|
||||||
concept have_quantity_for_ = Quantity<Q> && (!Quantity<V>) && quantity_value_for_<Func, typename Q::rep, V>;
|
concept have_quantity_for_ = Quantity<Q> && (!Quantity<V>) && quantity_value_for_<Func, typename Q::rep, V>;
|
||||||
@@ -151,7 +150,7 @@ public:
|
|||||||
quantity(quantity&&) = default;
|
quantity(quantity&&) = default;
|
||||||
|
|
||||||
template<typename Value>
|
template<typename Value>
|
||||||
requires safe_convertible_to_<std::remove_cvref_t<Value>, rep>
|
requires rep_safe_constructible_from_<rep, Value>
|
||||||
constexpr explicit(!detail::quantity_one<quantity>) quantity(Value&& v) : number_(std::forward<Value>(v))
|
constexpr explicit(!detail::quantity_one<quantity>) quantity(Value&& v) : number_(std::forward<Value>(v))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@@ -48,30 +48,6 @@ class quantity;
|
|||||||
// template<PointKind PK, UnitOf<typename PK::dimension> U, Representation Rep>
|
// template<PointKind PK, UnitOf<typename PK::dimension> U, Representation Rep>
|
||||||
// class quantity_point_kind;
|
// class quantity_point_kind;
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
template<typename From, typename To>
|
|
||||||
struct cast_traits;
|
|
||||||
|
|
||||||
template<typename From, typename To>
|
|
||||||
requires common_type_with_<std::common_type_t<From, To>, std::intmax_t>
|
|
||||||
struct cast_traits<From, To> {
|
|
||||||
using multiplier_type = std::common_type_t<std::common_type_t<From, To>, std::intmax_t>;
|
|
||||||
using rep_type = multiplier_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename From, typename To>
|
|
||||||
requires(!common_type_with_<std::common_type_t<From, To>, std::intmax_t> &&
|
|
||||||
scalable_number_<std::common_type_t<From, To>, std::intmax_t> &&
|
|
||||||
requires { typename std::common_type_t<From, To>::value_type; } &&
|
|
||||||
common_type_with_<typename std::common_type_t<From, To>::value_type, std::intmax_t>)
|
|
||||||
struct cast_traits<From, To> {
|
|
||||||
using multiplier_type = std::common_type_t<typename std::common_type_t<From, To>::value_type, std::intmax_t>;
|
|
||||||
using rep_type = std::common_type_t<From, To>;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Explicit cast of a quantity
|
* @brief Explicit cast of a quantity
|
||||||
*
|
*
|
||||||
@@ -84,16 +60,41 @@ struct cast_traits<From, To> {
|
|||||||
*
|
*
|
||||||
* @tparam To a target quantity type to cast to
|
* @tparam To a target quantity type to cast to
|
||||||
*/
|
*/
|
||||||
template<Quantity To, auto R, scalable_with_<typename To::rep> Rep>
|
template<Quantity To, auto R, typename Rep>
|
||||||
requires(interconvertible(To::reference, R))
|
requires(interconvertible(To::reference, R)) &&
|
||||||
|
((R.unit == To::unit && std::constructible_from<typename To::rep, Rep>) ||
|
||||||
|
(R.unit != To::unit)) // && scalable_with_<typename To::rep>))
|
||||||
|
// TODO how to constrain the second part here?
|
||||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
|
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
|
||||||
{
|
{
|
||||||
if constexpr (R.unit == To::unit) {
|
if constexpr (R.unit == To::unit) {
|
||||||
return To(static_cast<TYPENAME To::rep>(q.number()));
|
// no scaling of the number needed
|
||||||
|
return To(static_cast<TYPENAME To::rep>(q.number())); // this is the only (and recommended) way to do
|
||||||
|
// a truncating conversion on a number, so we are
|
||||||
|
// using static_cast to suppress all the compiler
|
||||||
|
// warnings on conversions
|
||||||
} else {
|
} else {
|
||||||
using traits = detail::cast_traits<Rep, typename To::rep>;
|
// scale the number
|
||||||
using multiplier_type = TYPENAME traits::multiplier_type;
|
using rep_type = decltype([] {
|
||||||
using rep_type = TYPENAME traits::rep_type;
|
// determines the best representation type
|
||||||
|
if constexpr (requires { typename std::common_type_t<Rep, typename To::rep>; })
|
||||||
|
// returns a common type of two representation types if available
|
||||||
|
// i.e. `double` and `int` will end up with `double` precision
|
||||||
|
return std::common_type_t<Rep, typename To::rep>{};
|
||||||
|
else
|
||||||
|
return Rep{};
|
||||||
|
}());
|
||||||
|
using multiplier_type = decltype([] {
|
||||||
|
// widen the type to prevent overflows
|
||||||
|
using wider_type = decltype(rep_type{} * std::intmax_t{});
|
||||||
|
// check if `wider_type` supports scaling operations
|
||||||
|
if constexpr (requires(wider_type v) { v* v / v; })
|
||||||
|
// if the `wider_type` can handle scaling operations then use it to improve accuracy
|
||||||
|
return wider_type{};
|
||||||
|
else
|
||||||
|
// needed for example for linear algebra where `op/` on matrix types is not available
|
||||||
|
return std::intmax_t{};
|
||||||
|
}());
|
||||||
|
|
||||||
constexpr Magnitude auto c_mag = detail::get_canonical_unit(R.unit).mag / detail::get_canonical_unit(To::unit).mag;
|
constexpr Magnitude auto c_mag = detail::get_canonical_unit(R.unit).mag / detail::get_canonical_unit(To::unit).mag;
|
||||||
constexpr Magnitude auto num = numerator(c_mag);
|
constexpr Magnitude auto num = numerator(c_mag);
|
||||||
@@ -181,8 +182,8 @@ template<Unit auto ToU, auto R, typename Rep>
|
|||||||
*
|
*
|
||||||
* @tparam ToRep a representation type to use for a target quantity
|
* @tparam ToRep a representation type to use for a target quantity
|
||||||
*/
|
*/
|
||||||
template<Representation ToRep, auto R, scalable_with_<ToRep> Rep>
|
template<Representation ToRep, auto R, typename Rep>
|
||||||
// requires(std::constructible_from<ToRep, std::common_type_t<ToRep, Rep>>)
|
requires std::constructible_from<ToRep, Rep>
|
||||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
|
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
|
||||||
{
|
{
|
||||||
return quantity_cast<quantity<R, ToRep>>(q);
|
return quantity_cast<quantity<R, ToRep>>(q);
|
||||||
@@ -308,7 +309,8 @@ template<Representation ToRep, auto R, scalable_with_<ToRep> Rep>
|
|||||||
// * Implicit conversions between quantity point kinds of different types are allowed only for "safe"
|
// * 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.
|
// * (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
|
// * This cast gets the target (quantity) point kind type to cast to or anything that works for quantity_kind_cast.
|
||||||
|
// For
|
||||||
// * example:
|
// * example:
|
||||||
// *
|
// *
|
||||||
// * auto q1 = units::quantity_point_kind_cast<decltype(ns::x_coordinate{1 * m))>(ns::x_coordinate{1 * mm});
|
// * auto q1 = units::quantity_point_kind_cast<decltype(ns::x_coordinate{1 * m))>(ns::x_coordinate{1 * mm});
|
||||||
|
Reference in New Issue
Block a user