refactor: reference now takes NTTPs rather then types

Even though it makes error logs a bit more obscure it simplifies coding a lot.
This commit is contained in:
Mateusz Pusz committed 2022-11-08 10:37:41 -10:00
1 parent 64d6193e74
commit 3d1f339115
8 files changed
+116 -101

No files matched your search

+12 -2
View File
@@ -32,16 +32,26 @@
namespace units {
template<Dimension D, Unit U>
template<Dimension auto D, Unit auto U>
struct reference;
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_reference = false;
template<Dimension auto D, Unit auto U>
inline constexpr bool is_specialization_of_reference<reference<D, U>> = true;
} // namespace detail
/**
* @brief A concept matching all references in the library.
*
* Satisfied by all specializations of @c reference.
*/
template<typename T>
concept Reference = is_specialization_of<T, reference>;
concept Reference = detail::is_specialization_of_reference<T>;
namespace detail {
@@ -91,7 +91,7 @@ struct quantity_values {
/**
* @brief Provides support for external quantity-like types
*
* The type trait should provide the following nested type aliases: @c dimension, @c unit, @c rep,
* The type trait should provide the following nested values @c dimension, @c unit and type alias @c rep,
* and a static member function @c number(T) that will return the raw value of the quantity.
*
* Usage example can be found in @c units/chrono.h header file.
+10 -10
View File
@@ -63,7 +63,7 @@ inline constexpr bool is_valid_unit_for_dimension = false;
template<typename U, typename Dim>
concept valid_unit_for_dimension = Unit<U> && Dimension<Dim> && detail::is_valid_unit_for_dimension<U{}, Dim{}>;
template<Dimension D, Unit U>
template<Dimension auto D, Unit auto U>
struct reference;
/**
@@ -104,13 +104,13 @@ struct base_dimension {
#ifdef __cpp_explicit_this_parameter
template<typename Self, valid_unit_for_dimension<Self> U>
[[nodiscard]] constexpr reference<Self, U> operator[](this const Self, U)
[[nodiscard]] constexpr auto operator[](this const Self, U)
#else
template<valid_unit_for_dimension<Self> U>
[[nodiscard]] constexpr reference<Self, U> operator[](U) const
[[nodiscard]] constexpr auto operator[](U) const
#endif
{
return {};
return reference<Self{}, U{}>{};
}
};
@@ -213,9 +213,9 @@ template<DerivedDimensionSpec... Ds>
struct derived_dimension : detail::derived_dimension_impl<Ds...> {
template<typename Self, Unit U>
requires valid_unit_for_dimension<U, Self> || (sizeof...(Ds) == 0 && convertible(U{}, one))
[[nodiscard]] constexpr reference<Self, U> operator[](this const Self, U)
[[nodiscard]] constexpr auto operator[](this const Self, U)
{
return {};
return reference<Self{}, U{}>{};
}
};
@@ -228,9 +228,9 @@ template<DerivedDimensionSpec... Ds>
struct derived_dimension<Ds...> : detail::derived_dimension_impl<Ds...> {
template<Unit U>
requires valid_unit_for_dimension<U, derived_dimension> || (sizeof...(Ds) == 0 && convertible(U{}, one))
[[nodiscard]] constexpr reference<derived_dimension, U> operator[](U) const
[[nodiscard]] constexpr auto operator[](U) const
{
return {};
return reference<derived_dimension{}, U{}>{};
}
};
@@ -239,9 +239,9 @@ struct derived_dimension<Self, D> : D {
template<Unit U>
requires valid_unit_for_dimension<U, Self> ||
(convertible(derived_dimension{}, derived_dimension<>{}) && convertible(U{}, one))
[[nodiscard]] constexpr reference<Self, U> operator[](U) const
[[nodiscard]] constexpr auto operator[](U) const
{
return {};
return reference<Self{}, U{}>{};
}
};
+32 -30
View File
@@ -85,11 +85,11 @@ concept have_quantity_for_ = Quantity<Q> && (!Quantity<V>) && quantity_value_for
template<typename T>
concept QuantityLike = requires(T q) {
typename quantity_like_traits<T>::dimension;
typename quantity_like_traits<T>::unit;
quantity_like_traits<T>::dimension;
quantity_like_traits<T>::unit;
typename quantity_like_traits<T>::rep;
requires Dimension<typename quantity_like_traits<T>::dimension>;
requires Unit<typename quantity_like_traits<T>::unit>;
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)
@@ -97,9 +97,8 @@ concept QuantityLike = requires(T q) {
};
template<QuantityLike Q>
using quantity_like_type =
quantity<reference<typename quantity_like_traits<Q>::dimension, typename quantity_like_traits<Q>::unit>{},
typename quantity_like_traits<Q>::rep>;
using quantity_like_type = quantity<reference<quantity_like_traits<Q>::dimension, quantity_like_traits<Q>::unit>{},
typename quantity_like_traits<Q>::rep>;
/**
* @brief A quantity
@@ -352,42 +351,42 @@ public:
[[nodiscard]] friend constexpr Quantity auto operator+(const quantity& lhs, const Value& rhs)
requires requires { // TODO: Simplify when Clang catches up.
requires !Quantity<Value>;
requires is_same_v<unit, units::one>;
requires unit == ::units::one;
requires invoke_result_convertible_to_<rep, std::plus<>, rep, Value>;
}
{
return units::quantity(lhs.number() + rhs);
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 is_same_v<unit, units::one>;
requires unit == ::units::one;
requires invoke_result_convertible_to_<rep, std::plus<>, Value, rep>;
}
{
return units::quantity(lhs + rhs.number());
return ::units::quantity(lhs + rhs.number());
}
template<typename Value>
[[nodiscard]] friend constexpr Quantity auto operator-(const quantity& lhs, const Value& rhs)
requires requires { // TODO: Simplify when Clang catches up.
requires !Quantity<Value>;
requires is_same_v<unit, units::one>;
requires unit == ::units::one;
requires invoke_result_convertible_to_<rep, std::minus<>, rep, Value>;
}
{
return units::quantity(lhs.number() - rhs);
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 is_same_v<unit, units::one>;
requires unit == ::units::one;
requires invoke_result_convertible_to_<rep, std::minus<>, Value, rep>;
}
{
return units::quantity(lhs - rhs.number());
return ::units::quantity(lhs - rhs.number());
}
template<Representation Value>
@@ -466,13 +465,13 @@ template<Representation Rep>
explicit(false) quantity(Rep)->quantity<dimension_one[one], Rep>;
template<QuantityLike Q>
explicit quantity(Q)
-> quantity<reference<typename quantity_like_traits<Q>::dimension, typename quantity_like_traits<Q>::unit>{},
typename quantity_like_traits<Q>::rep>;
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, std::convertible_to<Q1> Q2>
requires(quantity_value_for_<std::plus<>, typename Q1::rep, typename Q2::rep>)
template<Quantity Q1, Quantity Q2>
requires(convertible(Q1::reference, Q2::reference)) &&
quantity_value_for_<std::plus<>, typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs)
{
using ref = std::common_type_t<decltype(Q1::reference), decltype(Q2::reference)>;
@@ -480,8 +479,9 @@ template<Quantity Q1, std::convertible_to<Q1> Q2>
return ret(ret(lhs).number() + ret(rhs).number());
}
template<Quantity Q1, std::convertible_to<Q1> Q2>
requires(quantity_value_for_<std::minus<>, typename Q1::rep, typename Q2::rep>)
template<Quantity Q1, Quantity Q2>
requires(convertible(Q1::reference, Q2::reference)) &&
quantity_value_for_<std::minus<>, typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs)
{
using ref = std::common_type_t<decltype(Q1::reference), decltype(Q2::reference)>;
@@ -506,26 +506,28 @@ template<Quantity Q1, Quantity Q2>
template<Quantity Q1, Quantity Q2>
requires(!floating_point_<typename Q1::rep>) && (!floating_point_<typename Q2::rep>) &&
(std::convertible_to<Q2, Q1> || quantity_of<Q2, dimension_one>) &&
(quantity_value_for_<std::modulus<>, typename Q1::rep, typename Q2::rep>)
(convertible(Q1::reference, Q2::reference) || quantity_of<Q2, dimension_one>) &&
quantity_value_for_<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<typename Q1::dimension, typename Q1::unit>{},
using ret = quantity<reference<Q1::dimension, Q1::unit>{},
std::invoke_result_t<std::modulus<>, typename Q1::rep, typename Q2::rep>>;
return ret(lhs.number() % rhs.number());
}
template<Quantity Q1, std::convertible_to<Q1> Q2>
requires std::three_way_comparable_with<typename Q1::rep, typename Q2::rep>
template<Quantity Q1, Quantity Q2>
requires(convertible(Q1::reference, Q2::reference)) &&
std::three_way_comparable_with<typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr auto operator<=>(const Q1& lhs, const Q2& rhs)
{
using ref = std::common_type_t<decltype(Q1::reference), decltype(Q2::reference)>;
return quantity_cast<ref>(lhs).number() <=> quantity_cast<ref>(rhs).number();
return quantity_cast<ref{}>(lhs).number() <=> quantity_cast<ref{}>(rhs).number();
}
template<Quantity Q1, std::convertible_to<Q1> Q2>
requires std::equality_comparable_with<typename Q1::rep, typename Q2::rep>
template<Quantity Q1, Quantity Q2>
requires(convertible(Q1::reference, Q2::reference)) &&
std::equality_comparable_with<typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr bool operator==(const Q1& lhs, const Q2& rhs)
{
using ref = std::common_type_t<decltype(Q1::reference), decltype(Q2::reference)>;
+2 -2
View File
@@ -145,7 +145,7 @@ template<Dimension auto ToD, auto R, typename Rep>
requires(convertible(ToD, R.dimension))
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
{
constexpr reference<std::remove_const_t<decltype(ToD)>, typename quantity<R, Rep>::unit_t> r;
constexpr reference<ToD, quantity<R, Rep>::unit> r;
return quantity_cast<quantity<r, Rep>>(q);
}
@@ -165,7 +165,7 @@ template<Unit auto ToU, auto R, typename Rep>
requires(convertible(ToU, R.unit))
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
{
constexpr reference<typename quantity<R, Rep>::dimension_t, std::remove_const_t<decltype(ToU)>> r;
constexpr reference<quantity<R, Rep>::dimension, ToU> r;
return quantity_cast<quantity<r, Rep>>(q);
}
+8 -11
View File
@@ -67,31 +67,28 @@ namespace units {
* The following syntaxes are not allowed:
* `2 / s`, `km * 3`, `s / 4`, `70 * km / h`.
*/
template<Dimension D, Unit U>
template<Dimension auto D, Unit auto U>
struct reference {
static constexpr D dimension{};
static constexpr U unit{};
// static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = dimension::mag * unit::mag;
static constexpr auto dimension = D;
static constexpr auto unit = U;
};
// Reference
template<Magnitude M, Reference R>
[[nodiscard]] consteval reference<decltype(R::dimension), decltype(M{} * R::unit)> operator*(M, R)
[[nodiscard]] consteval reference<R::dimension, M{} * R::unit> operator*(M, R)
{
return {};
}
template<Reference R1, Reference R2>
[[nodiscard]] consteval reference<decltype(R1::dimension * R2::dimension), decltype(R1::unit * R2::unit)> operator*(R1,
R2)
[[nodiscard]] consteval reference<R1::dimension * R2::dimension, R1::unit * R2::unit> operator*(R1, R2)
{
return {};
}
template<Reference R1, Reference R2>
[[nodiscard]] consteval reference<decltype(R1::dimension / R2::dimension), decltype(R1::unit / R2::unit)> operator/(R1,
R2)
[[nodiscard]] consteval reference<R1::dimension / R2::dimension, R1::unit / R2::unit> operator/(R1, R2)
{
return {};
}
@@ -119,7 +116,7 @@ struct system_reference {
template<Unit U>
requires(convertible(coherent_unit, U{}))
[[nodiscard]] constexpr reference<std::remove_const_t<decltype(dimension)>, U> operator[](U) const
[[nodiscard]] constexpr reference<dimension, U{}> operator[](U) const
{
return {};
}
@@ -139,7 +136,7 @@ private:
using dim = common_type_t<remove_const_t<decltype(R1::dimension)>, remove_const_t<decltype(R2::dimension)>>;
using unit = common_type_t<remove_const_t<decltype(R1::unit)>, remove_const_t<decltype(R2::unit)>>;
public:
using type = units::reference<dim, unit>;
using type = units::reference<dim{}, unit{}>;
};
} // namespace std