forked from mpusz/mp-units
feat: quantities of the same kind can now be added, subtracted, or compared to each other
This commit is contained in:
@@ -80,4 +80,47 @@ concept is_derived_from_specialization_of = requires(T* t) { detail::to_base_spe
|
||||
template<typename T, typename... Ts>
|
||||
concept one_of = (false || ... || std::same_as<T, Ts>);
|
||||
|
||||
template<typename T, auto... Vs>
|
||||
consteval bool contains()
|
||||
{
|
||||
return (false || ... || is_same_v<std::remove_const_t<decltype(Vs)>, T>);
|
||||
}
|
||||
|
||||
template<template<typename...> typename T, typename... Ts>
|
||||
consteval bool contains()
|
||||
{
|
||||
return (false || ... || is_specialization_of<Ts, T>);
|
||||
}
|
||||
|
||||
template<typename T, std::same_as<T> auto V>
|
||||
consteval auto get()
|
||||
{
|
||||
return V;
|
||||
}
|
||||
|
||||
template<typename T, auto V1, auto V2, auto... Vs>
|
||||
consteval auto get()
|
||||
{
|
||||
if constexpr (is_same_v<T, std::remove_const_t<decltype(V1)>>)
|
||||
return V1;
|
||||
else
|
||||
return get<T, V2, Vs...>();
|
||||
}
|
||||
|
||||
template<template<typename...> typename T, typename T1>
|
||||
requires is_specialization_of<T1, T>
|
||||
consteval auto get()
|
||||
{
|
||||
return T1{};
|
||||
}
|
||||
|
||||
template<template<typename...> typename T, typename T1, typename T2, typename... Ts>
|
||||
consteval auto get()
|
||||
{
|
||||
if constexpr (is_specialization_of<T1, T2, T>)
|
||||
return T1{};
|
||||
else
|
||||
return get<T, T2, Ts...>();
|
||||
}
|
||||
|
||||
} // namespace mp_units
|
||||
|
@@ -455,7 +455,7 @@ explicit quantity(Q) -> quantity<quantity_like_traits<Q>::reference, typename qu
|
||||
|
||||
// non-member binary operators
|
||||
template<Quantity Q1, Quantity Q2>
|
||||
requires(interconvertible(Q1::reference, Q2::reference)) &&
|
||||
requires(get_kind(Q1::quantity_spec) == get_kind(Q2::quantity_spec)) &&
|
||||
invoke_result_of_<common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character, std::plus<>,
|
||||
typename Q1::rep, typename Q2::rep>
|
||||
[[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs)
|
||||
@@ -466,7 +466,7 @@ template<Quantity Q1, Quantity Q2>
|
||||
}
|
||||
|
||||
template<Quantity Q1, Quantity Q2>
|
||||
requires(interconvertible(Q1::reference, Q2::reference)) &&
|
||||
requires(get_kind(Q1::quantity_spec) == get_kind(Q2::quantity_spec)) &&
|
||||
invoke_result_of_<common_quantity_spec(Q1::quantity_spec, Q2::quantity_spec).character, std::minus<>,
|
||||
typename Q1::rep, typename Q2::rep>
|
||||
[[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs)
|
||||
@@ -505,7 +505,7 @@ template<Quantity Q1, Quantity Q2>
|
||||
}
|
||||
|
||||
template<Quantity Q1, Quantity Q2>
|
||||
requires(interconvertible(Q1::reference, Q2::reference)) &&
|
||||
requires(get_kind(Q1::quantity_spec) == get_kind(Q2::quantity_spec)) &&
|
||||
std::three_way_comparable_with<typename Q1::rep, typename Q2::rep>
|
||||
[[nodiscard]] constexpr auto operator<=>(const Q1& lhs, const Q2& rhs)
|
||||
{
|
||||
@@ -514,7 +514,7 @@ template<Quantity Q1, Quantity Q2>
|
||||
}
|
||||
|
||||
template<Quantity Q1, Quantity Q2>
|
||||
requires(interconvertible(Q1::reference, Q2::reference)) &&
|
||||
requires(get_kind(Q1::quantity_spec) == get_kind(Q2::quantity_spec)) &&
|
||||
std::equality_comparable_with<typename Q1::rep, typename Q2::rep>
|
||||
[[nodiscard]] constexpr bool operator==(const Q1& lhs, const Q2& rhs)
|
||||
{
|
||||
|
@@ -29,10 +29,26 @@
|
||||
#include <mp_units/bits/quantity_concepts.h>
|
||||
#include <mp_units/dimension.h>
|
||||
#include <mp_units/unit.h>
|
||||
#include <tuple>
|
||||
|
||||
namespace mp_units {
|
||||
|
||||
template<typename Q>
|
||||
struct kind_of_ {
|
||||
using type = Q;
|
||||
};
|
||||
|
||||
template<QuantitySpec auto Q>
|
||||
consteval kind_of_<std::remove_const_t<decltype(Q)>> kind_of()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename Q>
|
||||
consteval kind_of_<Q> kind_of()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
// TODO revise the note in the below comment
|
||||
@@ -65,8 +81,8 @@ template<typename... Qs1, typename... Qs2>
|
||||
template<auto... Args>
|
||||
[[nodiscard]] consteval quantity_character quantity_character_init(quantity_character ch)
|
||||
{
|
||||
if constexpr (one_of<quantity_character, std::remove_const_t<decltype(Args)>...>)
|
||||
return std::get<quantity_character>(std::make_tuple(Args...));
|
||||
if constexpr (contains<quantity_character, Args...>())
|
||||
return get<quantity_character, Args...>();
|
||||
else
|
||||
return ch;
|
||||
}
|
||||
@@ -411,6 +427,56 @@ inline constexpr bool is_dimensionless<struct dimensionless> = true;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace detail {
|
||||
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
template<QuantitySpec auto Q, auto... Args>
|
||||
[[nodiscard]] consteval bool defines_kind(quantity_spec<Q, Args...>)
|
||||
#else
|
||||
template<typename Self, QuantitySpec auto Q, auto... Args>
|
||||
[[nodiscard]] consteval bool defines_kind(quantity_spec<Self, Q, Args...>)
|
||||
#endif
|
||||
{
|
||||
return contains<kind_of_, std::remove_const_t<decltype(Args)>...>();
|
||||
}
|
||||
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
template<QuantitySpec auto Q, auto... Args>
|
||||
[[nodiscard]] consteval QuantitySpec auto fetch_kind(quantity_spec<Q, Args...>)
|
||||
#else
|
||||
template<typename Self, QuantitySpec auto Q, auto... Args>
|
||||
[[nodiscard]] consteval QuantitySpec auto fetch_kind(quantity_spec<Self, Q, Args...>)
|
||||
#endif
|
||||
{
|
||||
return typename decltype(get<kind_of_, std::remove_const_t<decltype(Args)>...>())::type{};
|
||||
}
|
||||
|
||||
template<QuantitySpec Q>
|
||||
requires requires(Q q) { get_kind(q); }
|
||||
using to_kind = std::remove_const_t<decltype(get_kind(Q{}))>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<QuantitySpec Q>
|
||||
[[nodiscard]] consteval QuantitySpec auto get_kind(Q q)
|
||||
{
|
||||
if constexpr (requires { Q::_parent_; }) {
|
||||
// named non-base quantity
|
||||
if constexpr (detail::defines_kind(q))
|
||||
return detail::fetch_kind(q);
|
||||
else
|
||||
return get_kind(Q::_parent_);
|
||||
} else if constexpr (requires {
|
||||
typename Q::_equation_;
|
||||
}) { // TODO can we just check if it is derived from the derived_quantity_spec?
|
||||
// derived quantity
|
||||
return detail::expr_map<detail::to_kind, mp_units::derived_quantity_spec, struct dimensionless,
|
||||
detail::type_list_of_quantity_spec_less>(typename Q::_base_{});
|
||||
} else {
|
||||
// base quantity
|
||||
return q;
|
||||
}
|
||||
}
|
||||
|
||||
// Operators
|
||||
|
||||
@@ -460,17 +526,13 @@ template<QuantitySpec Q1, QuantitySpec Q2>
|
||||
[[nodiscard]] consteval auto common_quantity_spec(QuantitySpec auto q) { return q; }
|
||||
|
||||
template<QuantitySpec Q1, QuantitySpec Q2>
|
||||
[[nodiscard]] consteval auto common_quantity_spec(Q1 q1, Q2 q2)
|
||||
requires(interconvertible(q1, q2))
|
||||
[[nodiscard]] consteval QuantitySpec auto common_quantity_spec(Q1 q1, Q2 q2)
|
||||
requires(get_kind(q1) == get_kind(q2))
|
||||
{
|
||||
if constexpr (std::derived_from<Q1, Q2>)
|
||||
return q1;
|
||||
else if constexpr (std::derived_from<Q2, Q1>)
|
||||
return q2;
|
||||
else if constexpr (NamedQuantitySpec<Q1>)
|
||||
return q1;
|
||||
if constexpr (detail::have_common_base(q1, q2))
|
||||
return detail::find_common_base(q1, q2);
|
||||
else
|
||||
return q2;
|
||||
return get_kind(q1);
|
||||
}
|
||||
|
||||
[[nodiscard]] consteval auto common_quantity_spec(QuantitySpec auto q1, QuantitySpec auto q2, QuantitySpec auto q3,
|
||||
|
@@ -51,8 +51,8 @@ QUANTITY_SPEC_(distance, path_length);
|
||||
QUANTITY_SPEC_(position_vector, length, quantity_character::vector);
|
||||
QUANTITY_SPEC_(period_duration, time);
|
||||
|
||||
QUANTITY_SPEC_(frequency, 1 / period_duration);
|
||||
QUANTITY_SPEC_(action, 1 / time);
|
||||
QUANTITY_SPEC_(frequency, 1 / period_duration, kind_of<frequency_>());
|
||||
QUANTITY_SPEC_(action, 1 / time, kind_of<action_>());
|
||||
QUANTITY_SPEC_(area, pow<2>(length));
|
||||
QUANTITY_SPEC_(volume, pow<3>(length));
|
||||
QUANTITY_SPEC_(velocity, position_vector / time);
|
||||
@@ -66,8 +66,9 @@ QUANTITY_SPEC_(stress, pressure, quantity_character::tensor);
|
||||
QUANTITY_SPEC_(strain, dimensionless, quantity_character::tensor);
|
||||
QUANTITY_SPEC_(power, force* velocity, quantity_character::scalar);
|
||||
QUANTITY_SPEC_(efficiency, power / power);
|
||||
QUANTITY_SPEC_(potential_energy, mass* acceleration* height);
|
||||
QUANTITY_SPEC_(energy, force * length);
|
||||
QUANTITY_SPEC_(energy, force * length, kind_of<energy_>());
|
||||
QUANTITY_SPEC_(potential_energy, mass* acceleration* height, kind_of<energy>());
|
||||
QUANTITY_SPEC_(kinetic_energy, mass* pow<2>(speed), kind_of<energy>());
|
||||
// clang-format on
|
||||
|
||||
// concepts verification
|
||||
@@ -208,6 +209,23 @@ concept invalid_operations = requires {
|
||||
};
|
||||
static_assert(invalid_operations<time>);
|
||||
|
||||
// get_kind
|
||||
static_assert(get_kind(length) == length);
|
||||
static_assert(get_kind(distance) == length);
|
||||
static_assert(get_kind(time) == time);
|
||||
static_assert(get_kind(period_duration) == time);
|
||||
static_assert(get_kind(length / time) == length / time);
|
||||
static_assert(get_kind(speed) == length / time);
|
||||
static_assert(get_kind(height / time) == length / time);
|
||||
static_assert(get_kind(1 / time) == 1 / time);
|
||||
static_assert(get_kind(1 / period_duration) == 1 / time);
|
||||
static_assert(get_kind(frequency) == frequency);
|
||||
static_assert(get_kind(mass * frequency) == mass * frequency);
|
||||
static_assert(get_kind(moment_of_force) == mass * pow<2>(length) / pow<2>(time));
|
||||
static_assert(get_kind(energy) == energy);
|
||||
static_assert(get_kind(potential_energy) == energy);
|
||||
static_assert(get_kind(kinetic_energy) == energy);
|
||||
|
||||
// comparisons of the same dimensions
|
||||
static_assert(length == length);
|
||||
static_assert(speed == speed);
|
||||
|
Reference in New Issue
Block a user