forked from mpusz/mp-units
refactor: interoperability with quantity-like types simplified
Type trait specialization for each `quantity` type is not needed anymore. Relates to #33.
This commit is contained in:
@ -59,20 +59,20 @@ Concepts
|
||||
|
||||
.. concept:: template<typename T> QuantityLike
|
||||
|
||||
A concept matching all quantity-like types. Satisfied by all types for which a correct specialization
|
||||
of :class:`quantity_traits` type trait is provided.
|
||||
A concept matching all quantity-like types other than specialization of :class:`quantity`. Satisfied by
|
||||
all types for which a correct specialization of :class:`quantity_like_traits` type trait is provided.
|
||||
|
||||
.. concept:: template<typename T> WrappedQuantity
|
||||
|
||||
A concept matching types that wrap quantity objects. Satisfied by all wrapper types that
|
||||
satisfy :expr:`QuantityLike<typename T::value_type>` recursively
|
||||
satisfy :expr:`Quantity<typename T::value_type> || QuantityLike<typename T::value_type>` recursively
|
||||
(i.e. ``std::optional<si::length<si::metre>>``).
|
||||
|
||||
.. concept:: template<typename T> QuantityValue
|
||||
|
||||
A concept matching types that can be used as a `Quantity` representation type. Satisfied
|
||||
by types that match :expr:`(!QuantityLike<T>) && (!WrappedQuantity<T>) && std::regular<T>` and
|
||||
satisfy one of the following:
|
||||
by types that match :expr:`(!Quantity<T>) && (!QuantityLike<T>) && (!WrappedQuantity<T>) && std::regular<T>`
|
||||
and satisfy one of the following:
|
||||
|
||||
- if :expr:`common_type_with<T, std::intmax_t>` is ``true``, then :expr:`std::common_type_t<T, std::intmax_t>`
|
||||
must at least provide binary multiplication and division operators,
|
||||
|
@ -6,5 +6,5 @@ Customization Points
|
||||
.. doxygenstruct:: units::quantity_values
|
||||
:members:
|
||||
|
||||
.. doxygenstruct:: units::quantity_traits
|
||||
.. doxygenstruct:: units::quantity_like_traits
|
||||
:members:
|
||||
|
@ -216,7 +216,7 @@ inline constexpr bool is_quantity_point = false;
|
||||
/**
|
||||
* @brief A concept matching all quantities in the library.
|
||||
*
|
||||
* Satisfied by all specializations of :class:`quantity`.
|
||||
* Satisfied by all specializations of @c quantity.
|
||||
*/
|
||||
template<typename T>
|
||||
concept Quantity = detail::is_quantity<T>;
|
||||
@ -224,23 +224,23 @@ concept Quantity = detail::is_quantity<T>;
|
||||
/**
|
||||
* @brief A concept matching all quantity points in the library.
|
||||
*
|
||||
* Satisfied by all specializations of :class:`quantity_point`.
|
||||
* Satisfied by all specializations of @c quantity_point.
|
||||
*/
|
||||
template<typename T>
|
||||
concept QuantityPoint = detail::is_quantity_point<T>;
|
||||
|
||||
/**
|
||||
* @brief A concept matching all quantity-like types
|
||||
* @brief A concept matching all quantity-like types (other than specialization of @c quantity)
|
||||
*
|
||||
* Satisfied by all types for which a correct specialization of `quantity_traits`
|
||||
* Satisfied by all types for which a correct specialization of `quantity_like_traits`
|
||||
* type trait is provided.
|
||||
*/
|
||||
template<typename T>
|
||||
concept QuantityLike = requires(T q) {
|
||||
typename quantity_traits<T>::dimension;
|
||||
typename quantity_traits<T>::unit;
|
||||
typename quantity_traits<T>::rep;
|
||||
{ quantity_traits<T>::count(q) } -> std::convertible_to<typename quantity_traits<T>::rep>;
|
||||
typename quantity_like_traits<T>::dimension;
|
||||
typename quantity_like_traits<T>::unit;
|
||||
typename quantity_like_traits<T>::rep;
|
||||
{ quantity_like_traits<T>::count(q) } -> std::convertible_to<typename quantity_like_traits<T>::rep>;
|
||||
};
|
||||
|
||||
// QuantityValue
|
||||
@ -279,7 +279,7 @@ inline constexpr bool is_wrapped_quantity = false;
|
||||
|
||||
template<typename T>
|
||||
requires requires { typename T::value_type; }
|
||||
inline constexpr bool is_wrapped_quantity<T> = QuantityLike<typename T::value_type> || is_wrapped_quantity<typename T::value_type>;
|
||||
inline constexpr bool is_wrapped_quantity<T> = Quantity<typename T::value_type> || QuantityLike<typename T::value_type> || is_wrapped_quantity<typename T::value_type>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
@ -300,6 +300,7 @@ concept wrapped_quantity_ = // exposition only
|
||||
*/
|
||||
template<typename T>
|
||||
concept QuantityValue =
|
||||
(!Quantity<T>) &&
|
||||
(!QuantityLike<T>) &&
|
||||
(!wrapped_quantity_<T>) &&
|
||||
std::regular<T> &&
|
||||
|
@ -29,7 +29,7 @@
|
||||
namespace units {
|
||||
|
||||
template<typename Rep, typename Period>
|
||||
struct quantity_traits<std::chrono::duration<Rep, Period>> {
|
||||
struct quantity_like_traits<std::chrono::duration<Rep, Period>> {
|
||||
using dimension = physical::si::dim_time;
|
||||
using unit = downcast_unit<dimension, ratio(Period::num, Period::den)>;
|
||||
using rep = Rep;
|
||||
|
@ -71,6 +71,6 @@ struct quantity_values {
|
||||
* @tparam T the type to provide support for
|
||||
*/
|
||||
template<typename T>
|
||||
struct quantity_traits;
|
||||
struct quantity_like_traits;
|
||||
|
||||
} // namespace units
|
||||
|
@ -36,8 +36,8 @@ namespace units {
|
||||
|
||||
template<typename T>
|
||||
concept floating_point_ = // exposition only
|
||||
(QuantityLike<T> && treat_as_floating_point<typename quantity_traits<T>::rep>) ||
|
||||
(!QuantityLike<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>
|
||||
concept safe_convertible_to_ = // exposition only
|
||||
@ -49,15 +49,15 @@ concept safe_convertible_to_ = // exposition only
|
||||
// QFrom ratio is an exact multiple of QTo
|
||||
template<typename QFrom, typename QTo>
|
||||
concept harmonic_ = // exposition only
|
||||
QuantityLike<QFrom> &&
|
||||
Quantity<QFrom> &&
|
||||
Quantity<QTo> &&
|
||||
requires(QFrom from, QTo to) { requires is_integral(detail::quantity_ratio(from) / detail::quantity_ratio(to)); };
|
||||
|
||||
template<typename QFrom, typename QTo>
|
||||
concept safe_castable_to_ = // exposition only
|
||||
QuantityLike<QFrom> &&
|
||||
QuantityOf<QTo, typename quantity_traits<QFrom>::dimension> &&
|
||||
scalable_with_<typename quantity_traits<QFrom>::rep, typename QTo::rep> &&
|
||||
Quantity<QFrom> &&
|
||||
QuantityOf<QTo, typename QFrom::dimension> &&
|
||||
scalable_with_<typename QFrom::rep, typename QTo::rep> &&
|
||||
(floating_point_<QTo> || (!floating_point_<QFrom> && harmonic_<QFrom, QTo>));
|
||||
|
||||
template<typename Func, typename T, typename U>
|
||||
@ -81,6 +81,9 @@ template<typename Func, Quantity Q1, QuantityEquivalentTo<Q1> Q2>
|
||||
requires quantity_value_for_<Func, typename Q1::rep, typename Q2::rep>
|
||||
using common_quantity_for = common_quantity<Q1, Q2, std::invoke_result_t<Func, typename Q1::rep, typename Q2::rep>>;
|
||||
|
||||
template<QuantityLike Q>
|
||||
using quantity_like_type = quantity<typename quantity_like_traits<Q>::dimension, typename quantity_like_traits<Q>::unit, typename quantity_like_traits<Q>::rep>;
|
||||
|
||||
/**
|
||||
* @brief A quantity
|
||||
*
|
||||
@ -133,8 +136,12 @@ public:
|
||||
template<safe_convertible_to_<rep> Value>
|
||||
explicit(!(equivalent<quantity, dimensionless<::units::one, rep>>)) constexpr quantity(const Value& v) : value_(static_cast<rep>(v)) {}
|
||||
|
||||
template<QuantityLike Q>
|
||||
requires std::same_as<quantity, quantity_like_type<Q>>
|
||||
explicit constexpr quantity(const Q& q) : value_(q.count()) {}
|
||||
|
||||
template<safe_castable_to_<quantity> Q>
|
||||
explicit(!Quantity<Q>) constexpr quantity(const Q& q) : value_(quantity_cast<quantity>(q).count()) {}
|
||||
constexpr quantity(const Q& q) : value_(quantity_cast<quantity>(q).count()) {}
|
||||
|
||||
quantity& operator=(const quantity&) = default;
|
||||
quantity& operator=(quantity&&) = default;
|
||||
@ -328,10 +335,10 @@ template<QuantityValue V>
|
||||
explicit(false) quantity(V) -> quantity<dim_one, one, V>;
|
||||
|
||||
template<QuantityLike Q>
|
||||
explicit(!Quantity<Q>) quantity(Q) -> quantity<typename quantity_traits<Q>::dimension, typename quantity_traits<Q>::unit, typename quantity_traits<Q>::rep>;
|
||||
explicit quantity(Q) -> quantity_like_type<Q>;
|
||||
|
||||
// non-member binary operators
|
||||
template<QuantityLike Q1, QuantityEquivalentTo<Q1> Q2>
|
||||
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
|
||||
requires quantity_value_for_<std::plus<>, typename Q1::rep, typename Q2::rep>
|
||||
[[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs)
|
||||
{
|
||||
@ -404,14 +411,6 @@ template<Quantity Q1, QuantityEquivalentTo<Q1> Q2>
|
||||
}
|
||||
|
||||
// type traits
|
||||
template<typename D, typename U, typename Rep>
|
||||
struct quantity_traits<quantity<D, U, Rep>> {
|
||||
using dimension = D;
|
||||
using unit = U;
|
||||
using rep = Rep;
|
||||
static constexpr rep count(const quantity<D, U, Rep>& q) { return q.count(); }
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename D, typename U, typename Rep>
|
||||
|
@ -44,17 +44,14 @@ class quantity_point;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<QuantityLike Q>
|
||||
constexpr auto quantity_ratio(const Q&)
|
||||
template<typename D, typename U, typename Rep>
|
||||
constexpr auto quantity_ratio(const quantity<D, U, Rep>&)
|
||||
{
|
||||
using traits = quantity_traits<Q>;
|
||||
using dim = TYPENAME traits::dimension;
|
||||
using unit = TYPENAME traits::unit;
|
||||
if constexpr(BaseDimension<dim>) {
|
||||
return unit::ratio;
|
||||
if constexpr(BaseDimension<D>) {
|
||||
return U::ratio;
|
||||
}
|
||||
else {
|
||||
return dim::base_units_ratio * unit::ratio / dim::coherent_unit::ratio;
|
||||
return D::base_units_ratio * U::ratio / D::coherent_unit::ratio;
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,34 +102,29 @@ struct cast_traits<From, To> {
|
||||
*
|
||||
* @tparam To a target quantity type to cast to
|
||||
*/
|
||||
template<Quantity To, QuantityLike From>
|
||||
requires QuantityOf<To, typename quantity_traits<From>::dimension> &&
|
||||
scalable_with_<typename quantity_traits<From>::rep, typename To::rep>
|
||||
[[nodiscard]] constexpr auto quantity_cast(const From& q)
|
||||
template<Quantity To, typename D, typename U, scalable_with_<typename To::rep> Rep>
|
||||
requires QuantityOf<To, D>
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
||||
{
|
||||
using traits = quantity_traits<From>;
|
||||
using from_dim = TYPENAME traits::dimension;
|
||||
using from_unit = TYPENAME traits::unit;
|
||||
using from_rep = TYPENAME traits::rep;
|
||||
using ret_unit = downcast_unit<typename To::dimension, To::unit::ratio>;
|
||||
using ret = quantity<typename To::dimension, ret_unit, typename To::rep>;
|
||||
using cast_traits = detail::cast_traits<from_rep, typename To::rep>;
|
||||
using ratio_type = TYPENAME cast_traits::ratio_type;
|
||||
using rep_type = TYPENAME cast_traits::rep_type;
|
||||
constexpr auto c_ratio = detail::cast_ratio(quantity<from_dim, from_unit, from_rep>(), To());
|
||||
using traits = detail::cast_traits<Rep, typename To::rep>;
|
||||
using ratio_type = TYPENAME traits::ratio_type;
|
||||
using rep_type = TYPENAME traits::rep_type;
|
||||
constexpr auto c_ratio = detail::cast_ratio(quantity<D, U, Rep>(), To());
|
||||
|
||||
if constexpr (treat_as_floating_point<rep_type>) {
|
||||
return ret(static_cast<TYPENAME To::rep>(static_cast<rep_type>(traits::count(q)) *
|
||||
return ret(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.count()) *
|
||||
(static_cast<ratio_type>(c_ratio.num) * detail::fpow10<ratio_type>(c_ratio.exp) / static_cast<ratio_type>(c_ratio.den))));
|
||||
}
|
||||
else {
|
||||
if constexpr (c_ratio.exp > 0) {
|
||||
return ret(static_cast<TYPENAME To::rep>(static_cast<rep_type>(traits::count(q)) *
|
||||
return ret(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.count()) *
|
||||
(static_cast<ratio_type>(c_ratio.num) * static_cast<ratio_type>(detail::ipow10(c_ratio.exp))) /
|
||||
static_cast<ratio_type>(c_ratio.den)));
|
||||
}
|
||||
else {
|
||||
return ret(static_cast<TYPENAME To::rep>(static_cast<rep_type>(traits::count(q)) *
|
||||
return ret(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.count()) *
|
||||
static_cast<ratio_type>(c_ratio.num) /
|
||||
(static_cast<ratio_type>(c_ratio.den) * static_cast<ratio_type>(detail::ipow10(-c_ratio.exp)))));
|
||||
}
|
||||
@ -151,11 +143,11 @@ template<Quantity To, QuantityLike From>
|
||||
*
|
||||
* @tparam ToD a dimension type to use for a target quantity
|
||||
*/
|
||||
template<Dimension ToD, QuantityLike From>
|
||||
requires equivalent<ToD, typename quantity_traits<From>::dimension>
|
||||
[[nodiscard]] constexpr auto quantity_cast(const From& q)
|
||||
template<Dimension ToD, typename D, typename U, typename Rep>
|
||||
requires equivalent<ToD, D>
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
||||
{
|
||||
return quantity_cast<quantity<ToD, dimension_unit<ToD>, typename quantity_traits<From>::rep>>(q);
|
||||
return quantity_cast<quantity<ToD, dimension_unit<ToD>, Rep>>(q);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -170,11 +162,11 @@ template<Dimension ToD, QuantityLike From>
|
||||
*
|
||||
* @tparam ToU a unit type to use for a target quantity
|
||||
*/
|
||||
template<Unit ToU, QuantityLike From>
|
||||
requires UnitOf<ToU, typename quantity_traits<From>::dimension>
|
||||
[[nodiscard]] constexpr auto quantity_cast(const From& q)
|
||||
template<Unit ToU, typename D, typename U, typename Rep>
|
||||
requires UnitOf<ToU, D>
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
||||
{
|
||||
return quantity_cast<quantity<typename quantity_traits<From>::dimension, ToU, typename quantity_traits<From>::rep>>(q);
|
||||
return quantity_cast<quantity<D, ToU, Rep>>(q);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -193,11 +185,11 @@ template<Unit ToU, QuantityLike From>
|
||||
* @tparam ToD a dimension type to use for a target quantity
|
||||
* @tparam ToU a unit type to use for a target quantity
|
||||
*/
|
||||
template<Dimension ToD, Unit ToU, QuantityLike From>
|
||||
requires equivalent<ToD, typename quantity_traits<From>::dimension> && UnitOf<ToU, ToD>
|
||||
[[nodiscard]] constexpr auto quantity_cast(const From& q)
|
||||
template<Dimension ToD, Unit ToU, typename D, typename U, typename Rep>
|
||||
requires equivalent<ToD, D> && UnitOf<ToU, ToD>
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
||||
{
|
||||
return quantity_cast<quantity<ToD, ToU, typename quantity_traits<From>::rep>>(q);
|
||||
return quantity_cast<quantity<ToD, ToU, Rep>>(q);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -212,11 +204,10 @@ template<Dimension ToD, Unit ToU, QuantityLike From>
|
||||
*
|
||||
* @tparam ToRep a representation type to use for a target quantity
|
||||
*/
|
||||
template<QuantityValue ToRep, QuantityLike From>
|
||||
requires scalable_with_<typename quantity_traits<From>::rep, ToRep>
|
||||
[[nodiscard]] constexpr auto quantity_cast(const From& q)
|
||||
template<QuantityValue ToRep, typename D, typename U, scalable_with_<ToRep> Rep>
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
||||
{
|
||||
return quantity_cast<quantity<typename quantity_traits<From>::dimension, typename quantity_traits<From>::unit, ToRep>>(q);
|
||||
return quantity_cast<quantity<D, U, ToRep>>(q);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -37,14 +37,11 @@ static_assert(std::constructible_from<si::time<si::second, std::int64_t>, std::c
|
||||
static_assert(!std::convertible_to<std::chrono::seconds, si::time<si::second, std::int64_t>>);
|
||||
static_assert(std::constructible_from<si::time<si::second, std::int64_t>, std::chrono::seconds>);
|
||||
static_assert(!std::convertible_to<std::chrono::seconds, si::time<si::second, std::int64_t>>);
|
||||
static_assert(std::constructible_from<si::time<si::second, std::int64_t>, std::chrono::hours>);
|
||||
static_assert(!std::constructible_from<si::time<si::second, std::int64_t>, std::chrono::hours>);
|
||||
static_assert(!std::convertible_to<std::chrono::hours, si::time<si::second, std::int64_t>>);
|
||||
static_assert(!std::constructible_from<si::time<si::hour, std::int64_t>, std::chrono::seconds>);
|
||||
static_assert(!std::convertible_to<std::chrono::seconds, si::time<si::hour, std::int64_t>>);
|
||||
|
||||
// quantity_cast
|
||||
static_assert(quantity_cast<si::time<si::hour, int>>(7200s).count() == 2);
|
||||
|
||||
// CTAD
|
||||
static_assert(std::is_same_v<decltype(quantity{1s}), si::time<si::second, std::int64_t>>);
|
||||
static_assert(std::is_same_v<decltype(quantity{1h}), si::time<si::hour, std::int64_t>>);
|
||||
|
@ -118,7 +118,7 @@ static_assert(!QuantityPoint<std::chrono::seconds>);
|
||||
|
||||
static_assert(QuantityLike<std::chrono::seconds>);
|
||||
static_assert(QuantityLike<std::chrono::hours>);
|
||||
static_assert(QuantityLike<si::time<si::second>>);
|
||||
static_assert(!QuantityLike<si::time<si::second>>);
|
||||
static_assert(!QuantityLike<int>);
|
||||
|
||||
// WrappedQuantity
|
||||
|
Reference in New Issue
Block a user