mirror of
https://github.com/mpusz/mp-units.git
synced 2025-07-30 18:37:15 +02:00
feat: tag types-related operators are now hidden friends as well
Resolves #591
This commit is contained in:
@ -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; }
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,8 +167,8 @@ 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),
|
||||
RepresentationOf<get_quantity_spec(R).character> Rep = double>
|
||||
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:
|
||||
// member types and values
|
||||
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
||||
|
||||
|
@ -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> ||
|
||||
(QuantityKindSpec<T> &&
|
||||
is_specialization_of<std::remove_const_t<decltype(T::_quantity_spec_)>, 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>));
|
||||
|
||||
} // 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);
|
||||
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)>);
|
||||
|
@ -62,14 +62,10 @@ 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
|
||||
static_assert(detail::BaseDimension<decltype(speed * time)>); // length
|
||||
|
||||
// derived dimension expression template syntax verification
|
||||
static_assert(is_of_type<inverse(time), derived_dimension<dimension_one_, per<time_>>>);
|
||||
|
Reference in New Issue
Block a user