feat: tag types-related operators are now hidden friends as well

Resolves #591
This commit is contained in:
Mateusz Pusz
2024-07-12 16:01:03 +02:00
parent b5e8f2afcd
commit 0010d868a7
11 changed files with 495 additions and 520 deletions

View File

@ -47,6 +47,53 @@
namespace mp_units {
template<detail::DerivedDimensionExpr... Expr>
struct derived_dimension;
MP_UNITS_EXPORT struct dimension_one;
namespace detail {
template<typename Lhs, typename Rhs>
struct base_dimension_less : std::bool_constant<(Lhs::symbol < Rhs::symbol)> {};
template<typename T1, typename T2>
using type_list_of_base_dimension_less = expr_less<T1, T2, base_dimension_less>;
template<DerivedDimensionExpr... Expr>
struct derived_dimension_impl : expr_fractions<is_dimension_one, Expr...> {};
template<auto Symbol>
[[nodiscard]] consteval std::true_type derived_from_the_same_base_dimension(const base_dimension<Symbol>&,
const base_dimension<Symbol>&)
{
return {};
}
[[nodiscard]] consteval std::false_type derived_from_the_same_base_dimension(...) { return {}; }
struct dimension_interface {
template<Dimension Lhs, Dimension Rhs>
[[nodiscard]] friend consteval Dimension auto operator*(Lhs, Rhs)
{
return expr_multiply<derived_dimension, struct dimension_one, type_list_of_base_dimension_less>(Lhs{}, Rhs{});
}
template<Dimension Lhs, Dimension Rhs>
[[nodiscard]] friend consteval Dimension auto operator/(Lhs, Rhs)
{
return expr_divide<derived_dimension, struct dimension_one, type_list_of_base_dimension_less>(Lhs{}, Rhs{});
}
template<Dimension Lhs, Dimension Rhs>
[[nodiscard]] friend consteval bool operator==(Lhs lhs, Rhs rhs)
{
return is_same_v<Lhs, Rhs> || derived_from_the_same_base_dimension(lhs, rhs);
}
};
} // namespace detail
/**
* @brief A dimension of a base quantity
*
@ -76,23 +123,10 @@ namespace mp_units {
* @tparam Symbol an unique identifier of the base dimension used to provide dimensional analysis support
*/
MP_UNITS_EXPORT template<symbol_text Symbol>
struct base_dimension {
struct base_dimension : detail::dimension_interface {
static constexpr auto symbol = Symbol; ///< Unique base dimension identifier
};
namespace detail {
template<BaseDimension Lhs, BaseDimension Rhs>
struct base_dimension_less : std::bool_constant<(Lhs::symbol < Rhs::symbol)> {};
template<typename T1, typename T2>
using type_list_of_base_dimension_less = expr_less<T1, T2, base_dimension_less>;
template<detail::DerivedDimensionExpr... Expr>
struct derived_dimension_impl : detail::expr_fractions<detail::is_dimension_one, Expr...> {};
} // namespace detail
/**
* @brief A dimension of a derived quantity
*
@ -136,7 +170,7 @@ struct derived_dimension_impl : detail::expr_fractions<detail::is_dimension_one,
* instantiate this type automatically based on the dimensional arithmetic equation provided by the user.
*/
template<detail::DerivedDimensionExpr... Expr>
struct derived_dimension final : detail::derived_dimension_impl<Expr...> {};
struct derived_dimension final : detail::dimension_interface, detail::derived_dimension_impl<Expr...> {};
/**
* @brief Dimension one
@ -145,7 +179,9 @@ struct derived_dimension final : detail::derived_dimension_impl<Expr...> {};
* dimensions are zero. It is a dimension of a quantity of dimension one also known as
* "dimensionless".
*/
MP_UNITS_EXPORT inline constexpr struct dimension_one final : detail::derived_dimension_impl<> {
MP_UNITS_EXPORT inline constexpr struct dimension_one final :
detail::dimension_interface,
detail::derived_dimension_impl<> {
} dimension_one;
namespace detail {
@ -155,43 +191,8 @@ struct is_dimension_one<struct dimension_one> : std::true_type {};
} // namespace detail
// Operators
MP_UNITS_EXPORT template<Dimension Lhs, Dimension Rhs>
[[nodiscard]] consteval Dimension auto operator*(Lhs, Rhs)
{
return detail::expr_multiply<derived_dimension, struct dimension_one, detail::type_list_of_base_dimension_less>(
Lhs{}, Rhs{});
}
MP_UNITS_EXPORT template<Dimension Lhs, Dimension Rhs>
[[nodiscard]] consteval Dimension auto operator/(Lhs, Rhs)
{
return detail::expr_divide<derived_dimension, struct dimension_one, detail::type_list_of_base_dimension_less>(Lhs{},
Rhs{});
}
namespace detail {
template<auto Symbol>
[[nodiscard]] consteval std::true_type derived_from_the_same_base_dimension(const base_dimension<Symbol>&,
const base_dimension<Symbol>&)
{
return {};
}
[[nodiscard]] consteval std::false_type derived_from_the_same_base_dimension(...) { return {}; }
} // namespace detail
MP_UNITS_EXPORT_BEGIN
template<Dimension Lhs, Dimension Rhs>
[[nodiscard]] consteval bool operator==(Lhs lhs, Rhs rhs)
{
return is_same_v<Lhs, Rhs> || detail::derived_from_the_same_base_dimension(lhs, rhs);
}
[[nodiscard]] consteval Dimension auto inverse(Dimension auto d) { return dimension_one / d; }
/**

View File

@ -30,6 +30,20 @@
namespace mp_units {
namespace detail {
struct dimension_interface;
}
/**
* @brief A concept matching all dimensions in the library.
*
* Satisfied by all dimension types in the library.
*/
MP_UNITS_EXPORT template<typename T>
concept Dimension = std::derived_from<T, detail::dimension_interface> && std::is_final_v<T>;
MP_UNITS_EXPORT template<symbol_text Symbol>
struct base_dimension;
@ -48,7 +62,7 @@ inline constexpr bool is_derived_from_specialization_of_base_dimension =
* Satisfied by all dimension types derived from a specialization of `base_dimension`.
*/
template<typename T>
concept BaseDimension = is_derived_from_specialization_of_base_dimension<T> && std::is_final_v<T>;
concept BaseDimension = Dimension<T> && is_derived_from_specialization_of_base_dimension<T>;
template<typename T>
struct is_dimension_one : std::false_type {};
@ -70,40 +84,11 @@ template<typename T>
concept DerivedDimensionExpr =
BaseDimension<T> || is_dimension_one<T>::value || is_power_of_dim<T> || is_per_of_dims<T>;
} // namespace detail
template<detail::DerivedDimensionExpr... Expr>
struct derived_dimension;
namespace detail {
/**
* @brief A concept matching all derived dimensions in the library.
*
* Satisfied by all dimension types being a specialization of `derived_dimension` or
* being the `dimension_one`.
*/
template<typename T>
concept DerivedDimension =
(is_specialization_of<T, derived_dimension> || is_dimension_one<T>::value) && std::is_final_v<T>;
} // namespace detail
/**
* @brief A concept matching all dimensions in the library.
*
* Satisfied by all dimension types for which either `BaseDimension<T>` or `DerivedDimension<T>` is `true`.
*/
MP_UNITS_EXPORT template<typename T>
concept Dimension = detail::BaseDimension<T> || detail::DerivedDimension<T>;
namespace detail {
template<auto D1, auto D2>
concept SameDimension =
Dimension<MP_UNITS_REMOVE_CONST(decltype(D1))> && Dimension<MP_UNITS_REMOVE_CONST(decltype(D2))> && (D1 == D2);
}
} // namespace detail
/**
* @brief A concept checking if the argument is of the same dimension.

View File

@ -185,6 +185,9 @@ inline constexpr bool is_specialization_of_power_v<power_v<V, Ints...>> = true;
template<typename T>
concept MagnitudeSpec = PowerVBase<T> || detail::is_specialization_of_power_v<T>;
template<MagnitudeSpec auto... Ms>
struct magnitude;
namespace detail {
template<MagnitudeSpec Element>
@ -464,123 +467,8 @@ namespace detail {
return is_rational(element) && get_exponent(element).num > 0;
}
} // namespace detail
/**
* @brief A representation for positive real numbers which optimizes taking products and rational powers.
*
* Magnitudes can be treated as values. Each type encodes exactly one value. Users can multiply, divide, raise to
* rational powers, and compare for equality.
*/
template<MagnitudeSpec auto... Ms>
// requires detail::is_element_pack_valid<Ms...>
struct magnitude {
[[nodiscard]] friend consteval bool is_integral(const magnitude&)
{
using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec
return (is_integral(Ms) && ...);
}
[[nodiscard]] friend consteval bool is_rational(const magnitude&)
{
using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec
return (is_rational(Ms) && ...);
}
};
namespace detail {
template<auto... Ms>
void to_base_specialization_of_magnitude(const volatile magnitude<Ms...>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_magnitude =
requires(T* t) { to_base_specialization_of_magnitude(t); };
template<typename T>
requires is_derived_from_specialization_of_magnitude<T>
inline constexpr bool is_magnitude<T> = true;
template<auto... Ms>
inline constexpr bool is_specialization_of_magnitude<magnitude<Ms...>> = true;
} // namespace detail
/**
* @brief The value of a Magnitude in a desired type T.
*/
template<typename T, auto... Ms>
requires(is_integral(magnitude<Ms...>{})) || treat_as_floating_point<T>
[[nodiscard]] consteval T get_value(const magnitude<Ms...>&)
{
// Force the expression to be evaluated in a constexpr context, to catch, e.g., overflow.
constexpr T result = detail::checked_static_cast<T>((detail::compute_base_power<T>(Ms) * ... * T{1}));
return result;
}
MP_UNITS_EXPORT_BEGIN
/**
* @brief A convenient Magnitude constant for pi, which we can manipulate like a regular number.
*/
#if MP_UNITS_COMP_CLANG
inline constexpr struct mag_pi : magnitude<mag_value{std::numbers::pi_v<long double>}> {
} mag_pi;
#else
inline constexpr struct mag_pi : magnitude<std::numbers::pi_v<long double>> {
} mag_pi;
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude equality implementation.
template<Magnitude M1, Magnitude M2>
[[nodiscard]] consteval bool operator==(M1, M2)
{
return std::is_same_v<M1, M2>;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude rational powers implementation.
template<std::intmax_t Num, std::intmax_t Den = 1, auto... Ms>
[[nodiscard]] consteval auto pow(magnitude<Ms...>)
{
if constexpr (Num == 0) {
return magnitude<>{};
} else {
return magnitude<
detail::power_v_or_T<detail::get_base(Ms), detail::get_exponent(Ms) * detail::ratio{Num, Den}>()...>{};
}
}
template<auto... Ms>
[[nodiscard]] consteval auto sqrt(magnitude<Ms...> m)
{
return pow<1, 2>(m);
}
template<auto... Ms>
[[nodiscard]] consteval auto cbrt(magnitude<Ms...> m)
{
return pow<1, 3>(m);
}
MP_UNITS_EXPORT_END
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude product implementation.
namespace detail {
[[nodiscard]] consteval bool less(MagnitudeSpec auto lhs, MagnitudeSpec auto rhs)
{
using lhs_base_t = decltype(get_base_value(lhs));
@ -594,18 +482,8 @@ namespace detail {
return is_named_magnitude<lhs_base_t>;
}
} // namespace detail
MP_UNITS_EXPORT_BEGIN
// Base cases, for when either (or both) inputs are the identity.
[[nodiscard]] consteval Magnitude auto operator*(magnitude<>, magnitude<>) { return magnitude<>{}; }
[[nodiscard]] consteval Magnitude auto operator*(magnitude<>, Magnitude auto m) { return m; }
[[nodiscard]] consteval Magnitude auto operator*(Magnitude auto m, magnitude<>) { return m; }
// Recursive case for the product of any two non-identity Magnitudes.
template<auto H1, auto... T1, auto H2, auto... T2>
[[nodiscard]] consteval Magnitude auto operator*(magnitude<H1, T1...>, magnitude<H2, T2...>)
[[nodiscard]] consteval Magnitude auto multiply_impl(magnitude<H1, T1...>, magnitude<H2, T2...>)
{
using namespace detail;
@ -641,10 +519,123 @@ template<auto H1, auto... T1, auto H2, auto... T2>
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude quotient implementation.
} // namespace detail
[[nodiscard]] consteval auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1>(r); }
/**
* @brief A representation for positive real numbers which optimizes taking products and rational powers.
*
* Magnitudes can be treated as values. Each type encodes exactly one value. Users can multiply, divide, raise to
* rational powers, and compare for equality.
*/
template<MagnitudeSpec auto... Ms>
// requires detail::is_element_pack_valid<Ms...>
struct magnitude {
[[nodiscard]] friend consteval bool is_integral(const magnitude&)
{
using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec
return (is_integral(Ms) && ...);
}
[[nodiscard]] friend consteval bool is_rational(const magnitude&)
{
using namespace detail; // needed for recursive case when magnitudes are in the MagnitudeSpec
return (is_rational(Ms) && ...);
}
template<Magnitude M>
[[nodiscard]] friend consteval Magnitude auto operator*(magnitude m1, M m2)
{
if constexpr (sizeof...(Ms) == 0)
return m2;
else if constexpr (is_same_v<M, magnitude<>>)
return m1;
else
return detail::multiply_impl(m1, m2);
}
[[nodiscard]] friend consteval auto operator/(magnitude l, Magnitude auto r) { return l * pow<-1>(r); }
template<Magnitude M2>
[[nodiscard]] friend consteval bool operator==(magnitude, M2)
{
return std::is_same_v<magnitude, M2>;
}
/**
* @brief The value of a Magnitude in a desired type T.
*/
template<typename T>
requires((detail::is_integral(Ms) && ...)) || treat_as_floating_point<T>
[[nodiscard]] friend consteval T get_value(const magnitude&)
{
// Force the expression to be evaluated in a constexpr context, to catch, e.g., overflow.
constexpr T result = detail::checked_static_cast<T>((detail::compute_base_power<T>(Ms) * ... * T{1}));
return result;
}
};
namespace detail {
template<auto... Ms>
void to_base_specialization_of_magnitude(const volatile magnitude<Ms...>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_magnitude =
requires(T* t) { to_base_specialization_of_magnitude(t); };
template<typename T>
requires is_derived_from_specialization_of_magnitude<T>
inline constexpr bool is_magnitude<T> = true;
template<auto... Ms>
inline constexpr bool is_specialization_of_magnitude<magnitude<Ms...>> = true;
} // namespace detail
MP_UNITS_EXPORT_BEGIN
/**
* @brief A convenient Magnitude constant for pi, which we can manipulate like a regular number.
*/
#if MP_UNITS_COMP_CLANG
inline constexpr struct mag_pi : magnitude<mag_value{std::numbers::pi_v<long double>}> {
} mag_pi;
#else
inline constexpr struct mag_pi : magnitude<std::numbers::pi_v<long double>> {
} mag_pi;
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Magnitude rational powers implementation.
template<std::intmax_t Num, std::intmax_t Den = 1, auto... Ms>
[[nodiscard]] consteval auto pow(magnitude<Ms...>)
{
if constexpr (Num == 0) {
return magnitude<>{};
} else {
return magnitude<
detail::power_v_or_T<detail::get_base(Ms), detail::get_exponent(Ms) * detail::ratio{Num, Den}>()...>{};
}
}
template<auto... Ms>
[[nodiscard]] consteval auto sqrt(magnitude<Ms...> m)
{
return pow<1, 2>(m);
}
template<auto... Ms>
[[nodiscard]] consteval auto cbrt(magnitude<Ms...> m)
{
return pow<1, 3>(m);
}
MP_UNITS_EXPORT_END

View File

@ -36,13 +36,81 @@
namespace mp_units {
namespace detail {
template<PointOrigin PO>
inline constexpr bool is_specialization_of_zeroth_point_origin = false;
template<PointOrigin PO>
[[nodiscard]] consteval bool is_zeroth_point_origin(PO)
{
return is_specialization_of_zeroth_point_origin<PO>;
}
struct point_origin_interface {
template<PointOrigin PO, Quantity Q>
requires ReferenceOf<std::remove_const_t<decltype(Q::reference)>, PO::quantity_spec>
[[nodiscard]] friend constexpr quantity_point<Q::reference, PO{}, typename Q::rep> operator+(PO, Q&& q)
{
return quantity_point{std::forward<Q>(q), PO{}};
}
template<Quantity Q, PointOrigin PO>
requires ReferenceOf<std::remove_const_t<decltype(Q::reference)>, PO::quantity_spec>
[[nodiscard]] friend constexpr quantity_point<Q::reference, PO{}, typename Q::rep> operator+(Q&& q, PO po)
{
return po + std::forward<Q>(q);
}
template<PointOrigin PO, Quantity Q>
requires ReferenceOf<std::remove_const_t<decltype(Q::reference)>, PO::quantity_spec>
[[nodiscard]] friend constexpr QuantityPoint auto operator-(PO po, const Q& q)
requires requires { -q; }
{
return po + (-q);
}
template<PointOrigin PO1, detail::SameAbsolutePointOriginAs<PO1{}> PO2>
requires QuantitySpecOf<std::remove_const_t<decltype(PO1::quantity_spec)>, PO2::quantity_spec> &&
(detail::is_derived_from_specialization_of_relative_point_origin<PO1> ||
detail::is_derived_from_specialization_of_relative_point_origin<PO2>)
[[nodiscard]] friend constexpr Quantity auto operator-(PO1 po1, PO2 po2)
{
if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin<PO1>) {
return -(po2.quantity_point - po2.quantity_point.absolute_point_origin);
} else if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin<PO2>) {
return po1.quantity_point - po1.quantity_point.absolute_point_origin;
} else {
return po1.quantity_point - po2.quantity_point;
}
}
template<PointOrigin PO1, PointOrigin PO2>
[[nodiscard]] friend consteval bool operator==(PO1 po1, PO2 po2)
{
if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin<PO1> &&
detail::is_derived_from_specialization_of_absolute_point_origin<PO2>)
return is_same_v<PO1, PO2> || (detail::is_zeroth_point_origin(po1) && detail::is_zeroth_point_origin(po2) &&
interconvertible(po1.quantity_spec, po2.quantity_spec));
else if constexpr (detail::is_derived_from_specialization_of_relative_point_origin<PO1> &&
detail::is_derived_from_specialization_of_relative_point_origin<PO2>)
return PO1::quantity_point == PO2::quantity_point;
else if constexpr (detail::is_derived_from_specialization_of_relative_point_origin<PO1>)
return detail::same_absolute_point_origins(po1, po2) && is_eq_zero(PO1::quantity_point.quantity_from_zero());
else if constexpr (detail::is_derived_from_specialization_of_relative_point_origin<PO2>)
return detail::same_absolute_point_origins(po1, po2) && is_eq_zero(PO2::quantity_point.quantity_from_zero());
}
};
} // namespace detail
MP_UNITS_EXPORT template<QuantitySpec auto QS>
struct absolute_point_origin {
struct absolute_point_origin : detail::point_origin_interface {
static constexpr QuantitySpec auto quantity_spec = QS;
};
MP_UNITS_EXPORT template<QuantityPoint auto QP>
struct relative_point_origin {
struct relative_point_origin : detail::point_origin_interface {
static constexpr QuantityPoint auto quantity_point = QP;
static constexpr QuantitySpec auto quantity_spec = []() {
// select the strongest of specs
@ -62,33 +130,11 @@ inline constexpr zeroth_point_origin_<QS> zeroth_point_origin;
namespace detail {
template<PointOrigin PO>
inline constexpr bool is_specialization_of_zeroth_point_origin = false;
template<auto QS>
inline constexpr bool is_specialization_of_zeroth_point_origin<zeroth_point_origin_<QS>> = true;
template<PointOrigin PO>
[[nodiscard]] consteval bool is_zeroth_point_origin(PO)
{
return is_specialization_of_zeroth_point_origin<PO>;
}
} // namespace detail
MP_UNITS_EXPORT template<PointOrigin PO1, PointOrigin PO2>
[[nodiscard]] consteval bool operator==(PO1 po1, PO2 po2)
{
if constexpr (detail::AbsolutePointOrigin<PO1> && detail::AbsolutePointOrigin<PO2>)
return is_same_v<PO1, PO2> || (detail::is_zeroth_point_origin(po1) && detail::is_zeroth_point_origin(po2) &&
interconvertible(po1.quantity_spec, po2.quantity_spec));
else if constexpr (detail::RelativePointOrigin<PO1> && detail::RelativePointOrigin<PO2>)
return PO1::quantity_point == PO2::quantity_point;
else if constexpr (detail::RelativePointOrigin<PO1>)
return detail::same_absolute_point_origins(po1, po2) && is_eq_zero(PO1::quantity_point.quantity_from_zero());
else if constexpr (detail::RelativePointOrigin<PO2>)
return detail::same_absolute_point_origins(po1, po2) && is_eq_zero(PO2::quantity_point.quantity_from_zero());
}
MP_UNITS_EXPORT template<Reference R>
[[nodiscard]] consteval PointOriginFor<get_quantity_spec(R{})> auto default_point_origin(R)
@ -104,7 +150,7 @@ namespace detail {
template<PointOrigin PO>
[[nodiscard]] consteval PointOrigin auto get_absolute_point_origin(PO po)
{
if constexpr (AbsolutePointOrigin<PO>)
if constexpr (is_derived_from_specialization_of_absolute_point_origin<PO>)
return po;
else
return po.quantity_point.absolute_point_origin;
@ -112,8 +158,6 @@ template<PointOrigin PO>
} // namespace detail
MP_UNITS_EXPORT_BEGIN
/**
* @brief A quantity point
*
@ -123,7 +167,7 @@ MP_UNITS_EXPORT_BEGIN
* @tparam PO a type that represents the origin point from which the quantity point is measured from
* @tparam Rep a type to be used to represent values of a quantity point
*/
template<Reference auto R, PointOriginFor<get_quantity_spec(R)> auto PO = default_point_origin(R),
MP_UNITS_EXPORT template<Reference auto R, PointOriginFor<get_quantity_spec(R)> auto PO = default_point_origin(R),
RepresentationOf<get_quantity_spec(R).character> Rep = double>
class quantity_point {
public:
@ -524,44 +568,4 @@ explicit(is_specialization_of<decltype(quantity_point_like_traits<QP>::to_numeri
-> quantity_point<quantity_point_like_traits<QP>::reference, quantity_point_like_traits<QP>::point_origin,
typename quantity_point_like_traits<QP>::rep>;
// binary operators on point origins
template<PointOrigin PO, Quantity Q>
requires ReferenceOf<std::remove_const_t<decltype(Q::reference)>, PO::quantity_spec>
[[nodiscard]] constexpr quantity_point<Q::reference, PO{}, typename Q::rep> operator+(PO, Q&& q)
{
return quantity_point{std::forward<Q>(q), PO{}};
}
template<Quantity Q, PointOrigin PO>
requires ReferenceOf<std::remove_const_t<decltype(Q::reference)>, PO::quantity_spec>
[[nodiscard]] constexpr quantity_point<Q::reference, PO{}, typename Q::rep> operator+(Q&& q, PO po)
{
return po + std::forward<Q>(q);
}
template<PointOrigin PO, Quantity Q>
requires ReferenceOf<std::remove_const_t<decltype(Q::reference)>, PO::quantity_spec>
[[nodiscard]] constexpr QuantityPoint auto operator-(PO po, const Q& q)
requires requires { -q; }
{
return po + (-q);
}
template<PointOrigin PO1, detail::SameAbsolutePointOriginAs<PO1{}> PO2>
requires QuantitySpecOf<std::remove_const_t<decltype(PO1::quantity_spec)>, PO2::quantity_spec> &&
(detail::is_derived_from_specialization_of_relative_point_origin<PO1> ||
detail::is_derived_from_specialization_of_relative_point_origin<PO2>)
[[nodiscard]] constexpr Quantity auto operator-(PO1 po1, PO2 po2)
{
if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin<PO1>) {
return -(po2.quantity_point - po2.quantity_point.absolute_point_origin);
} else if constexpr (detail::is_derived_from_specialization_of_absolute_point_origin<PO2>) {
return po1.quantity_point - po1.quantity_point.absolute_point_origin;
} else {
return po1.quantity_point - po2.quantity_point;
}
}
MP_UNITS_EXPORT_END
} // namespace mp_units

View File

@ -47,9 +47,6 @@ template<typename T>
inline constexpr bool is_derived_from_specialization_of_absolute_point_origin =
requires(T* t) { to_base_specialization_of_absolute_point_origin(t); };
template<typename T>
concept AbsolutePointOrigin = is_derived_from_specialization_of_absolute_point_origin<T> && std::is_final_v<T>;
} // namespace detail
/**
@ -72,8 +69,7 @@ template<typename T>
inline constexpr bool is_derived_from_specialization_of_relative_point_origin =
requires(T* t) { to_base_specialization_of_relative_point_origin(t); };
template<typename T>
concept RelativePointOrigin = is_derived_from_specialization_of_relative_point_origin<T> && std::is_final_v<T>;
struct point_origin_interface;
} // namespace detail
@ -83,7 +79,7 @@ concept RelativePointOrigin = is_derived_from_specialization_of_relative_point_o
* Satisfied by either quantity points or by all types derived from `absolute_point_origin` class template.
*/
MP_UNITS_EXPORT template<typename T>
concept PointOrigin = detail::AbsolutePointOrigin<T> || detail::RelativePointOrigin<T>;
concept PointOrigin = std::derived_from<T, detail::point_origin_interface> && std::is_final_v<T>;
/**
* @brief A concept matching all quantity point origins for a specified quantity type in the library
@ -113,13 +109,15 @@ inline constexpr bool is_quantity_point<T> = true;
template<PointOrigin PO1, PointOrigin PO2>
[[nodiscard]] consteval bool same_absolute_point_origins(PO1 po1, PO2 po2)
{
if constexpr (AbsolutePointOrigin<PO1> && AbsolutePointOrigin<PO2>)
if constexpr (is_derived_from_specialization_of_absolute_point_origin<PO1> &&
is_derived_from_specialization_of_absolute_point_origin<PO2>)
return po1 == po2;
else if constexpr (RelativePointOrigin<PO1> && RelativePointOrigin<PO2>)
else if constexpr (is_derived_from_specialization_of_relative_point_origin<PO1> &&
is_derived_from_specialization_of_relative_point_origin<PO2>)
return po1.absolute_point_origin == po2.absolute_point_origin;
else if constexpr (RelativePointOrigin<PO1>)
else if constexpr (is_derived_from_specialization_of_relative_point_origin<PO1>)
return po1.absolute_point_origin == po2;
else if constexpr (RelativePointOrigin<PO2>)
else if constexpr (is_derived_from_specialization_of_relative_point_origin<PO2>)
return po1 == po2.absolute_point_origin;
else
return false;

View File

@ -46,13 +46,15 @@
namespace mp_units {
MP_UNITS_EXPORT struct dimensionless;
namespace detail {
template<QuantitySpec QS, Unit U>
requires(!AssociatedUnit<U>) || UnitOf<U, QS{}>
[[nodiscard]] consteval Reference auto make_reference(QS, U u)
{
if constexpr (detail::QuantityKindSpec<QS>)
if constexpr (QuantityKindSpec<QS>)
return u;
else
return reference<QS, U>{};
@ -100,7 +102,7 @@ template<auto... Args>
}
template<NamedQuantitySpec Lhs, NamedQuantitySpec Rhs>
struct quantity_spec_less : std::bool_constant<detail::type_name<Lhs>() < detail::type_name<Rhs>()> {};
struct quantity_spec_less : std::bool_constant<type_name<Lhs>() < type_name<Rhs>()> {};
template<typename T1, typename T2>
using type_list_of_quantity_spec_less = expr_less<T1, T2, quantity_spec_less>;
@ -112,6 +114,12 @@ using to_dimension = std::remove_const_t<decltype(Q::dimension)>;
template<AssociatedUnit U>
[[nodiscard]] consteval auto get_associated_quantity(U);
template<QuantitySpec auto... From, QuantitySpec Q>
[[nodiscard]] consteval QuantitySpec auto clone_kind_of(Q q);
template<QuantitySpec Q>
[[nodiscard]] consteval auto remove_kind(Q q);
#if !MP_UNITS_API_NO_CRTP
template<typename Self>
#endif
@ -120,33 +128,55 @@ struct quantity_spec_interface {
template<typename Self, UnitOf<Self{}> U>
[[nodiscard]] consteval Reference auto operator[](this Self self, U u)
{
return detail::make_reference(self, u);
return make_reference(self, u);
}
template<typename Self, typename Q>
requires Quantity<std::remove_cvref_t<Q>> &&
detail::QuantitySpecExplicitlyConvertibleTo<std::remove_reference_t<Q>::quantity_spec, Self{}>
QuantitySpecExplicitlyConvertibleTo<std::remove_reference_t<Q>::quantity_spec, Self{}>
[[nodiscard]] constexpr Quantity auto operator()(this Self self, Q&& q)
{
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
detail::make_reference(self, std::remove_cvref_t<Q>::unit)};
make_reference(self, std::remove_cvref_t<Q>::unit)};
}
#else
template<typename Self_ = Self, UnitOf<Self_{}> U>
[[nodiscard]] MP_UNITS_CONSTEVAL Reference auto operator[](U u) const
{
return detail::make_reference(Self{}, u);
return make_reference(Self{}, u);
}
template<typename Q, typename Self_ = Self>
requires Quantity<std::remove_cvref_t<Q>> &&
detail::QuantitySpecExplicitlyConvertibleTo<std::remove_reference_t<Q>::quantity_spec, Self_{}>
QuantitySpecExplicitlyConvertibleTo<std::remove_reference_t<Q>::quantity_spec, Self_{}>
[[nodiscard]] constexpr Quantity auto operator()(Q&& q) const
{
return quantity{std::forward<Q>(q).numerical_value_is_an_implementation_detail_,
detail::make_reference(Self{}, std::remove_cvref_t<Q>::unit)};
make_reference(Self{}, std::remove_cvref_t<Q>::unit)};
}
#endif
template<QuantitySpec Lhs, QuantitySpec Rhs>
[[nodiscard]] friend consteval QuantitySpec auto operator*(Lhs lhs, Rhs rhs)
{
return clone_kind_of<Lhs{}, Rhs{}>(
expr_multiply<derived_quantity_spec, struct dimensionless, type_list_of_quantity_spec_less>(remove_kind(lhs),
remove_kind(rhs)));
}
template<QuantitySpec Lhs, QuantitySpec Rhs>
[[nodiscard]] friend consteval QuantitySpec auto operator/(Lhs lhs, Rhs rhs)
{
return clone_kind_of<Lhs{}, Rhs{}>(
expr_divide<derived_quantity_spec, struct dimensionless, type_list_of_quantity_spec_less>(remove_kind(lhs),
remove_kind(rhs)));
}
template<QuantitySpec Lhs, QuantitySpec Rhs>
[[nodiscard]] friend consteval bool operator==(Lhs, Rhs)
{
return is_same_v<Lhs, Rhs>;
}
};
} // namespace detail
@ -546,30 +576,6 @@ template<QuantitySpec Q>
MP_UNITS_EXPORT_BEGIN
// Operators
template<QuantitySpec Lhs, QuantitySpec Rhs>
[[nodiscard]] consteval QuantitySpec auto operator*(Lhs lhs, Rhs rhs)
{
return detail::clone_kind_of<Lhs{}, Rhs{}>(
detail::expr_multiply<derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(
detail::remove_kind(lhs), detail::remove_kind(rhs)));
}
template<QuantitySpec Lhs, QuantitySpec Rhs>
[[nodiscard]] consteval QuantitySpec auto operator/(Lhs lhs, Rhs rhs)
{
return detail::clone_kind_of<Lhs{}, Rhs{}>(
detail::expr_divide<derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(
detail::remove_kind(lhs), detail::remove_kind(rhs)));
}
template<QuantitySpec Lhs, QuantitySpec Rhs>
[[nodiscard]] consteval bool operator==(Lhs, Rhs)
{
return is_same_v<Lhs, Rhs>;
}
[[nodiscard]] consteval QuantitySpec auto inverse(QuantitySpec auto q) { return dimensionless / q; }

View File

@ -30,6 +30,31 @@
namespace mp_units {
#if MP_UNITS_API_NO_CRTP
namespace detail {
struct quantity_spec_interface;
}
MP_UNITS_EXPORT template<typename T>
concept QuantitySpec = std::derived_from<T, detail::quantity_spec_interface> && std::is_final_v<T>;
#else
namespace detail {
template<typename Self>
struct quantity_spec_interface;
}
MP_UNITS_EXPORT template<typename T>
concept QuantitySpec = is_derived_from_specialization_of<T, detail::quantity_spec_interface> && std::is_final_v<T>;
#endif
MP_UNITS_EXPORT
#if MP_UNITS_API_NO_CRTP
template<auto...>
@ -71,7 +96,7 @@ inline constexpr bool is_derived_from_specialization_of_quantity_spec =
*/
template<typename T>
concept NamedQuantitySpec =
is_derived_from_specialization_of_quantity_spec<T> && std::is_final_v<T> && (!QuantityKindSpec<T>);
QuantitySpec<T> && is_derived_from_specialization_of_quantity_spec<T> && (!QuantityKindSpec<T>);
template<typename T>
struct is_dimensionless : std::false_type {};
@ -110,16 +135,13 @@ namespace detail {
*/
template<typename T>
concept DerivedQuantitySpec =
is_specialization_of<T, derived_quantity_spec> ||
QuantitySpec<T> && (is_specialization_of<T, derived_quantity_spec> ||
(QuantityKindSpec<T> &&
is_specialization_of<std::remove_const_t<decltype(T::_quantity_spec_)>, derived_quantity_spec>);
is_specialization_of<std::remove_const_t<decltype(T::_quantity_spec_)>, derived_quantity_spec>));
} // namespace detail
MP_UNITS_EXPORT template<typename T>
concept QuantitySpec = detail::NamedQuantitySpec<T> || detail::DerivedQuantitySpec<T> || detail::QuantityKindSpec<T>;
MP_UNITS_EXPORT template<QuantitySpec Q>
[[nodiscard]] consteval detail::QuantityKindSpec auto get_kind(Q q);

View File

@ -56,6 +56,155 @@ namespace mp_units {
namespace detail {
template<Magnitude auto M, Unit U>
struct scaled_unit_impl;
template<typename T>
inline constexpr bool is_specialization_of_scaled_unit = false;
template<DerivedUnitExpr... Expr>
struct derived_unit_impl;
/**
* @brief A canonical representation of a unit
*
* A canonical representation of a unit consists of:
* - a reference unit being the result of extraction of all the intermediate derived units,
* - a magnitude being a product of all the prefixes and magnitudes of extracted scaled units.
*
* All units having the same canonical unit are deemed equal.
* All units having the same reference unit are convertible (their magnitude may differ and
* is used during conversion).
*
* @tparam U a unit to use as a `reference_unit`
* @tparam M a Magnitude representing an absolute scaling factor of this unit
*/
template<Magnitude M, Unit U>
struct canonical_unit {
M mag;
U reference_unit;
};
#if MP_UNITS_COMP_CLANG
template<Magnitude M, Unit U>
canonical_unit(M, U) -> canonical_unit<M, U>;
#endif
template<Unit T, symbol_text Symbol, auto... Args>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Args...>&);
template<Unit T, symbol_text Symbol, Unit auto U, auto... Args>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, U, Args...>&);
template<typename T, typename F, int Num, int... Den>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const power<F, Num, Den...>&);
template<Unit T, typename... Expr>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit_impl<Expr...>&);
template<Unit T, auto M, typename U>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const scaled_unit_impl<M, U>&);
template<Unit Lhs, Unit Rhs>
struct unit_less : std::bool_constant<(Lhs::symbol < Rhs::symbol)> {};
template<typename T1, typename T2>
using type_list_of_unit_less = expr_less<T1, T2, unit_less>;
} // namespace detail
// TODO this should really be in the `details` namespace but is used in `chrono.h` (a part of mp_units.systems)
// Even though it is not exported, it is visible to the other module via ADL
[[nodiscard]] consteval auto get_canonical_unit(Unit auto u) { return detail::get_canonical_unit_impl(u, u); }
namespace detail {
template<Unit U1, Unit U2>
[[nodiscard]] consteval bool have_same_canonical_reference_unit(U1 u1, U2 u2)
{
if constexpr (is_same_v<U1, U2>)
return true;
else
return is_same_v<decltype(get_canonical_unit(u1).reference_unit), decltype(get_canonical_unit(u2).reference_unit)>;
}
struct unit_interface {
/**
* Multiplication by `1` returns the same unit, otherwise `scaled_unit` is being returned.
*/
template<Magnitude M, Unit U>
[[nodiscard]] friend MP_UNITS_CONSTEVAL Unit auto operator*(M, U u)
{
if constexpr (std::is_same_v<M, std::remove_const_t<decltype(mp_units::mag<1>)>>)
return u;
else
return scaled_unit<M{}, U>{};
}
[[nodiscard]] friend consteval Unit auto operator*(Unit auto, Magnitude auto)
#if __cpp_deleted_function
= delete("To scale a unit use `mag * unit` syntax");
#else
= delete;
#endif
/**
* Returns the result of multiplication with an inverse unit.
*/
template<Magnitude M, Unit U>
[[nodiscard]] friend MP_UNITS_CONSTEVAL Unit auto operator/(M mag, U u)
{
return mag * inverse(u);
}
/**
* `scaled_unit` specializations have priority in this operation. This means that the library framework
* prevents passing it as an element to the `derived_unit`. In such case only the reference unit is passed
* to the derived unit and the magnitude remains outside forming another scaled unit as a result of the operation.
*/
template<Unit Lhs, Unit Rhs>
[[nodiscard]] friend MP_UNITS_CONSTEVAL Unit auto operator*(Lhs lhs, Rhs rhs)
{
if constexpr (is_specialization_of_scaled_unit<Lhs> && is_specialization_of_scaled_unit<Rhs>)
return (Lhs::mag * Rhs::mag) * (Lhs::reference_unit * Rhs::reference_unit);
else if constexpr (is_specialization_of_scaled_unit<Lhs>)
return Lhs::mag * (Lhs::reference_unit * rhs);
else if constexpr (is_specialization_of_scaled_unit<Rhs>)
return Rhs::mag * (lhs * Rhs::reference_unit);
else
return expr_multiply<derived_unit, struct one, type_list_of_unit_less>(lhs, rhs);
}
/**
* `scaled_unit` specializations have priority in this operation. This means that the library framework
* prevents passing it as an element to the `derived_unit`. In such case only the reference unit is passed
* to the derived unit and the magnitude remains outside forming another scaled unit as a result of the operation.
*/
template<Unit Lhs, Unit Rhs>
[[nodiscard]] friend MP_UNITS_CONSTEVAL Unit auto operator/(Lhs lhs, Rhs rhs)
{
if constexpr (is_specialization_of_scaled_unit<Lhs> && is_specialization_of_scaled_unit<Rhs>)
return (Lhs::mag / Rhs::mag) * (Lhs::reference_unit / Rhs::reference_unit);
else if constexpr (is_specialization_of_scaled_unit<Lhs>)
return Lhs::mag * (Lhs::reference_unit / rhs);
else if constexpr (is_specialization_of_scaled_unit<Rhs>)
return mag<1> / Rhs::mag * (lhs / Rhs::reference_unit);
else
return expr_divide<derived_unit, struct one, type_list_of_unit_less>(lhs, rhs);
}
[[nodiscard]] friend consteval bool operator==(Unit auto lhs, Unit auto rhs)
{
auto canonical_lhs = get_canonical_unit(lhs);
auto canonical_rhs = get_canonical_unit(rhs);
return convertible(canonical_lhs.reference_unit, canonical_rhs.reference_unit) &&
canonical_lhs.mag == canonical_rhs.mag;
}
};
template<Unit U, bool = requires { U::point_origin; }>
struct propagate_point_origin {};
@ -65,7 +214,7 @@ struct propagate_point_origin<U, true> {
};
template<Magnitude auto M, Unit U>
struct scaled_unit_impl : detail::propagate_point_origin<U> {
struct scaled_unit_impl : detail::unit_interface, detail::propagate_point_origin<U> {
using _base_type_ = scaled_unit_impl; // exposition only
static constexpr MP_UNITS_CONSTRAINED_AUTO_WORKAROUND(Magnitude) auto mag = M;
static constexpr U reference_unit{};
@ -87,9 +236,6 @@ struct scaled_unit final : detail::scaled_unit_impl<M, U> {};
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_scaled_unit = false;
template<auto M, Unit U>
inline constexpr bool is_specialization_of_scaled_unit<scaled_unit<M, U>> = true;
@ -142,7 +288,7 @@ struct named_unit;
*/
template<symbol_text Symbol, detail::QuantityKindSpec auto QS>
requires(!Symbol.empty()) && detail::BaseDimension<std::remove_const_t<decltype(QS.dimension)>>
struct named_unit<Symbol, QS> {
struct named_unit<Symbol, QS> : detail::unit_interface {
using _base_type_ = named_unit; // exposition only
static constexpr auto symbol = Symbol; ///< Unique base unit identifier
static constexpr auto quantity_spec = QS;
@ -150,7 +296,7 @@ struct named_unit<Symbol, QS> {
template<symbol_text Symbol, detail::QuantityKindSpec auto QS, PointOrigin auto PO>
requires(!Symbol.empty()) && detail::BaseDimension<std::remove_const_t<decltype(QS.dimension)>>
struct named_unit<Symbol, QS, PO> {
struct named_unit<Symbol, QS, PO> : detail::unit_interface {
using _base_type_ = named_unit; // exposition only
static constexpr auto symbol = Symbol; ///< Unique base unit identifier
static constexpr auto quantity_spec = QS;
@ -169,7 +315,7 @@ struct named_unit<Symbol, QS, PO> {
*/
template<symbol_text Symbol>
requires(!Symbol.empty())
struct named_unit<Symbol> {
struct named_unit<Symbol> : detail::unit_interface {
using _base_type_ = named_unit; // exposition only
static constexpr auto symbol = Symbol; ///< Unique base unit identifier
};
@ -258,7 +404,7 @@ template<typename T>
struct is_one : std::false_type {};
template<DerivedUnitExpr... Expr>
struct derived_unit_impl : detail::expr_fractions<detail::is_one, Expr...> {
struct derived_unit_impl : detail::unit_interface, detail::expr_fractions<detail::is_one, Expr...> {
using _base_type_ = derived_unit_impl; // exposition only
};
@ -326,45 +472,6 @@ namespace detail {
template<>
struct is_one<struct one> : std::true_type {};
/**
* @brief A canonical representation of a unit
*
* A canonical representation of a unit consists of:
* - a reference unit being the result of extraction of all the intermediate derived units,
* - a magnitude being a product of all the prefixes and magnitudes of extracted scaled units.
*
* All units having the same canonical unit are deemed equal.
* All units having the same reference unit are convertible (their magnitude may differ and
* is used during conversion).
*
* @tparam U a unit to use as a `reference_unit`
* @tparam M a Magnitude representing an absolute scaling factor of this unit
*/
template<Magnitude M, Unit U>
struct canonical_unit {
M mag;
U reference_unit;
};
#if MP_UNITS_COMP_CLANG
template<Magnitude M, Unit U>
canonical_unit(M, U) -> canonical_unit<M, U>;
#endif
template<Unit T, symbol_text Symbol, auto... Args>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Args...>&);
template<Unit T, symbol_text Symbol, Unit auto U, auto... Args>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const named_unit<Symbol, U, Args...>&);
template<typename T, typename F, int Num, int... Den>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const power<F, Num, Den...>&);
template<Unit T, typename... Expr>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const derived_unit_impl<Expr...>&);
template<Unit T, auto M, typename U>
[[nodiscard]] consteval auto get_canonical_unit_impl(T, const scaled_unit_impl<M, U>&)
{
@ -422,113 +529,6 @@ template<Unit T, typename... Expr>
return canonical_unit{num.mag / den.mag, num.reference_unit / den.reference_unit};
}
template<Unit Lhs, Unit Rhs>
struct unit_less : std::bool_constant<(Lhs::symbol < Rhs::symbol)> {};
template<typename T1, typename T2>
using type_list_of_unit_less = expr_less<T1, T2, unit_less>;
} // namespace detail
// TODO this should really be in the `details` namespace but is used in `chrono.h` (a part of mp_units.systems)
// Even though it is not exported, it is visible to the other module via ADL
[[nodiscard]] consteval auto get_canonical_unit(Unit auto u) { return detail::get_canonical_unit_impl(u, u); }
MP_UNITS_EXPORT_BEGIN
// Operators
/**
* Multiplication by `1` returns the same unit, otherwise `scaled_unit` is being returned.
*/
template<Magnitude M, Unit U>
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator*(M, U u)
{
if constexpr (std::is_same_v<M, std::remove_const_t<decltype(mp_units::mag<1>)>>)
return u;
else
return scaled_unit<M{}, U>{};
}
[[nodiscard]] consteval Unit auto operator*(Unit auto, Magnitude auto)
#if __cpp_deleted_function
= delete("To scale a unit use `mag * unit` syntax");
#else
= delete;
#endif
/**
* Returns the result of multiplication with an inverse unit.
*/
template<Magnitude M, Unit U>
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator/(M mag, U u)
{
return mag * inverse(u);
}
/**
* `scaled_unit` specializations have priority in this operation. This means that the library framework
* prevents passing it as an element to the `derived_unit`. In such case only the reference unit is passed
* to the derived unit and the magnitude remains outside forming another scaled unit as a result of the operation.
*/
template<Unit Lhs, Unit Rhs>
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator*(Lhs lhs, Rhs rhs)
{
if constexpr (detail::is_specialization_of_scaled_unit<Lhs> && detail::is_specialization_of_scaled_unit<Rhs>)
return (Lhs::mag * Rhs::mag) * (Lhs::reference_unit * Rhs::reference_unit);
else if constexpr (detail::is_specialization_of_scaled_unit<Lhs>)
return Lhs::mag * (Lhs::reference_unit * rhs);
else if constexpr (detail::is_specialization_of_scaled_unit<Rhs>)
return Rhs::mag * (lhs * Rhs::reference_unit);
else
return detail::expr_multiply<derived_unit, struct one, detail::type_list_of_unit_less>(lhs, rhs);
}
/**
* `scaled_unit` specializations have priority in this operation. This means that the library framework
* prevents passing it as an element to the `derived_unit`. In such case only the reference unit is passed
* to the derived unit and the magnitude remains outside forming another scaled unit as a result of the operation.
*/
template<Unit Lhs, Unit Rhs>
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto operator/(Lhs lhs, Rhs rhs)
{
if constexpr (detail::is_specialization_of_scaled_unit<Lhs> && detail::is_specialization_of_scaled_unit<Rhs>)
return (Lhs::mag / Rhs::mag) * (Lhs::reference_unit / Rhs::reference_unit);
else if constexpr (detail::is_specialization_of_scaled_unit<Lhs>)
return Lhs::mag * (Lhs::reference_unit / rhs);
else if constexpr (detail::is_specialization_of_scaled_unit<Rhs>)
return mag<1> / Rhs::mag * (lhs / Rhs::reference_unit);
else
return detail::expr_divide<derived_unit, struct one, detail::type_list_of_unit_less>(lhs, rhs);
}
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto inverse(Unit auto u) { return one / u; }
MP_UNITS_EXPORT_END
namespace detail {
template<Unit U1, Unit U2>
[[nodiscard]] consteval bool have_same_canonical_reference_unit(U1 u1, U2 u2)
{
if constexpr (is_same_v<U1, U2>)
return true;
else
return is_same_v<decltype(get_canonical_unit(u1).reference_unit), decltype(get_canonical_unit(u2).reference_unit)>;
}
} // namespace detail
MP_UNITS_EXPORT [[nodiscard]] consteval bool operator==(Unit auto lhs, Unit auto rhs)
{
auto canonical_lhs = get_canonical_unit(lhs);
auto canonical_rhs = get_canonical_unit(rhs);
return convertible(canonical_lhs.reference_unit, canonical_rhs.reference_unit) &&
canonical_lhs.mag == canonical_rhs.mag;
}
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_derived_unit = false;
@ -539,6 +539,8 @@ inline constexpr bool is_specialization_of_derived_unit<derived_unit<Expr...>> =
MP_UNITS_EXPORT_BEGIN
[[nodiscard]] MP_UNITS_CONSTEVAL Unit auto inverse(Unit auto u) { return one / u; }
/**
* @brief Computes the value of a unit raised to the `Num/Den` power
*

View File

@ -33,8 +33,7 @@ namespace mp_units {
namespace detail {
template<typename T>
inline constexpr bool is_unit = false;
struct unit_interface;
} // namespace detail
@ -44,7 +43,7 @@ inline constexpr bool is_unit = false;
* Satisfied by all unit types provided by the library.
*/
MP_UNITS_EXPORT template<typename T>
concept Unit = detail::is_unit<T>;
concept Unit = std::derived_from<T, detail::unit_interface> && std::is_final_v<T>;
template<Magnitude auto M, Unit U>
struct scaled_unit;
@ -122,24 +121,6 @@ struct prefixed_unit;
namespace detail {
template<auto M, typename U>
void is_unit_impl(const scaled_unit<M, U>*);
template<symbol_text Symbol, auto... Args>
void is_unit_impl(const named_unit<Symbol, Args...>*);
template<symbol_text Symbol, auto M, auto U>
void is_unit_impl(const prefixed_unit<Symbol, M, U>*);
template<typename... Expr>
void is_unit_impl(const derived_unit<Expr...>*);
void is_unit_impl(const one*);
template<typename T>
requires requires(T* t) { is_unit_impl(t); } && std::is_final_v<T>
inline constexpr bool is_unit<T> = true;
template<Unit U>
[[nodiscard]] consteval bool has_associated_quantity(U);

View File

@ -58,17 +58,6 @@ static_assert(!detail::BaseDimension<base_dimension<"L">>);
static_assert(!detail::BaseDimension<struct si::metre>);
static_assert(!detail::BaseDimension<int>);
// DerivedDimension
static_assert(detail::DerivedDimension<decltype(isq::dim_length / isq::dim_time)>);
static_assert(detail::DerivedDimension<decltype(inverse(isq::dim_time))>);
static_assert(detail::DerivedDimension<decltype(pow<2>(isq::dim_length))>);
static_assert(detail::DerivedDimension<derived_dimension<struct isq::dim_length, per<struct isq::dim_time>>>);
static_assert(detail::DerivedDimension<struct dimension_one>);
static_assert(detail::DerivedDimension<std::remove_const_t<decltype(dim_speed)>>);
static_assert(!detail::DerivedDimension<struct isq::dim_length>);
static_assert(!detail::DerivedDimension<struct si::metre>);
static_assert(!detail::DerivedDimension<int>);
// Dimension
static_assert(Dimension<struct isq::dim_length>);
static_assert(Dimension<decltype(isq::dim_length / isq::dim_time)>);

View File

@ -62,13 +62,9 @@ inline constexpr auto energy = force * length;
// concepts verification
static_assert(detail::BaseDimension<length_>);
static_assert(!detail::BaseDimension<std::remove_const_t<decltype(frequency)>>);
static_assert(!detail::DerivedDimension<length_>);
static_assert(detail::DerivedDimension<std::remove_const_t<decltype(frequency)>>);
static_assert(Dimension<length_>);
static_assert(Dimension<std::remove_const_t<decltype(frequency)>>);
static_assert(detail::DerivedDimension<dimension_one_>);
static_assert(detail::DerivedDimension<decltype(length / length)>); // dimension_one
static_assert(detail::BaseDimension<decltype(speed * time)>); // length
// derived dimension expression template syntax verification