refactor: quantity_cast refactored

This commit is contained in:
Mateusz Pusz
2022-11-30 17:54:13 +01:00
parent 1fbaeb4ffc
commit 872caf460a
3 changed files with 43 additions and 46 deletions

View File

@@ -81,10 +81,6 @@ concept scalable_ = // exposition only
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>>);
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>
concept Representation = (!Quantity<T>) &&
// (!QuantityLike<T>) && (!wrapped_quantity_<T>) &&

View File

@@ -56,10 +56,10 @@ template<typename T>
concept floating_point_ = // exposition only
(Quantity<T> && treat_as_floating_point<typename T::rep>) || (!Quantity<T> && treat_as_floating_point<T>);
template<typename From, typename To>
concept safe_convertible_to_ = // exposition only
(!Quantity<From>) && (!Quantity<To>) && std::convertible_to<From, To> &&
(floating_point_<To> || (!floating_point_<From>));
template<typename T, typename Arg>
concept rep_safe_constructible_from_ = // exposition only
(!Quantity<std::remove_cvref_t<Arg>>) && std::constructible_from<T, Arg> &&
(floating_point_<T> || (!floating_point_<Arg>));
// QFrom ratio is an exact multiple of QTo
template<typename QFrom, typename QTo>
@@ -69,16 +69,15 @@ concept harmonic_ = // exposition only
template<typename QFrom, typename QTo>
concept quantity_convertible_to_ = // exposition only
Quantity<QFrom> && Quantity<QTo> &&
interconvertible(QFrom::reference, QTo::reference) && scalable_with_<typename QFrom::rep, typename QTo::rep> &&
Quantity<QFrom> && Quantity<QTo> && requires(QFrom q) { quantity_cast<QTo>(q); } &&
(floating_point_<QTo> || (!floating_point_<QFrom> && harmonic_<QFrom, QTo>));
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>>;
template<typename T, typename Func, typename U, typename V>
concept invoke_result_convertible_to_ =
Representation<T> && quantity_value_for_<Func, U, V> && safe_convertible_to_<T, std::invoke_result_t<Func, U, V>>;
concept invoke_result_convertible_to_ = Representation<T> && quantity_value_for_<Func, U, V> &&
rep_safe_constructible_from_<std::invoke_result_t<Func, U, V>, T>;
template<typename Func, typename Q, typename 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;
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))
{
}

View File

@@ -48,30 +48,6 @@ class quantity;
// template<PointKind PK, UnitOf<typename PK::dimension> U, Representation Rep>
// 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
*
@@ -84,16 +60,41 @@ struct cast_traits<From, To> {
*
* @tparam To a target quantity type to cast to
*/
template<Quantity To, auto R, scalable_with_<typename To::rep> Rep>
requires(interconvertible(To::reference, R))
template<Quantity To, auto R, typename Rep>
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)
{
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 {
using traits = detail::cast_traits<Rep, typename To::rep>;
using multiplier_type = TYPENAME traits::multiplier_type;
using rep_type = TYPENAME traits::rep_type;
// scale the number
using rep_type = decltype([] {
// 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 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
*/
template<Representation ToRep, auto R, scalable_with_<ToRep> Rep>
// requires(std::constructible_from<ToRep, std::common_type_t<ToRep, Rep>>)
template<Representation ToRep, auto R, typename Rep>
requires std::constructible_from<ToRep, Rep>
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& 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"
// * (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:
// *
// * auto q1 = units::quantity_point_kind_cast<decltype(ns::x_coordinate{1 * m))>(ns::x_coordinate{1 * mm});