feat: quantities of the same kind can now be added, subtracted, or compared to each other

This commit is contained in:
Mateusz Pusz
2023-01-28 11:00:50 +01:00
parent 9a7a55b873
commit 1b2d77af41
4 changed files with 143 additions and 20 deletions

View File

@@ -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

View File

@@ -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)
{

View File

@@ -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,

View File

@@ -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);