refactor: RepresentationOf concept usage added + quantity concepts cleanup

This commit is contained in:
Mateusz Pusz
2022-12-20 17:22:51 +01:00
parent 6028f182d3
commit c51f6a9d29
3 changed files with 67 additions and 194 deletions

View File

@@ -36,18 +36,12 @@ UNITS_DIAGNOSTIC_IGNORE_LOSS_OF_DATA
namespace units {
template<Reference auto R, Representation Rep>
template<Reference auto R, RepresentationOf<R.quantity_spec.character> Rep>
class quantity;
// template<PointOrigin O, UnitOf<typename O::dimension> U, Representation Rep>
// class quantity_point;
// template<Kind K, UnitOf<typename K::dimension> U, Representation Rep>
// class quantity_kind;
// template<PointKind PK, UnitOf<typename PK::dimension> U, Representation Rep>
// class quantity_point_kind;
/**
* @brief Explicit cast of a quantity
*
@@ -180,7 +174,7 @@ template<Unit auto ToU, auto R, typename Rep>
* @tparam ToRep a representation type to use for a target quantity
*/
template<Representation ToRep, auto R, typename Rep>
requires std::constructible_from<ToRep, Rep>
requires RepresentationOf<ToRep, R.quantity_spec.character> && std::constructible_from<ToRep, Rep>
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
{
return quantity_cast<quantity<R, ToRep>>(q);
@@ -240,128 +234,6 @@ template<Representation ToRep, auto R, typename Rep>
// return quantity_point_cast<quantity_point<rebind_point_origin_dimension<O, ToD>, ToU, Rep>>(q);
// }
// /**
// * @brief Explicit cast of a quantity kind
// *
// * Implicit conversions between quantity 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) kind type to cast to or anything that works for quantity_cast. For example:
// *
// * auto q1 = units::quantity_kind_cast<decltype(ns::width{1 * m})>(quantity_kind{ns::width{1 * mm});
// * auto q1 = units::quantity_kind_cast<ns::height_kind>(ns::width{1 * m});
// * auto q1 = units::quantity_kind_cast<units::isq::si::length<units::isq::si::metre>>(ns::width{1 * mm});
// * auto q1 = units::quantity_kind_cast<units::isq::si::dim_acceleration>(ns::rate_of_climb{200 * Gal});
// * auto q1 = units::quantity_kind_cast<units::isq::si::metre>(ns::width{1 * mm});
// * auto q1 = units::quantity_kind_cast<int>(ns::width{1.0 * mm});
// *
// * @tparam CastSpec a target (quantity) kind type to cast to or anything that works for quantity_cast
// */
// template<typename CastSpec, typename K, typename U, typename Rep>
// [[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind<K, U, Rep>& qk)
// requires requires {
// requires is_specialization_of<CastSpec, quantity_kind>;
// requires requires { quantity_cast<typename CastSpec::quantity_type>(qk.common()); };
// } || requires {
// requires Kind<CastSpec>;
// requires UnitOf<U, typename CastSpec::dimension>;
// } || requires { quantity_cast<CastSpec>(qk.common()); } // TODO: Simplify when Clang catches up.
// {
// if constexpr (is_specialization_of<CastSpec, quantity_kind>)
// return CastSpec(quantity_cast<typename CastSpec::quantity_type>(qk.common()));
// else if constexpr (Kind<CastSpec>)
// return quantity_kind<CastSpec, U, Rep>(qk.common());
// else {
// auto q{quantity_cast<CastSpec>(qk.common())};
// using Q = decltype(q);
// return quantity_kind<K, typename Q::unit, typename Q::rep>(static_cast<Q&&>(q));
// }
// }
// /**
// * @brief Explicit cast of a quantity kind
// *
// * Implicit conversions between quantity 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 both the target kind and unit to cast to. For example:
// *
// * auto q1 = units::quantity_kind_cast<ns::height_kind, units::isq::si::kilometre>(w);
// *
// * @note This cast is especially useful when working with quantity kinds of unknown kind.
// *
// * @tparam ToK the kind type to use for the target quantity
// * @tparam ToU the unit type to use for the target quantity
// */
// template<Kind ToK, Unit ToU, typename K, typename U, typename Rep>
// requires equivalent<typename ToK::dimension, typename K::dimension> && UnitOf<ToU, typename ToK::dimension>
// [[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind<K, U, Rep>& qk)
// {
// return quantity_kind_cast<quantity_kind<ToK, ToU, Rep>>(qk);
// }
// /**
// * @brief Explicit cast of a quantity point kind
// *
// * 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
// * 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::width{1 * m})>(ns::x_coordinate{1 * mm});
// * auto q1 = units::quantity_point_kind_cast<ns::y_coordinate_kind>(ns::x_coordinate{1 * m});
// * auto q1 = units::quantity_point_kind_cast<ns::height_kind>(ns::x_coordinate{1 * m});
// * auto q1 = units::quantity_point_kind_cast<units::isq::si::length<units::isq::si::metre>>(ns::x_coordinate{1 *
// mm});
// * auto q1 =
// units::quantity_point_kind_cast<units::isq::si::dim_acceleration>(quantity_point_kind(ns::rate_of_climb{200
// * * Gal})); auto q1 = units::quantity_point_kind_cast<units::isq::si::metre>(ns::x_coordinate{1 * mm}); auto q1 =
// * units::quantity_point_kind_cast<int>(ns::x_coordinate{1.0 * mm});
// *
// * @tparam CastSpec a target (quantity) point kind type to cast to or anything that works for quantity_kind_cast
// */
// template<typename CastSpec, typename PK, typename U, typename Rep>
// [[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind<PK, U, Rep>& qpk)
// requires requires {
// requires is_specialization_of<CastSpec, quantity_point_kind>;
// requires requires { quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative()); };
// requires equivalent<typename PK::origin, typename CastSpec::point_kind_type::origin>;
// } || requires { requires PointKind<CastSpec> && UnitOf<U, typename CastSpec::dimension>; } ||
// requires { quantity_kind_cast<CastSpec>(qpk.relative()); } // TODO: Simplify when Clang catches up.
// {
// if constexpr (is_specialization_of<CastSpec, quantity_point_kind>)
// return CastSpec(quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative()));
// else if constexpr (PointKind<CastSpec>)
// return quantity_point_kind(quantity_kind_cast<typename CastSpec::base_kind>(qpk.relative()));
// else
// return quantity_point_kind(quantity_kind_cast<CastSpec>(qpk.relative()));
// }
// /**
// * @brief Explicit cast of a quantity point kind
// *
// * 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 both the target point kind and unit to cast to. For example:
// *
// * auto q1 = units::quantity_point_kind_cast<ns::y_coordinate_kind, units::isq::si::kilometre>(x);
// *
// * @note This cast is especially useful when working with quantity point kinds of unknown point kind.
// *
// * @tparam ToPK the point kind type to use for the target quantity
// * @tparam ToU the unit type to use for the target quantity
// */
// template<PointKind ToPK, Unit ToU, typename PK, typename U, typename Rep>
// requires equivalent<typename ToPK::dimension, typename PK::dimension> && UnitOf<ToU, typename ToPK::dimension>
// [[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind<PK, U, Rep>& qpk)
// {
// return quantity_point_kind_cast<quantity_point_kind<ToPK, ToU, Rep>>(qpk);
// }
} // namespace units
UNITS_DIAGNOSTIC_POP

View File

@@ -126,4 +126,17 @@ concept weak_quantity_of = Quantity<Q> &&
(Reference<std::remove_const_t<decltype(V)>> && Q::dimension == V.dimension &&
Q::unit == V.unit));
template<typename T>
concept quantity_like = requires(T q) {
quantity_like_traits<T>::dimension;
quantity_like_traits<T>::unit;
typename quantity_like_traits<T>::rep;
requires Dimension<std::remove_const_t<decltype(quantity_like_traits<T>::dimension)>>;
requires Unit<std::remove_const_t<decltype(quantity_like_traits<T>::unit)>>;
requires Representation<typename quantity_like_traits<T>::rep>;
{
quantity_like_traits<T>::number(q)
} -> std::convertible_to<typename quantity_like_traits<T>::rep>;
};
} // namespace units

View File

@@ -47,18 +47,13 @@ inline constexpr auto make_quantity = [](Representation auto&& v) {
};
template<typename T>
concept quantity_one = Quantity<T> && (T::dimension == dimension_one) && (T::unit == one);
concept quantity_one = quantity_of<T, dimensionless[one]>;
} // namespace detail
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 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>));
std::constructible_from<T, Arg> && (treat_as_floating_point<T> || !treat_as_floating_point<Arg>);
// QFrom ratio is an exact multiple of QTo
template<typename QFrom, typename QTo>
@@ -69,32 +64,14 @@ concept harmonic_ = // exposition only
template<typename QFrom, typename QTo>
concept quantity_convertible_to_ = // exposition only
Quantity<QFrom> && Quantity<QTo> && requires(QFrom q) { quantity_cast<QTo>(q); } &&
(floating_point_<QTo> || (!floating_point_<QFrom> && harmonic_<QFrom, QTo>));
(treat_as_floating_point<typename QTo::rep> ||
(!treat_as_floating_point<typename QFrom::rep> && 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<quantity_character Ch, typename Func, typename T, typename U>
concept invoke_result_of_ =
std::regular_invocable<Func, T, U> && RepresentationOf<std::invoke_result_t<Func, T, U>, Ch>;
template<typename T, typename Func, typename U, typename 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>;
template<typename T>
concept QuantityLike = requires(T q) {
quantity_like_traits<T>::dimension;
quantity_like_traits<T>::unit;
typename quantity_like_traits<T>::rep;
requires Dimension<std::remove_const_t<decltype(quantity_like_traits<T>::dimension)>>;
requires Unit<std::remove_const_t<decltype(quantity_like_traits<T>::unit)>>;
requires Representation<typename quantity_like_traits<T>::rep>;
{
quantity_like_traits<T>::number(q)
} -> std::convertible_to<typename quantity_like_traits<T>::rep>;
};
template<QuantityLike Q>
template<quantity_like Q>
using quantity_like_type = quantity<reference<quantity_like_traits<Q>::dimension, quantity_like_traits<Q>::unit>{},
typename quantity_like_traits<Q>::rep>;
@@ -108,7 +85,7 @@ using quantity_like_type = quantity<reference<quantity_like_traits<Q>::dimension
* @tparam U a measurement unit of the quantity
* @tparam Rep a type to be used to represent values of a quantity
*/
template<Reference auto R, Representation Rep = double>
template<Reference auto R, RepresentationOf<R.quantity_spec.character> Rep = double>
class quantity {
Rep number_;
public:
@@ -160,7 +137,7 @@ public:
{
}
template<QuantityLike Q>
template<quantity_like Q>
requires quantity_convertible_to_<quantity_like_type<Q>, quantity>
constexpr explicit quantity(const Q& q) : quantity(quantity_like_type<Q>(quantity_like_traits<Q>::number(q)))
{
@@ -315,7 +292,7 @@ public:
template<typename Rep2>
constexpr quantity& operator%=(const Rep2& rhs)
requires(!floating_point_<rep>) && (!floating_point_<Rep2>) && requires(rep a, const Rep2 b) {
requires(!treat_as_floating_point<rep>) && (!treat_as_floating_point<Rep2>) && requires(rep a, const Rep2 b) {
{
a %= b
} -> std::same_as<rep&>;
@@ -328,7 +305,8 @@ public:
template<detail::quantity_one Q>
constexpr quantity& operator%=(const Q& rhs)
requires(!floating_point_<rep>) && (!floating_point_<typename Q::rep>) && requires(rep a, const typename Q::rep b) {
requires(!treat_as_floating_point<rep>) && (!treat_as_floating_point<typename Q::rep>) &&
requires(rep a, const typename Q::rep b) {
{
a %= b
} -> std::same_as<rep&>;
@@ -340,7 +318,7 @@ public:
}
constexpr quantity& operator%=(const quantity& q)
requires(!floating_point_<rep>) && requires(rep a, rep b) {
requires(!treat_as_floating_point<rep>) && requires(rep a, rep b) {
{
a %= b
} -> std::same_as<rep&>;
@@ -358,17 +336,18 @@ public:
requires requires { // TODO: Simplify when Clang catches up.
requires !Quantity<Value>;
requires unit == ::units::one;
requires invoke_result_convertible_to_<rep, std::plus<>, rep, Value>;
requires invoke_result_of_<R.quantity_spec.character, std::plus<>, rep, Value>;
}
{
return ::units::quantity(lhs.number() + rhs);
}
template<typename Value>
[[nodiscard]] friend constexpr Quantity auto operator+(const Value& lhs, const quantity& rhs)
requires requires { // TODO: Simplify when Clang catches up.
requires !Quantity<Value>;
requires unit == ::units::one;
requires invoke_result_convertible_to_<rep, std::plus<>, Value, rep>;
requires invoke_result_of_<R.quantity_spec.character, std::plus<>, Value, rep>;
}
{
return ::units::quantity(lhs + rhs.number());
@@ -379,24 +358,25 @@ public:
requires requires { // TODO: Simplify when Clang catches up.
requires !Quantity<Value>;
requires unit == ::units::one;
requires invoke_result_convertible_to_<rep, std::minus<>, rep, Value>;
requires invoke_result_of_<R.quantity_spec.character, std::minus<>, rep, Value>;
}
{
return ::units::quantity(lhs.number() - rhs);
}
template<typename Value>
[[nodiscard]] friend constexpr Quantity auto operator-(const Value& lhs, const quantity& rhs)
requires requires { // TODO: Simplify when Clang catches up.
requires !Quantity<Value>;
requires unit == ::units::one;
requires invoke_result_convertible_to_<rep, std::minus<>, Value, rep>;
requires invoke_result_of_<R.quantity_spec.character, std::minus<>, Value, rep>;
}
{
return ::units::quantity(lhs - rhs.number());
}
template<Representation Value>
requires invoke_result_convertible_to_<rep, std::multiplies<>, rep, const Value&>
requires invoke_result_of_<R.quantity_spec.character, std::multiplies<>, rep, const Value&>
[[nodiscard]] friend constexpr Quantity auto operator*(const quantity& q, const Value& v)
{
using ret = quantity<R, std::invoke_result_t<std::multiplies<>, rep, Value>>;
@@ -404,7 +384,7 @@ public:
}
template<Representation Value>
requires invoke_result_convertible_to_<rep, std::multiplies<>, const Value&, rep>
requires invoke_result_of_<R.quantity_spec.character, std::multiplies<>, const Value&, rep>
[[nodiscard]] friend constexpr Quantity auto operator*(const Value& v, const quantity& q)
{
using ret = quantity<R, std::invoke_result_t<std::multiplies<>, Value, rep>>;
@@ -412,7 +392,7 @@ public:
}
template<typename Value>
requires(!Quantity<Value>) && invoke_result_convertible_to_<rep, std::divides<>, rep, const Value&>
requires(!Quantity<Value>) && invoke_result_of_<R.quantity_spec.character, std::divides<>, rep, const Value&>
[[nodiscard]] friend constexpr Quantity auto operator/(const quantity& q, const Value& v)
{
gsl_ExpectsAudit(v != quantity_values<Value>::zero());
@@ -421,15 +401,15 @@ public:
}
template<typename Value>
requires(!Quantity<Value>) && invoke_result_convertible_to_<rep, std::divides<>, const Value&, rep>
requires(!Quantity<Value>) && invoke_result_of_<R.quantity_spec.character, std::divides<>, const Value&, rep>
[[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q)
{
return detail::make_quantity<dimensionless[::units::one] / reference>(v / q.number());
}
template<typename Value>
requires(!Quantity<Value>) && (!floating_point_<rep>) &&
(!floating_point_<Value>) && invoke_result_convertible_to_<rep, std::modulus<>, rep, const Value&>
requires(!Quantity<Value>) && (!treat_as_floating_point<rep>) && (!treat_as_floating_point<Value>) &&
invoke_result_of_<R.quantity_spec.character, std::modulus<>, rep, const Value&>
[[nodiscard]] friend constexpr Quantity auto operator%(const quantity& q, const Value& v)
{
gsl_ExpectsAudit(v != quantity_values<Value>::zero());
@@ -438,7 +418,7 @@ public:
}
[[nodiscard]] friend constexpr Quantity auto operator%(const quantity& lhs, const quantity& rhs)
requires(!floating_point_<rep>) && invoke_result_convertible_to_<rep, std::modulus<>, rep, rep>
requires(!treat_as_floating_point<rep>) && invoke_result_of_<R.quantity_spec.character, std::modulus<>, rep, rep>
{
gsl_ExpectsAudit(rhs.number() != quantity_values<rep>::zero());
using ret = quantity<R, std::invoke_result_t<std::modulus<>, rep, rep>>;
@@ -447,7 +427,7 @@ public:
[[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity& rhs)
requires std::three_way_comparable<rep>
#if UNITS_COMP_GCC == 10 && UNITS_COMP_GCC_MINOR >= 2
#if __cpp_impl_three_way_comparison
= default;
#else
{
@@ -462,17 +442,23 @@ public:
template<auto R, typename Rep>
explicit(false) quantity(quantity<R, Rep>) -> quantity<R, Rep>;
template<Representation Rep>
template<RepresentationOf<units::quantity_character::scalar> Rep>
explicit(false) quantity(Rep) -> quantity<dimensionless[one], Rep>;
template<QuantityLike Q>
#if !UNITS_COMP_CLANG || UNITS_COMP_CLANG > 16
template<auto R, RepresentationOf<R.quantity_spec.character> Rep>
explicit(false) quantity(Rep&&) -> quantity<R, Rep>;
#endif
template<quantity_like Q>
explicit quantity(Q) -> quantity<reference<quantity_like_traits<Q>::dimension, quantity_like_traits<Q>::unit>{},
typename quantity_like_traits<Q>::rep>;
// non-member binary operators
template<Quantity Q1, Quantity Q2>
requires(interconvertible(Q1::reference, Q2::reference)) &&
quantity_value_for_<std::plus<>, typename Q1::rep, typename Q2::rep>
invoke_result_of_<common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character, std::plus<>,
typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs)
{
constexpr auto ref = common_reference(Q1::reference, Q2::reference);
@@ -482,7 +468,8 @@ template<Quantity Q1, Quantity Q2>
template<Quantity Q1, Quantity Q2>
requires(interconvertible(Q1::reference, Q2::reference)) &&
quantity_value_for_<std::minus<>, typename Q1::rep, typename Q2::rep>
invoke_result_of_<common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character, std::minus<>,
typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs)
{
constexpr auto ref = common_reference(Q1::reference, Q2::reference);
@@ -491,14 +478,16 @@ template<Quantity Q1, Quantity Q2>
}
template<Quantity Q1, Quantity Q2>
requires quantity_value_for_<std::multiplies<>, typename Q1::rep, typename Q2::rep>
requires invoke_result_of_<(Q1::quantity_spec * Q2::quantity_spec).character, std::multiplies<>, typename Q1::rep,
typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator*(const Q1& lhs, const Q2& rhs)
{
return detail::make_quantity<Q1::reference * Q2::reference>(lhs.number() * rhs.number());
}
template<Quantity Q1, Quantity Q2>
requires quantity_value_for_<std::divides<>, typename Q1::rep, typename Q2::rep>
requires invoke_result_of_<(Q1::quantity_spec / Q2::quantity_spec).character, std::divides<>, typename Q1::rep,
typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator/(const Q1& lhs, const Q2& rhs)
{
gsl_ExpectsAudit(rhs.number() != quantity_values<typename Q2::rep>::zero());
@@ -506,14 +495,13 @@ template<Quantity Q1, Quantity Q2>
}
template<Quantity Q1, Quantity Q2>
requires(!floating_point_<typename Q1::rep>) && (!floating_point_<typename Q2::rep>) &&
(interconvertible(Q1::reference, Q2::reference) || quantity_of<Q2, dimension_one>) &&
quantity_value_for_<std::modulus<>, typename Q1::rep, typename Q2::rep>
requires(!treat_as_floating_point<typename Q1::rep>) && (!treat_as_floating_point<typename Q2::rep>) &&
(interconvertible(Q1::reference, Q2::reference) || quantity_of<Q2, dimensionless>) &&
invoke_result_of_<Q1::reference.character, std::modulus<>, typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator%(const Q1& lhs, const Q2& rhs)
{
gsl_ExpectsAudit(rhs.number() != quantity_values<typename Q2::rep>::zero());
using ret = quantity<reference<Q1::dimension, Q1::unit>{},
std::invoke_result_t<std::modulus<>, typename Q1::rep, typename Q2::rep>>;
using ret = quantity<Q1::reference, std::invoke_result_t<std::modulus<>, typename Q1::rep, typename Q2::rep>>;
return ret(lhs.number() % rhs.number());
}