forked from mpusz/mp-units
refactor: casts cleanup and improved
This commit is contained in:
@@ -719,6 +719,16 @@ template<auto H1, auto... T1, auto H2, auto... T2>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<auto... Ms>
|
||||||
|
[[nodiscard]] consteval auto common_magnitude_type_impl(magnitude<Ms...>)
|
||||||
|
{
|
||||||
|
return (... * decltype(get_base(Ms)){}) * std::intmax_t{};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the most precise type to express the magnitude factor
|
||||||
|
template<Magnitude auto M>
|
||||||
|
using common_magnitude_type = decltype(common_magnitude_type_impl(M));
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -764,6 +774,7 @@ struct prime_factorization<1> {
|
|||||||
|
|
||||||
template<std::intmax_t N>
|
template<std::intmax_t N>
|
||||||
inline constexpr auto prime_factorization_v = prime_factorization<N>::value;
|
inline constexpr auto prime_factorization_v = prime_factorization<N>::value;
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -22,22 +22,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <mp-units/bits/external/type_traits.h>
|
|
||||||
#include <mp-units/bits/magnitude.h>
|
|
||||||
#include <mp-units/bits/quantity_concepts.h>
|
#include <mp-units/bits/quantity_concepts.h>
|
||||||
#include <mp-units/bits/reference_concepts.h>
|
|
||||||
#include <mp-units/bits/representation_concepts.h>
|
|
||||||
#include <mp-units/bits/unit_concepts.h>
|
|
||||||
#include <mp-units/customization_points.h>
|
|
||||||
#include <mp-units/dimension.h>
|
|
||||||
#include <mp-units/reference.h>
|
#include <mp-units/reference.h>
|
||||||
#include <mp-units/unit.h>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace mp_units {
|
namespace mp_units {
|
||||||
|
|
||||||
template<Reference auto R, RepresentationOf<get_quantity_spec(R).character> Rep>
|
|
||||||
class quantity;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Explicit cast of a quantity type
|
* @brief Explicit cast of a quantity type
|
||||||
*
|
*
|
||||||
@@ -56,11 +46,10 @@ class quantity;
|
|||||||
* @tparam ToQS a quantity specification to use for a target quantity
|
* @tparam ToQS a quantity specification to use for a target quantity
|
||||||
*/
|
*/
|
||||||
template<QuantitySpec auto ToQS, typename Q>
|
template<QuantitySpec auto ToQS, typename Q>
|
||||||
requires Quantity<std::remove_cvref_t<Q>> && (castable(std::remove_cvref_t<Q>::quantity_spec, ToQS))
|
|
||||||
[[nodiscard]] constexpr Quantity auto quantity_cast(Q&& q)
|
[[nodiscard]] constexpr Quantity auto quantity_cast(Q&& q)
|
||||||
|
requires Quantity<std::remove_cvref_t<Q>> && (castable(q.quantity_spec, ToQS))
|
||||||
{
|
{
|
||||||
constexpr reference<ToQS, std::remove_cvref_t<Q>::unit> r;
|
return make_quantity<reference<ToQS, q.unit>{}>(std::forward<Q>(q).number());
|
||||||
return std::forward<Q>(q).number() * r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mp_units
|
} // namespace mp_units
|
||||||
|
@@ -28,67 +28,71 @@
|
|||||||
#include <mp-units/bits/reference_concepts.h>
|
#include <mp-units/bits/reference_concepts.h>
|
||||||
#include <mp-units/unit.h>
|
#include <mp-units/unit.h>
|
||||||
|
|
||||||
namespace mp_units {
|
namespace mp_units::detail {
|
||||||
|
|
||||||
template<Reference auto R, RepresentationOf<get_quantity_spec(R).character> Rep>
|
// determines the best available representation type
|
||||||
class quantity;
|
template<Quantity From, Quantity To>
|
||||||
|
[[nodiscard]] consteval auto common_rep_type(From, To)
|
||||||
|
{
|
||||||
|
if constexpr (requires { typename std::common_type_t<typename From::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<typename From::rep, typename To::rep>{};
|
||||||
|
else
|
||||||
|
return typename From::rep{};
|
||||||
|
}
|
||||||
|
|
||||||
namespace detail {
|
// determines the value type used by the representation type
|
||||||
|
template<typename Rep>
|
||||||
|
[[nodiscard]] consteval auto rep_value_type(Rep)
|
||||||
|
{
|
||||||
|
if constexpr (requires { typename Rep::value_type; })
|
||||||
|
return typename Rep::value_type{};
|
||||||
|
else if constexpr (requires { typename Rep::element_type; })
|
||||||
|
return typename Rep::element_type{};
|
||||||
|
else
|
||||||
|
return Rep{};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Explicit cast of entire quantity
|
* @brief Explicit cast between different quantity types
|
||||||
*
|
*
|
||||||
* @note This is too powerful to be used by users.
|
* @note This is a low-level facility and is too powerful to be used by the users directly. They should either use
|
||||||
|
* `value_cast` or `quantity_cast`.
|
||||||
*
|
*
|
||||||
* @tparam To a target quantity type to cast to
|
* @tparam To a target quantity type to cast to
|
||||||
*/
|
*/
|
||||||
template<Quantity To, auto R, typename Rep>
|
template<Quantity To, typename From>
|
||||||
requires(castable(get_quantity_spec(R), To::quantity_spec)) &&
|
requires Quantity<std::remove_cvref_t<From>> &&
|
||||||
((get_unit(R) == To::unit && std::constructible_from<typename To::rep, Rep>) ||
|
(castable(std::remove_reference_t<From>::quantity_spec, To::quantity_spec)) &&
|
||||||
(get_unit(R) != To::unit)) // && scalable_with_<typename To::rep>))
|
((std::remove_reference_t<From>::unit == To::unit &&
|
||||||
|
std::constructible_from<typename To::rep, typename std::remove_reference_t<From>::rep>) ||
|
||||||
|
(std::remove_reference_t<From>::unit != To::unit)) // && scalable_with_<typename To::rep>))
|
||||||
// TODO how to constrain the second part here?
|
// TODO how to constrain the second part here?
|
||||||
[[nodiscard]] constexpr Quantity auto sudo_cast(const quantity<R, Rep>& q)
|
[[nodiscard]] constexpr Quantity auto sudo_cast(From&& q)
|
||||||
{
|
{
|
||||||
if constexpr (get_unit(R) == To::unit) {
|
if constexpr (q.unit == To::unit) {
|
||||||
// no scaling of the number needed
|
// no scaling of the number needed
|
||||||
return static_cast<TYPENAME To::rep>(q.number()) * To::reference; // this is the only (and recommended) way to do
|
return make_quantity<To::reference>(
|
||||||
|
static_cast<TYPENAME To::rep>(std::forward<From>(q).number())); // this is the only (and recommended) way to do
|
||||||
// a truncating conversion on a number, so we are
|
// a truncating conversion on a number, so we are
|
||||||
// using static_cast to suppress all the compiler
|
// using static_cast to suppress all the compiler
|
||||||
// warnings on conversions
|
// warnings on conversions
|
||||||
} else {
|
} else {
|
||||||
// scale the number
|
// scale the number
|
||||||
using rep_type = decltype([] {
|
constexpr Magnitude auto c_mag = get_canonical_unit(q.unit).mag / get_canonical_unit(To::unit).mag;
|
||||||
// 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(get_unit(R)).mag / detail::get_canonical_unit(To::unit).mag;
|
|
||||||
constexpr Magnitude auto num = numerator(c_mag);
|
constexpr Magnitude auto num = numerator(c_mag);
|
||||||
constexpr Magnitude auto den = denominator(c_mag);
|
constexpr Magnitude auto den = denominator(c_mag);
|
||||||
constexpr Magnitude auto irr = c_mag * (den / num);
|
constexpr Magnitude auto irr = c_mag * (den / num);
|
||||||
|
using c_mag_type = common_magnitude_type<c_mag>;
|
||||||
|
using c_rep_type = decltype(common_rep_type(q, To{}));
|
||||||
|
using v_type = decltype(rep_value_type(c_rep_type{}));
|
||||||
|
using multiplier_type = decltype(v_type{} * c_mag_type{});
|
||||||
constexpr auto val = [](Magnitude auto m) { return get_value<multiplier_type>(m); };
|
constexpr auto val = [](Magnitude auto m) { return get_value<multiplier_type>(m); };
|
||||||
return static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.number()) * val(num) / val(den) * val(irr)) *
|
return static_cast<TYPENAME To::rep>(static_cast<c_rep_type>(std::forward<From>(q).number()) * val(num) / val(den) *
|
||||||
|
val(irr)) *
|
||||||
To::reference;
|
To::reference;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace mp_units::detail
|
||||||
} // namespace mp_units
|
|
||||||
|
@@ -23,16 +23,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <mp-units/bits/quantity_concepts.h>
|
#include <mp-units/bits/quantity_concepts.h>
|
||||||
#include <mp-units/bits/reference_concepts.h>
|
|
||||||
#include <mp-units/bits/representation_concepts.h>
|
#include <mp-units/bits/representation_concepts.h>
|
||||||
#include <mp-units/bits/sudo_cast.h>
|
#include <mp-units/bits/sudo_cast.h>
|
||||||
#include <mp-units/bits/unit_concepts.h>
|
#include <mp-units/bits/unit_concepts.h>
|
||||||
|
#include <mp-units/reference.h>
|
||||||
|
|
||||||
namespace mp_units {
|
namespace mp_units {
|
||||||
|
|
||||||
template<Reference auto R, RepresentationOf<get_quantity_spec(R).character> Rep>
|
|
||||||
class quantity;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Explicit cast of a quantity's unit
|
* @brief Explicit cast of a quantity's unit
|
||||||
*
|
*
|
||||||
@@ -43,17 +40,19 @@ class quantity;
|
|||||||
*
|
*
|
||||||
* @tparam ToU a unit to use for a target quantity
|
* @tparam ToU a unit to use for a target quantity
|
||||||
*/
|
*/
|
||||||
template<Unit auto ToU, auto R, typename Rep>
|
template<Unit auto ToU, typename Q>
|
||||||
requires(convertible(get_unit(R), ToU))
|
[[nodiscard]] constexpr Quantity auto value_cast(Q&& q)
|
||||||
[[nodiscard]] constexpr Quantity auto value_cast(const quantity<R, Rep>& q)
|
requires Quantity<std::remove_cvref_t<Q>> && (convertible(q.reference, ToU))
|
||||||
{
|
{
|
||||||
if constexpr (detail::is_specialization_of_reference<std::remove_const_t<decltype(R)>> ||
|
using q_type = std::remove_reference_t<Q>;
|
||||||
!AssociatedUnit<std::remove_const_t<decltype(ToU)>>) {
|
constexpr auto r = [] {
|
||||||
constexpr reference<quantity<R, Rep>::quantity_spec, ToU> r;
|
if constexpr (detail::is_specialization_of_reference<std::remove_const_t<decltype(q_type::reference)>> ||
|
||||||
return detail::sudo_cast<quantity<r, Rep>>(q);
|
!AssociatedUnit<std::remove_const_t<decltype(ToU)>>)
|
||||||
} else {
|
return reference<q_type::quantity_spec, ToU>{};
|
||||||
return detail::sudo_cast<quantity<ToU, Rep>>(q);
|
else
|
||||||
}
|
return ToU;
|
||||||
|
}();
|
||||||
|
return detail::sudo_cast<quantity<r, typename q_type::rep>>(std::forward<Q>(q));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,11 +65,13 @@ 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, typename Rep>
|
template<Representation ToRep, typename Q>
|
||||||
requires RepresentationOf<ToRep, get_quantity_spec(R).character> && std::constructible_from<ToRep, Rep>
|
requires Quantity<std::remove_cvref_t<Q>> &&
|
||||||
[[nodiscard]] constexpr Quantity auto value_cast(const quantity<R, Rep>& q)
|
RepresentationOf<ToRep, std::remove_reference_t<Q>::quantity_spec.character> &&
|
||||||
|
std::constructible_from<ToRep, typename std::remove_reference_t<Q>::rep>
|
||||||
|
[[nodiscard]] constexpr quantity<std::remove_reference_t<Q>::reference, ToRep> value_cast(Q&& q)
|
||||||
{
|
{
|
||||||
return detail::sudo_cast<quantity<R, ToRep>>(q);
|
return detail::sudo_cast<quantity<q.reference, ToRep>>(std::forward<Q>(q));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mp_units
|
} // namespace mp_units
|
||||||
|
Reference in New Issue
Block a user