mirror of
https://github.com/mpusz/mp-units.git
synced 2025-08-01 03:14:29 +02:00
refactor: quantity arithmetics implemented
This commit is contained in:
@@ -23,6 +23,9 @@
|
||||
#include <units/si/si.h>
|
||||
|
||||
|
||||
template<typename T>
|
||||
consteval bool print();
|
||||
|
||||
template<typename T, typename Expr>
|
||||
constexpr bool is_of_type(Expr)
|
||||
{
|
||||
@@ -34,33 +37,102 @@ namespace {
|
||||
using namespace units;
|
||||
using namespace units::si::unit_symbols;
|
||||
|
||||
constexpr auto power = 5 * si::power[W];
|
||||
static_assert(is_of_type<quantity<reference<struct isq::power_dim, struct si::watt>{}, int>>(power));
|
||||
// clang-format off
|
||||
inline constexpr struct activity_dim : decltype(1 / isq::time_dim) {} activity_dim;
|
||||
inline constexpr struct activity : system_reference<activity_dim, si::becquerel> {} activity;
|
||||
// clang-format on
|
||||
|
||||
constexpr auto speed = 5 * si::speed[m / s];
|
||||
|
||||
// Named quantity/dimension and unit
|
||||
static_assert(
|
||||
is_of_type<quantity<reference<struct isq::speed_dim, derived_unit<struct si::metre, per<struct si::second>>>{}, int>>(
|
||||
speed));
|
||||
is_same_v<decltype(5 * si::power[W]), quantity<reference<struct isq::power_dim, struct si::watt>{}, int>>);
|
||||
|
||||
constexpr auto q = 10 * si::length[m] / (2 * si::time[s]);
|
||||
static_assert(is_of_type<quantity<reference<derived_dimension<struct isq::length_dim, per<struct isq::time_dim>>,
|
||||
derived_unit<struct si::metre, per<struct si::second>>>{},
|
||||
int>>(q));
|
||||
// Named quantity/dimension and derived (unnamed) unit
|
||||
static_assert(
|
||||
is_same_v<decltype(5 * si::speed[m / s]),
|
||||
quantity<reference<struct isq::speed_dim, derived_unit<struct si::metre, per<struct si::second>>>{}, int>>);
|
||||
|
||||
constexpr auto distance = 5 * si::speed[m / s] * (5 * si::time[s]);
|
||||
// Derived (unnamed) quantity/dimension and derived (unnamed) unit
|
||||
static_assert(is_same_v<decltype(10 * si::length[m] / (2 * si::time[s])),
|
||||
quantity<reference<derived_dimension<struct isq::length_dim, per<struct isq::time_dim>>,
|
||||
derived_unit<struct si::metre, per<struct si::second>>>{},
|
||||
int>>);
|
||||
|
||||
static_assert(is_of_type<quantity<reference<struct isq::length_dim, struct si::metre>{}, int>>(distance));
|
||||
// Base quantity as a result of dimensional transformation
|
||||
static_assert(is_same_v<decltype(5 * si::speed[m / s] * (5 * si::time[s])),
|
||||
quantity<reference<struct isq::length_dim, struct si::metre>{}, int>>);
|
||||
|
||||
constexpr auto dimensionless = 20 * si::speed[m / s] / (10 * si::length[m]) * (5 * si::time[s]);
|
||||
// Dimensionless
|
||||
static_assert(is_same_v<decltype(20 * si::speed[m / s] / (10 * si::length[m]) * (5 * si::time[s])),
|
||||
quantity<reference<struct one_dim, struct one>{}, int>>);
|
||||
|
||||
static_assert(is_of_type<quantity<reference<struct one_dim, struct one>{}, int>>(dimensionless));
|
||||
// Comparisons
|
||||
|
||||
// Named and derived dimensions (same units)
|
||||
static_assert(10 * si::length[m] / (2 * si::time[s]) == 5 * si::speed[m / s]);
|
||||
static_assert(5 * si::speed[m / s] == 10 * si::length[m] / (2 * si::time[s]));
|
||||
|
||||
// constexpr auto q1 = 10 * si::length[m] / (2 * si::time[s]) + 5 * si::speed[m / s];
|
||||
// static_assert(is_of_type<quantity<reference<derived_dimension<struct isq::length_dim, per<struct isq::time_dim>>,
|
||||
// derived_unit<struct si::metre, per<struct si::second>>>{},
|
||||
// int>>(q1));
|
||||
// Named and derived dimensions (different units)
|
||||
static_assert(10 / (2 * si::time[s]) == 5 * si::frequency[Hz]);
|
||||
static_assert(5 * si::frequency[Hz] == 10 / (2 * si::time[s]));
|
||||
|
||||
// Different named dimensions
|
||||
template<Reference auto R1, Reference auto R2>
|
||||
concept invalid_comparison = requires {
|
||||
requires !requires { 2 * R1 == 2 * R2; };
|
||||
requires !requires { 2 * R2 == 2 * R1; };
|
||||
};
|
||||
static_assert(invalid_comparison<activity[Bq], si::frequency[Hz]>);
|
||||
|
||||
// static_assert(print<decltype(10 * si::length[m] / (2 * si::time[s]) + 5 * si::speed[m / s])>());
|
||||
|
||||
// Arithmetics
|
||||
|
||||
// Named and derived dimensions (same units)
|
||||
static_assert(10 * si::length[m] / (2 * si::time[s]) + 5 * si::speed[m / s] == 10 * si::speed[m / s]);
|
||||
static_assert(5 * si::speed[m / s] + 10 * si::length[m] / (2 * si::time[s]) == 10 * si::speed[m / s]);
|
||||
static_assert(10 * si::length[m] / (2 * si::time[s]) - 5 * si::speed[m / s] == 0 * si::speed[m / s]);
|
||||
static_assert(5 * si::speed[m / s] - 10 * si::length[m] / (2 * si::time[s]) == 0 * si::speed[m / s]);
|
||||
static_assert(
|
||||
is_same_v<decltype(10 * si::length[m] / (2 * si::time[s]) + 5 * si::speed[m / s]),
|
||||
quantity<reference<struct isq::speed_dim, derived_unit<struct si::metre, per<struct si::second>>>{}, int>>);
|
||||
static_assert(
|
||||
is_same_v<decltype(5 * si::speed[m / s] + 10 * si::length[m] / (2 * si::time[s])),
|
||||
quantity<reference<struct isq::speed_dim, derived_unit<struct si::metre, per<struct si::second>>>{}, int>>);
|
||||
static_assert(
|
||||
is_same_v<decltype(10 * si::length[m] / (2 * si::time[s]) - 5 * si::speed[m / s]),
|
||||
quantity<reference<struct isq::speed_dim, derived_unit<struct si::metre, per<struct si::second>>>{}, int>>);
|
||||
static_assert(
|
||||
is_same_v<decltype(5 * si::speed[m / s] - 10 * si::length[m] / (2 * si::time[s])),
|
||||
quantity<reference<struct isq::speed_dim, derived_unit<struct si::metre, per<struct si::second>>>{}, int>>);
|
||||
|
||||
// Named and derived dimensions (different units)
|
||||
static_assert(10 / (2 * si::time[s]) + 5 * si::frequency[Hz] == 10 * si::frequency[Hz]);
|
||||
static_assert(5 * si::frequency[Hz] + 10 / (2 * si::time[s]) == 10 * si::frequency[Hz]);
|
||||
static_assert(10 / (2 * si::time[s]) - 5 * si::frequency[Hz] == 0 * si::frequency[Hz]);
|
||||
static_assert(5 * si::frequency[Hz] - 10 / (2 * si::time[s]) == 0 * si::frequency[Hz]);
|
||||
static_assert(is_same_v<decltype(10 / (2 * si::time[s]) + 5 * si::frequency[Hz]),
|
||||
quantity<reference<struct isq::frequency_dim, struct si::hertz>{}, int>>);
|
||||
static_assert(is_same_v<decltype(5 * si::frequency[Hz] + 10 / (2 * si::time[s])),
|
||||
quantity<reference<struct isq::frequency_dim, struct si::hertz>{}, int>>);
|
||||
static_assert(is_same_v<decltype(10 / (2 * si::time[s]) - 5 * si::frequency[Hz]),
|
||||
quantity<reference<struct isq::frequency_dim, struct si::hertz>{}, int>>);
|
||||
static_assert(is_same_v<decltype(5 * si::frequency[Hz] - 10 / (2 * si::time[s])),
|
||||
quantity<reference<struct isq::frequency_dim, struct si::hertz>{}, int>>);
|
||||
|
||||
// Different named dimensions
|
||||
template<typename... Ts>
|
||||
consteval bool invalid_arithmetic(Ts... ts)
|
||||
{
|
||||
return requires {
|
||||
requires !requires { (... + ts); };
|
||||
requires !requires { (... - ts); };
|
||||
};
|
||||
}
|
||||
static_assert(invalid_arithmetic(5 * activity[Bq], 5 * si::frequency[Hz]));
|
||||
static_assert(invalid_arithmetic(5 * activity[Bq], 10 / (2 * si::time[s]), 5 * si::frequency[Hz]));
|
||||
|
||||
// static_assert(quantity_of<decltype(60 * si::speed[km / h]), isq::speed_dim>);
|
||||
// static_assert(quantity_of<decltype(120 * si::length[km] / (2 * si::time[h])), isq::speed_dim>);
|
||||
// static_assert(quantity_of<decltype(120 * si::length[km] / (2 * si::time[h])), si::speed[km / h]>);
|
||||
// static_assert(!quantity_of<decltype(120 * si::length[km] / (2 * si::time[h])), si::speed[m / s]>);
|
||||
@@ -222,8 +294,6 @@ namespace units::isq::si {
|
||||
// quantity<speed[km / s]> speed3(20);
|
||||
// quantity<length[m] / si::time[s]> speed4(20);
|
||||
|
||||
template<typename T>
|
||||
void print();
|
||||
|
||||
// constexpr auto avg_speed(quantity<length[km]> d, quantity<si::time[h]> t) { return d / t; }
|
||||
|
||||
|
@@ -1,122 +0,0 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2018 Mateusz Pusz
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <units/concepts.h>
|
||||
#include <units/dimension.h>
|
||||
#include <units/unit.h>
|
||||
// #include <units/bits/equivalent.h>
|
||||
// #include <units/quantity_cast.h>
|
||||
|
||||
namespace units {
|
||||
|
||||
// template<PointOrigin O, UnitOf<typename O::dimension> U, Representation Rep>
|
||||
// class quantity_point;
|
||||
|
||||
// template<Kind K, UnitOf<typename K::dimension> U, Representation Rep>
|
||||
// class quantity_kind;
|
||||
|
||||
// template<PointKind PK, UnitOf<typename PK::dimension> U, Representation Rep>
|
||||
// class quantity_point_kind;
|
||||
|
||||
// TODO common_unit should use common_magnitude(U1::mag, U2::mag)
|
||||
|
||||
template<Dimension D1, Dimension D2>
|
||||
requires(equivalent(D1{}, D2{}))
|
||||
using common_dimension = conditional<std::is_same_v<D1, D2>, D1, detail::dim_type<D1>>;
|
||||
|
||||
template<Unit U1, Unit U2>
|
||||
// requires(equivalent<U1, U2>)
|
||||
using common_unit = U1;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename R1, typename R2>
|
||||
struct common_quantity_reference_impl;
|
||||
|
||||
template<typename R>
|
||||
struct common_quantity_reference_impl<R, R> {
|
||||
using type = R;
|
||||
};
|
||||
|
||||
template<typename D, typename U1, typename U2>
|
||||
struct common_quantity_reference_impl<reference<D, U1>, reference<D, U2>> {
|
||||
using type = reference<D, common_unit<U1, U2>>;
|
||||
};
|
||||
|
||||
template<typename R1, typename R2>
|
||||
requires(equivalent(R1::dimension, R2::dimension))
|
||||
struct common_quantity_reference_impl<R1, R2> {
|
||||
using type = reference<common_dimension<decltype(R1::dimension), decltype(R2::dimension)>,
|
||||
common_unit<decltype(R1::unit), decltype(R2::unit)>>;
|
||||
};
|
||||
|
||||
template<Quantity Q1, quantity_equivalent_to<Q1> Q2>
|
||||
using common_quantity_reference =
|
||||
TYPENAME detail::common_quantity_reference_impl<std::remove_const_t<decltype(Q1::reference)>,
|
||||
std::remove_const_t<decltype(Q2::reference)>>::type;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace units
|
||||
|
||||
namespace std {
|
||||
|
||||
template<units::Quantity Q1, units::quantity_equivalent_to<Q1> Q2>
|
||||
requires requires { typename common_type_t<typename Q1::rep, typename Q2::rep>; }
|
||||
struct common_type<Q1, Q2> {
|
||||
private:
|
||||
using ref = units::detail::common_quantity_reference<Q1, Q2>;
|
||||
public:
|
||||
using type = units::quantity<ref{}, common_type_t<typename Q1::rep, typename Q2::rep>>;
|
||||
};
|
||||
|
||||
// template<units::QuantityPoint QP1, units::QuantityPointEquivalentTo<QP1> QP2>
|
||||
// requires requires { typename common_type_t<typename QP1::rep, typename QP2::rep>; }
|
||||
// struct common_type<QP1, QP2> {
|
||||
// using type =
|
||||
// units::quantity_point<units::rebind_point_origin_dimension<
|
||||
// typename QP1::origin, typename common_type_t<typename QP1::quantity_type,
|
||||
// typename QP2::quantity_type>::dimension>,
|
||||
// typename common_type_t<typename QP1::quantity_type, typename QP2::quantity_type>::unit,
|
||||
// typename common_type_t<typename QP1::quantity_type, typename QP2::quantity_type>::rep>;
|
||||
// };
|
||||
|
||||
// template<units::QuantityKind QK1, units::QuantityKindEquivalentTo<QK1> QK2>
|
||||
// requires requires { typename common_type_t<typename QK1::rep, typename QK2::rep>; }
|
||||
// struct common_type<QK1, QK2> {
|
||||
// using type =
|
||||
// units::quantity_kind<typename QK1::kind_type,
|
||||
// typename common_type_t<typename QK1::quantity_type, typename QK2::quantity_type>::unit,
|
||||
// typename common_type_t<typename QK1::quantity_type, typename QK2::quantity_type>::rep>;
|
||||
// };
|
||||
|
||||
// template<units::QuantityPointKind QPK1, units::QuantityPointKindEquivalentTo<QPK1> QPK2>
|
||||
// requires requires { typename common_type_t<typename QPK1::rep, typename QPK2::rep>; }
|
||||
// struct common_type<QPK1, QPK2> {
|
||||
// using type = units::quantity_point_kind<
|
||||
// typename QPK1::point_kind_type,
|
||||
// typename common_type_t<typename QPK1::quantity_kind_type, typename QPK2::quantity_kind_type>::unit,
|
||||
// typename common_type_t<typename QPK1::quantity_kind_type, typename QPK2::quantity_kind_type>::rep>;
|
||||
// };
|
||||
|
||||
} // namespace std
|
@@ -102,15 +102,7 @@ inline constexpr bool is_quantity<quantity<R, Rep>> = true;
|
||||
*/
|
||||
template<typename Q, auto V>
|
||||
concept quantity_of = Quantity<Q> && ((Dimension<std::remove_const_t<decltype(V)>> && Q::dimension == V) ||
|
||||
(Reference<std::remove_const_t<decltype(V)>> && Q::dimension == V.dimension));
|
||||
|
||||
/**
|
||||
* @brief A concept matching two equivalent quantities
|
||||
*
|
||||
* Satisfied by quantities having equivalent dimensions and units.
|
||||
*/
|
||||
template<typename Q1, typename Q2>
|
||||
concept quantity_equivalent_to = Quantity<Q1> && equivalent(Q1::dimension, Q2::dimension) &&
|
||||
equivalent(Q1::unit, Q2::unit);
|
||||
(Reference<std::remove_const_t<decltype(V)>> && Q::dimension == V.dimension &&
|
||||
Q::unit == V.unit));
|
||||
|
||||
} // namespace units
|
||||
|
@@ -195,6 +195,23 @@ template<Dimension D1, Dimension D2>
|
||||
return is_same_v<detail::dim_type<D1>, detail::dim_type<D2>>;
|
||||
}
|
||||
|
||||
template<Dimension D1, Dimension D2>
|
||||
[[nodiscard]] consteval bool convertible(D1, D2)
|
||||
{
|
||||
return std::derived_from<D1, D2> || std::derived_from<D2, D1>;
|
||||
}
|
||||
|
||||
// TODO consider adding the support for text output of the dimensional equation
|
||||
|
||||
} // namespace units
|
||||
|
||||
namespace std {
|
||||
|
||||
template<units::Dimension D1, units::Dimension D2>
|
||||
requires(units::convertible(D1{}, D2{}))
|
||||
struct common_type<D1, D2> {
|
||||
using type = ::units::conditional<std::derived_from<std::remove_const_t<D1>, std::remove_const_t<D2>>,
|
||||
std::remove_const_t<D1>, std::remove_const_t<D2>>;
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
@@ -23,13 +23,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
// #include <units/bits/common_type.h>
|
||||
// #include <units/generic/dimensionless.h>
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
// #include <units/quantity_cast.h>
|
||||
#include <units/quantity_cast.h>
|
||||
// #include <units/ratio.h>
|
||||
#include <units/bits/common_type.h>
|
||||
#include <units/concepts.h>
|
||||
#include <units/customization_points.h>
|
||||
#include <units/dimension.h>
|
||||
@@ -38,8 +34,7 @@
|
||||
#include <compare>
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
// #include <units/reference.h>
|
||||
// #include <utility>
|
||||
#include <utility>
|
||||
|
||||
namespace units {
|
||||
|
||||
@@ -73,8 +68,9 @@ concept harmonic_ = // exposition only
|
||||
// Quantity<QFrom> && Quantity<QTo> && is_integral(detail::quantity_magnitude<QFrom> / detail::quantity_magnitude<QTo>);
|
||||
|
||||
template<typename QFrom, typename QTo>
|
||||
concept safe_castable_to_ = // exposition only
|
||||
Quantity<QFrom> && quantity_of<QTo, QFrom::dimension> && scalable_with_<typename QFrom::rep, typename QTo::rep> &&
|
||||
concept quantity_convertible_to_ = // exposition only
|
||||
Quantity<QFrom> && Quantity<QTo> && convertible(QFrom::dimension, QTo::dimension) &&
|
||||
convertible(QFrom::unit, QTo::unit) && scalable_with_<typename QFrom::rep, typename QTo::rep> &&
|
||||
(floating_point_<QTo> || (!floating_point_<QFrom> && harmonic_<QFrom, QTo>));
|
||||
|
||||
template<typename Func, typename T, typename U>
|
||||
@@ -161,13 +157,13 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
template<safe_castable_to_<quantity> Q>
|
||||
template<quantity_convertible_to_<quantity> Q>
|
||||
constexpr explicit(false) quantity(const Q& q) : number_(quantity_cast<quantity>(q).number())
|
||||
{
|
||||
}
|
||||
|
||||
template<QuantityLike Q>
|
||||
requires(safe_castable_to_<quantity_like_type<Q>, quantity>)
|
||||
requires(quantity_convertible_to_<quantity_like_type<Q>, quantity>)
|
||||
constexpr explicit quantity(const Q& q) : quantity(quantity_like_type<Q>(quantity_like_traits<Q>::number(q)))
|
||||
{
|
||||
}
|
||||
@@ -423,7 +419,7 @@ public:
|
||||
requires(!Quantity<Value>) && (invoke_result_convertible_to_<rep, std::divides<>, const Value&, rep>)
|
||||
[[nodiscard]] friend constexpr Quantity auto operator/(const Value& v, const quantity& q)
|
||||
{
|
||||
return detail::make_quantity<dimensionless[one] / reference>(v / q.number());
|
||||
return detail::make_quantity<dimensionless[::units::one] / reference>(v / q.number());
|
||||
}
|
||||
|
||||
template<typename Value>
|
||||
@@ -475,20 +471,20 @@ explicit quantity(Q)
|
||||
typename quantity_like_traits<Q>::rep>;
|
||||
|
||||
// non-member binary operators
|
||||
template<Quantity Q1, quantity_equivalent_to<Q1> Q2>
|
||||
template<Quantity Q1, std::convertible_to<Q1> Q2>
|
||||
requires(quantity_value_for_<std::plus<>, typename Q1::rep, typename Q2::rep>)
|
||||
[[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs)
|
||||
{
|
||||
using ref = detail::common_quantity_reference<Q1, Q2>;
|
||||
using ref = std::common_type_t<decltype(Q1::reference), decltype(Q2::reference)>;
|
||||
using ret = quantity<ref{}, decltype(lhs.number() + rhs.number())>;
|
||||
return ret(ret(lhs).number() + ret(rhs).number());
|
||||
}
|
||||
|
||||
template<Quantity Q1, quantity_equivalent_to<Q1> Q2>
|
||||
template<Quantity Q1, std::convertible_to<Q1> Q2>
|
||||
requires(quantity_value_for_<std::minus<>, typename Q1::rep, typename Q2::rep>)
|
||||
[[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs)
|
||||
{
|
||||
using ref = detail::common_quantity_reference<Q1, Q2>;
|
||||
using ref = std::common_type_t<decltype(Q1::reference), decltype(Q2::reference)>;
|
||||
using ret = quantity<ref{}, decltype(lhs.number() - rhs.number())>;
|
||||
return ret(ret(lhs).number() - ret(rhs).number());
|
||||
}
|
||||
@@ -510,7 +506,7 @@ template<Quantity Q1, Quantity Q2>
|
||||
|
||||
template<Quantity Q1, Quantity Q2>
|
||||
requires(!floating_point_<typename Q1::rep>) && (!floating_point_<typename Q2::rep>) &&
|
||||
(quantity_equivalent_to<Q2, Q1> || quantity_of<Q2, one_dim>) &&
|
||||
(std::convertible_to<Q2, Q1> || quantity_of<Q2, one_dim>) &&
|
||||
(quantity_value_for_<std::modulus<>, typename Q1::rep, typename Q2::rep>)
|
||||
[[nodiscard]] constexpr Quantity auto operator%(const Q1& lhs, const Q2& rhs)
|
||||
{
|
||||
@@ -520,20 +516,36 @@ template<Quantity Q1, Quantity Q2>
|
||||
return ret(lhs.number() % rhs.number());
|
||||
}
|
||||
|
||||
template<Quantity Q1, quantity_equivalent_to<Q1> Q2>
|
||||
template<Quantity Q1, std::convertible_to<Q1> Q2>
|
||||
requires std::three_way_comparable_with<typename Q1::rep, typename Q2::rep>
|
||||
[[nodiscard]] constexpr auto operator<=>(const Q1& lhs, const Q2& rhs)
|
||||
{
|
||||
using cq = std::common_type_t<Q1, Q2>;
|
||||
return cq(lhs).number() <=> cq(rhs).number();
|
||||
using ref = std::common_type_t<decltype(Q1::reference), decltype(Q2::reference)>;
|
||||
return quantity_cast<ref>(lhs).number() <=> quantity_cast<ref>(rhs).number();
|
||||
}
|
||||
|
||||
template<Quantity Q1, quantity_equivalent_to<Q1> Q2>
|
||||
template<Quantity Q1, std::convertible_to<Q1> Q2>
|
||||
requires std::equality_comparable_with<typename Q1::rep, typename Q2::rep>
|
||||
[[nodiscard]] constexpr bool operator==(const Q1& lhs, const Q2& rhs)
|
||||
{
|
||||
using cq = std::common_type_t<Q1, Q2>;
|
||||
return cq(lhs).number() == cq(rhs).number();
|
||||
using ref = std::common_type_t<decltype(Q1::reference), decltype(Q2::reference)>;
|
||||
return quantity_cast<ref{}>(lhs).number() == quantity_cast<ref{}>(rhs).number();
|
||||
}
|
||||
|
||||
} // namespace units
|
||||
|
||||
namespace std {
|
||||
|
||||
template<units::Quantity Q1, units::Quantity Q2>
|
||||
requires requires {
|
||||
typename common_type_t<remove_const_t<decltype(Q1::reference)>, remove_const_t<decltype(Q2::reference)>>;
|
||||
typename common_type_t<typename Q1::rep, typename Q2::rep>;
|
||||
}
|
||||
struct common_type<Q1, Q2> {
|
||||
private:
|
||||
using ref = common_type_t<remove_const_t<decltype(Q1::reference)>, remove_const_t<decltype(Q2::reference)>>;
|
||||
public:
|
||||
using type = units::quantity<ref{}, common_type_t<typename Q1::rep, typename Q2::rep>>;
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
@@ -22,12 +22,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <units/bits/dimension_op.h>
|
||||
#include <units/bits/external/type_traits.h>
|
||||
#include <units/concepts.h>
|
||||
#include <units/customization_points.h>
|
||||
#include <units/dimension.h>
|
||||
#include <units/magnitude.h>
|
||||
#include <units/reference.h>
|
||||
#include <units/unit.h>
|
||||
|
||||
UNITS_DIAGNOSTIC_PUSH
|
||||
// warning C4244: 'argument': conversion from 'intmax_t' to 'T', possible loss of data with T=int
|
||||
@@ -35,53 +36,53 @@ UNITS_DIAGNOSTIC_IGNORE_LOSS_OF_DATA
|
||||
|
||||
namespace units {
|
||||
|
||||
template<Dimension D, UnitOf<D> U, Representation Rep>
|
||||
template<Reference auto R, Representation Rep>
|
||||
class quantity;
|
||||
|
||||
template<PointOrigin O, UnitOf<typename O::dimension> U, Representation Rep>
|
||||
class quantity_point;
|
||||
// template<PointOrigin O, UnitOf<typename O::dimension> U, Representation Rep>
|
||||
// class quantity_point;
|
||||
|
||||
template<Kind K, UnitOf<typename K::dimension> U, Representation Rep>
|
||||
class quantity_kind;
|
||||
// template<Kind K, UnitOf<typename K::dimension> U, Representation Rep>
|
||||
// class quantity_kind;
|
||||
|
||||
template<PointKind PK, UnitOf<typename PK::dimension> U, Representation Rep>
|
||||
class quantity_point_kind;
|
||||
// template<PointKind PK, UnitOf<typename PK::dimension> U, Representation Rep>
|
||||
// class quantity_point_kind;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<Quantity Q>
|
||||
inline constexpr Magnitude auto quantity_magnitude = decltype(Q::reference)::mag;
|
||||
// template<Quantity Q>
|
||||
// inline constexpr Magnitude auto quantity_magnitude = decltype(Q::reference)::mag;
|
||||
|
||||
template<typename QFrom, typename QTo>
|
||||
inline constexpr Magnitude auto cast_magnitude = [] {
|
||||
using FromU = TYPENAME QFrom::unit;
|
||||
using ToU = TYPENAME QTo::unit;
|
||||
if constexpr (same_unit_reference<FromU, ToU>::value) {
|
||||
return FromU::mag / ToU::mag;
|
||||
} else {
|
||||
return quantity_magnitude<QFrom> / quantity_magnitude<QTo>;
|
||||
}
|
||||
}();
|
||||
// template<typename QFrom, typename QTo>
|
||||
// inline constexpr Magnitude auto cast_magnitude = [] {
|
||||
// using FromU = TYPENAME QFrom::unit;
|
||||
// using ToU = TYPENAME QTo::unit;
|
||||
// if constexpr (same_unit_reference<FromU, ToU>::value) {
|
||||
// return FromU::mag / ToU::mag;
|
||||
// } else {
|
||||
// return quantity_magnitude<QFrom> / quantity_magnitude<QTo>;
|
||||
// }
|
||||
// }();
|
||||
|
||||
template<typename From, typename To>
|
||||
struct cast_traits;
|
||||
// template<typename From, typename To>
|
||||
// struct cast_traits;
|
||||
|
||||
template<typename From, typename To>
|
||||
requires common_type_with_<std::common_type_t<From, To>, std::intmax_t>
|
||||
struct cast_traits<From, To> {
|
||||
using ratio_type = std::common_type_t<std::common_type_t<From, To>, std::intmax_t>;
|
||||
using rep_type = ratio_type;
|
||||
};
|
||||
// template<typename From, typename To>
|
||||
// requires common_type_with_<std::common_type_t<From, To>, std::intmax_t>
|
||||
// struct cast_traits<From, To> {
|
||||
// using ratio_type = std::common_type_t<std::common_type_t<From, To>, std::intmax_t>;
|
||||
// using rep_type = ratio_type;
|
||||
// };
|
||||
|
||||
template<typename From, typename To>
|
||||
requires(!common_type_with_<std::common_type_t<From, To>, std::intmax_t> &&
|
||||
scalable_number_<std::common_type_t<From, To>, std::intmax_t> &&
|
||||
requires { typename std::common_type_t<From, To>::value_type; } &&
|
||||
common_type_with_<typename std::common_type_t<From, To>::value_type, std::intmax_t>)
|
||||
struct cast_traits<From, To> {
|
||||
using ratio_type = std::common_type_t<typename std::common_type_t<From, To>::value_type, std::intmax_t>;
|
||||
using rep_type = std::common_type_t<From, To>;
|
||||
};
|
||||
// template<typename From, typename To>
|
||||
// requires(!common_type_with_<std::common_type_t<From, To>, std::intmax_t> &&
|
||||
// scalable_number_<std::common_type_t<From, To>, std::intmax_t> &&
|
||||
// requires { typename std::common_type_t<From, To>::value_type; } &&
|
||||
// common_type_with_<typename std::common_type_t<From, To>::value_type, std::intmax_t>)
|
||||
// struct cast_traits<From, To> {
|
||||
// using ratio_type = std::common_type_t<typename std::common_type_t<From, To>::value_type, std::intmax_t>;
|
||||
// using rep_type = std::common_type_t<From, To>;
|
||||
// };
|
||||
|
||||
} // namespace detail
|
||||
|
||||
@@ -97,59 +98,28 @@ struct cast_traits<From, To> {
|
||||
*
|
||||
* @tparam To a target quantity type to cast to
|
||||
*/
|
||||
template<Quantity To, typename D, typename U, scalable_with_<typename To::rep> Rep>
|
||||
requires QuantityOf<To, D> && (std::constructible_from<typename To::rep, std::common_type_t<typename To::rep, Rep>>)
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
||||
template<Quantity To, auto R, scalable_with_<typename To::rep> Rep>
|
||||
requires(convertible(R, To::reference))
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
|
||||
{
|
||||
using traits = detail::cast_traits<Rep, typename To::rep>;
|
||||
using ratio_type = TYPENAME traits::ratio_type;
|
||||
using rep_type = TYPENAME traits::rep_type;
|
||||
// TODO implement same unit magnitude check
|
||||
if constexpr (std::same_as<decltype(R.unit), decltype(To::unit)>) {
|
||||
return To(static_cast<TYPENAME To::rep>(q.number()));
|
||||
} else {
|
||||
// using traits = detail::cast_traits<Rep, typename To::rep>;
|
||||
// using ratio_type = TYPENAME traits::ratio_type;
|
||||
// using rep_type = TYPENAME traits::rep_type;
|
||||
|
||||
constexpr Magnitude auto c_mag = detail::cast_magnitude<quantity<D, U, Rep>, To>;
|
||||
constexpr Magnitude auto num = numerator(c_mag);
|
||||
constexpr Magnitude auto den = denominator(c_mag);
|
||||
constexpr Magnitude auto irr = c_mag * (den / num);
|
||||
// constexpr Magnitude auto c_mag = detail::cast_magnitude<quantity<D, U, Rep>, To>;
|
||||
// constexpr Magnitude auto num = numerator(c_mag);
|
||||
// constexpr Magnitude auto den = denominator(c_mag);
|
||||
// constexpr Magnitude auto irr = c_mag * (den / num);
|
||||
|
||||
constexpr auto val = [](Magnitude auto m) { return get_value<ratio_type>(m); };
|
||||
return To(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.number()) * val(num) / val(den) * val(irr)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity
|
||||
*
|
||||
* Implicit conversions between quantities of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
|
||||
*
|
||||
* This cast gets only the target dimension to cast to. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_cast<units::isq::si::dim_acceleration>(200_q_Gal);
|
||||
*
|
||||
* @tparam ToD a dimension type to use for a target quantity
|
||||
*/
|
||||
template<Dimension ToD, typename D, typename U, typename Rep>
|
||||
requires equivalent<ToD, D>
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
||||
{
|
||||
return quantity_cast<quantity<ToD, downcast_unit<ToD, U::mag>, Rep>>(q);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity
|
||||
*
|
||||
* Implicit conversions between quantities of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
|
||||
*
|
||||
* This cast gets only the target unit to cast to. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_cast<units::isq::si::second>(1_q_ms);
|
||||
*
|
||||
* @tparam ToU a unit type to use for a target quantity
|
||||
*/
|
||||
template<Unit ToU, typename D, typename U, typename Rep>
|
||||
requires UnitOf<ToU, D>
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
||||
{
|
||||
return quantity_cast<quantity<D, ToU, Rep>>(q);
|
||||
// constexpr auto val = [](Magnitude auto m) { return get_value<ratio_type>(m); };
|
||||
// return To(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.number()) * val(num) / val(den) * val(irr)));
|
||||
// TODO implement that
|
||||
return q;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,11 +138,52 @@ template<Unit ToU, typename D, typename U, typename Rep>
|
||||
* @tparam ToD a dimension type to use for a target quantity
|
||||
* @tparam ToU a unit type to use for a target quantity
|
||||
*/
|
||||
template<Dimension ToD, Unit ToU, typename D, typename U, typename Rep>
|
||||
requires equivalent<ToD, D> && UnitOf<ToU, ToD>
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
||||
template<Reference auto ToR, auto R, typename Rep>
|
||||
requires(convertible(ToR, R))
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
|
||||
{
|
||||
return quantity_cast<quantity<ToD, ToU, Rep>>(q);
|
||||
return quantity_cast<quantity<ToR, Rep>>(q);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity
|
||||
*
|
||||
* Implicit conversions between quantities of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
|
||||
*
|
||||
* This cast gets only the target dimension to cast to. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_cast<units::isq::si::dim_acceleration>(200_q_Gal);
|
||||
*
|
||||
* @tparam ToD a dimension type to use for a target quantity
|
||||
*/
|
||||
template<Dimension auto ToD, auto R, typename Rep>
|
||||
requires(convertible(ToD, R.dimension))
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
|
||||
{
|
||||
constexpr reference<std::remove_const_t<decltype(ToD)>, std::remove_const_t<decltype(R.unit)>> r;
|
||||
return quantity_cast<quantity<r, Rep>>(q);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity
|
||||
*
|
||||
* Implicit conversions between quantities of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
|
||||
*
|
||||
* This cast gets only the target unit to cast to. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_cast<units::isq::si::second>(1_q_ms);
|
||||
*
|
||||
* @tparam ToU a unit type to use for a target quantity
|
||||
*/
|
||||
template<Unit auto ToU, auto R, typename Rep>
|
||||
requires(convertible(ToU, R.unit))
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
|
||||
{
|
||||
constexpr reference<std::remove_const_t<decltype(R.dimension)>, std::remove_const_t<decltype(ToU)>> r;
|
||||
return quantity_cast<quantity<r, Rep>>(q);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,185 +198,187 @@ template<Dimension ToD, Unit ToU, typename D, typename U, typename Rep>
|
||||
*
|
||||
* @tparam ToRep a representation type to use for a target quantity
|
||||
*/
|
||||
template<Representation ToRep, typename D, typename U, scalable_with_<ToRep> Rep>
|
||||
requires(std::constructible_from<ToRep, std::common_type_t<ToRep, Rep>>)
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q)
|
||||
template<Representation ToRep, auto R, scalable_with_<ToRep> Rep>
|
||||
// requires(std::constructible_from<ToRep, std::common_type_t<ToRep, Rep>>)
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
|
||||
{
|
||||
return quantity_cast<quantity<D, U, ToRep>>(q);
|
||||
return To(static_cast<ToRep>(q.number()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity point
|
||||
*
|
||||
* Implicit conversions between quantity points of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
*
|
||||
* This cast gets the target quantity point type to cast to or anything that works for quantity_cast. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_point_cast<decltype(quantity_point{0_q_s})>(quantity_point{1_q_ms});
|
||||
* auto q1 = units::quantity_point_cast<units::isq::si::time<units::isq::si::second>>(quantity_point{1_q_ms});
|
||||
* auto q1 = units::quantity_point_cast<units::isq::si::dim_acceleration>(quantity_point{200_q_Gal});
|
||||
* auto q1 = units::quantity_point_cast<units::isq::si::second>(quantity_point{1_q_ms});
|
||||
* auto q1 = units::quantity_point_cast<int>(quantity_point{1_q_ms});
|
||||
*
|
||||
* @tparam CastSpec a target quantity point type to cast to or anything that works for quantity_cast
|
||||
*/
|
||||
template<typename CastSpec, typename O, typename U, typename Rep>
|
||||
[[nodiscard]] constexpr auto quantity_point_cast(const quantity_point<O, U, Rep>& qp)
|
||||
requires requires {
|
||||
requires is_specialization_of<CastSpec, quantity_point>;
|
||||
requires requires { quantity_cast<typename CastSpec::quantity_type>(qp.relative()); };
|
||||
requires equivalent<O, typename CastSpec::origin>;
|
||||
} || // TODO: Simplify when Clang catches up.
|
||||
requires { quantity_cast<CastSpec>(qp.relative()); }
|
||||
{
|
||||
if constexpr (is_specialization_of<CastSpec, quantity_point>)
|
||||
return quantity_point(quantity_cast<typename CastSpec::quantity_type>(qp.relative()));
|
||||
else
|
||||
return quantity_point(quantity_cast<CastSpec>(qp.relative()));
|
||||
}
|
||||
// /**
|
||||
// * @brief Explicit cast of a quantity point
|
||||
// *
|
||||
// * Implicit conversions between quantity points of different types are allowed only for "safe"
|
||||
// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
// *
|
||||
// * This cast gets the target quantity point type to cast to or anything that works for quantity_cast. For example:
|
||||
// *
|
||||
// * auto q1 = units::quantity_point_cast<decltype(quantity_point{0_q_s})>(quantity_point{1_q_ms});
|
||||
// * auto q1 = units::quantity_point_cast<units::isq::si::time<units::isq::si::second>>(quantity_point{1_q_ms});
|
||||
// * auto q1 = units::quantity_point_cast<units::isq::si::dim_acceleration>(quantity_point{200_q_Gal});
|
||||
// * auto q1 = units::quantity_point_cast<units::isq::si::second>(quantity_point{1_q_ms});
|
||||
// * auto q1 = units::quantity_point_cast<int>(quantity_point{1_q_ms});
|
||||
// *
|
||||
// * @tparam CastSpec a target quantity point type to cast to or anything that works for quantity_cast
|
||||
// */
|
||||
// template<typename CastSpec, typename O, typename U, typename Rep>
|
||||
// [[nodiscard]] constexpr auto quantity_point_cast(const quantity_point<O, U, Rep>& qp)
|
||||
// requires requires {
|
||||
// requires is_specialization_of<CastSpec, quantity_point>;
|
||||
// requires requires { quantity_cast<typename CastSpec::quantity_type>(qp.relative()); };
|
||||
// requires equivalent<O, typename CastSpec::origin>;
|
||||
// } || // TODO: Simplify when Clang catches up.
|
||||
// requires { quantity_cast<CastSpec>(qp.relative()); }
|
||||
// {
|
||||
// if constexpr (is_specialization_of<CastSpec, quantity_point>)
|
||||
// return quantity_point(quantity_cast<typename CastSpec::quantity_type>(qp.relative()));
|
||||
// else
|
||||
// return quantity_point(quantity_cast<CastSpec>(qp.relative()));
|
||||
// }
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity point
|
||||
*
|
||||
* Implicit conversions between quantity points of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
*
|
||||
* This cast gets both the target dimension and unit to cast to. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_point_cast<units::isq::si::dim_speed, units::isq::si::kilometre_per_hour>(v1);
|
||||
*
|
||||
* @note This cast is especially useful when working with quantity points of unknown dimensions
|
||||
* (@c unknown_dimension).
|
||||
*
|
||||
* @tparam ToD a dimension type to use for a target quantity
|
||||
* @tparam ToU a unit type to use for a target quantity
|
||||
*/
|
||||
template<Dimension ToD, Unit ToU, typename O, typename U, typename Rep>
|
||||
requires equivalent<ToD, typename O::dimension> && UnitOf<ToU, ToD> && RebindablePointOriginFor<O, ToD>
|
||||
[[nodiscard]] constexpr auto quantity_point_cast(const quantity_point<O, U, Rep>& q)
|
||||
{
|
||||
return quantity_point_cast<quantity_point<rebind_point_origin_dimension<O, ToD>, ToU, Rep>>(q);
|
||||
}
|
||||
// /**
|
||||
// * @brief Explicit cast of a quantity point
|
||||
// *
|
||||
// * Implicit conversions between quantity points of different types are allowed only for "safe"
|
||||
// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
// *
|
||||
// * This cast gets both the target dimension and unit to cast to. For example:
|
||||
// *
|
||||
// * auto q1 = units::quantity_point_cast<units::isq::si::dim_speed, units::isq::si::kilometre_per_hour>(v1);
|
||||
// *
|
||||
// * @note This cast is especially useful when working with quantity points of unknown dimensions
|
||||
// * (@c unknown_dimension).
|
||||
// *
|
||||
// * @tparam ToD a dimension type to use for a target quantity
|
||||
// * @tparam ToU a unit type to use for a target quantity
|
||||
// */
|
||||
// template<Dimension ToD, Unit ToU, typename O, typename U, typename Rep>
|
||||
// requires equivalent<ToD, typename O::dimension> && UnitOf<ToU, ToD> && RebindablePointOriginFor<O, ToD>
|
||||
// [[nodiscard]] constexpr auto quantity_point_cast(const quantity_point<O, U, Rep>& q)
|
||||
// {
|
||||
// return quantity_point_cast<quantity_point<rebind_point_origin_dimension<O, ToD>, ToU, Rep>>(q);
|
||||
// }
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity kind
|
||||
*
|
||||
* Implicit conversions between quantity kinds of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
*
|
||||
* This cast gets the target (quantity) kind type to cast to or anything that works for quantity_cast. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_kind_cast<decltype(ns::width{1 * m})>(quantity_kind{ns::width{1 * mm});
|
||||
* auto q1 = units::quantity_kind_cast<ns::height_kind>(ns::width{1 * m});
|
||||
* auto q1 = units::quantity_kind_cast<units::isq::si::length<units::isq::si::metre>>(ns::width{1 * mm});
|
||||
* auto q1 = units::quantity_kind_cast<units::isq::si::dim_acceleration>(ns::rate_of_climb{200 * Gal});
|
||||
* auto q1 = units::quantity_kind_cast<units::isq::si::metre>(ns::width{1 * mm});
|
||||
* auto q1 = units::quantity_kind_cast<int>(ns::width{1.0 * mm});
|
||||
*
|
||||
* @tparam CastSpec a target (quantity) kind type to cast to or anything that works for quantity_cast
|
||||
*/
|
||||
template<typename CastSpec, typename K, typename U, typename Rep>
|
||||
[[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind<K, U, Rep>& qk)
|
||||
requires requires {
|
||||
requires is_specialization_of<CastSpec, quantity_kind>;
|
||||
requires requires { quantity_cast<typename CastSpec::quantity_type>(qk.common()); };
|
||||
} || requires {
|
||||
requires Kind<CastSpec>;
|
||||
requires UnitOf<U, typename CastSpec::dimension>;
|
||||
} || requires { quantity_cast<CastSpec>(qk.common()); } // TODO: Simplify when Clang catches up.
|
||||
{
|
||||
if constexpr (is_specialization_of<CastSpec, quantity_kind>)
|
||||
return CastSpec(quantity_cast<typename CastSpec::quantity_type>(qk.common()));
|
||||
else if constexpr (Kind<CastSpec>)
|
||||
return quantity_kind<CastSpec, U, Rep>(qk.common());
|
||||
else {
|
||||
auto q{quantity_cast<CastSpec>(qk.common())};
|
||||
using Q = decltype(q);
|
||||
return quantity_kind<K, typename Q::unit, typename Q::rep>(static_cast<Q&&>(q));
|
||||
}
|
||||
}
|
||||
// /**
|
||||
// * @brief Explicit cast of a quantity kind
|
||||
// *
|
||||
// * Implicit conversions between quantity kinds of different types are allowed only for "safe"
|
||||
// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
// *
|
||||
// * This cast gets the target (quantity) kind type to cast to or anything that works for quantity_cast. For example:
|
||||
// *
|
||||
// * auto q1 = units::quantity_kind_cast<decltype(ns::width{1 * m})>(quantity_kind{ns::width{1 * mm});
|
||||
// * auto q1 = units::quantity_kind_cast<ns::height_kind>(ns::width{1 * m});
|
||||
// * auto q1 = units::quantity_kind_cast<units::isq::si::length<units::isq::si::metre>>(ns::width{1 * mm});
|
||||
// * auto q1 = units::quantity_kind_cast<units::isq::si::dim_acceleration>(ns::rate_of_climb{200 * Gal});
|
||||
// * auto q1 = units::quantity_kind_cast<units::isq::si::metre>(ns::width{1 * mm});
|
||||
// * auto q1 = units::quantity_kind_cast<int>(ns::width{1.0 * mm});
|
||||
// *
|
||||
// * @tparam CastSpec a target (quantity) kind type to cast to or anything that works for quantity_cast
|
||||
// */
|
||||
// template<typename CastSpec, typename K, typename U, typename Rep>
|
||||
// [[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind<K, U, Rep>& qk)
|
||||
// requires requires {
|
||||
// requires is_specialization_of<CastSpec, quantity_kind>;
|
||||
// requires requires { quantity_cast<typename CastSpec::quantity_type>(qk.common()); };
|
||||
// } || requires {
|
||||
// requires Kind<CastSpec>;
|
||||
// requires UnitOf<U, typename CastSpec::dimension>;
|
||||
// } || requires { quantity_cast<CastSpec>(qk.common()); } // TODO: Simplify when Clang catches up.
|
||||
// {
|
||||
// if constexpr (is_specialization_of<CastSpec, quantity_kind>)
|
||||
// return CastSpec(quantity_cast<typename CastSpec::quantity_type>(qk.common()));
|
||||
// else if constexpr (Kind<CastSpec>)
|
||||
// return quantity_kind<CastSpec, U, Rep>(qk.common());
|
||||
// else {
|
||||
// auto q{quantity_cast<CastSpec>(qk.common())};
|
||||
// using Q = decltype(q);
|
||||
// return quantity_kind<K, typename Q::unit, typename Q::rep>(static_cast<Q&&>(q));
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity kind
|
||||
*
|
||||
* Implicit conversions between quantity kinds of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
*
|
||||
* This cast gets both the target kind and unit to cast to. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_kind_cast<ns::height_kind, units::isq::si::kilometre>(w);
|
||||
*
|
||||
* @note This cast is especially useful when working with quantity kinds of unknown kind.
|
||||
*
|
||||
* @tparam ToK the kind type to use for the target quantity
|
||||
* @tparam ToU the unit type to use for the target quantity
|
||||
*/
|
||||
template<Kind ToK, Unit ToU, typename K, typename U, typename Rep>
|
||||
requires equivalent<typename ToK::dimension, typename K::dimension> && UnitOf<ToU, typename ToK::dimension>
|
||||
[[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind<K, U, Rep>& qk)
|
||||
{
|
||||
return quantity_kind_cast<quantity_kind<ToK, ToU, Rep>>(qk);
|
||||
}
|
||||
// /**
|
||||
// * @brief Explicit cast of a quantity kind
|
||||
// *
|
||||
// * Implicit conversions between quantity kinds of different types are allowed only for "safe"
|
||||
// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
// *
|
||||
// * This cast gets both the target kind and unit to cast to. For example:
|
||||
// *
|
||||
// * auto q1 = units::quantity_kind_cast<ns::height_kind, units::isq::si::kilometre>(w);
|
||||
// *
|
||||
// * @note This cast is especially useful when working with quantity kinds of unknown kind.
|
||||
// *
|
||||
// * @tparam ToK the kind type to use for the target quantity
|
||||
// * @tparam ToU the unit type to use for the target quantity
|
||||
// */
|
||||
// template<Kind ToK, Unit ToU, typename K, typename U, typename Rep>
|
||||
// requires equivalent<typename ToK::dimension, typename K::dimension> && UnitOf<ToU, typename ToK::dimension>
|
||||
// [[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind<K, U, Rep>& qk)
|
||||
// {
|
||||
// return quantity_kind_cast<quantity_kind<ToK, ToU, Rep>>(qk);
|
||||
// }
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity point kind
|
||||
*
|
||||
* Implicit conversions between quantity point kinds of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
*
|
||||
* This cast gets the target (quantity) point kind type to cast to or anything that works for quantity_kind_cast. For
|
||||
* example:
|
||||
*
|
||||
* auto q1 = units::quantity_point_kind_cast<decltype(ns::x_coordinate{1 * m))>(ns::x_coordinate{1 * mm});
|
||||
* auto q1 = units::quantity_point_kind_cast<decltype(ns::width{1 * m})>(ns::x_coordinate{1 * mm});
|
||||
* auto q1 = units::quantity_point_kind_cast<ns::y_coordinate_kind>(ns::x_coordinate{1 * m});
|
||||
* auto q1 = units::quantity_point_kind_cast<ns::height_kind>(ns::x_coordinate{1 * m});
|
||||
* auto q1 = units::quantity_point_kind_cast<units::isq::si::length<units::isq::si::metre>>(ns::x_coordinate{1 * mm});
|
||||
* auto q1 = units::quantity_point_kind_cast<units::isq::si::dim_acceleration>(quantity_point_kind(ns::rate_of_climb{200
|
||||
* * Gal})); auto q1 = units::quantity_point_kind_cast<units::isq::si::metre>(ns::x_coordinate{1 * mm}); auto q1 =
|
||||
* units::quantity_point_kind_cast<int>(ns::x_coordinate{1.0 * mm});
|
||||
*
|
||||
* @tparam CastSpec a target (quantity) point kind type to cast to or anything that works for quantity_kind_cast
|
||||
*/
|
||||
template<typename CastSpec, typename PK, typename U, typename Rep>
|
||||
[[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind<PK, U, Rep>& qpk)
|
||||
requires requires {
|
||||
requires is_specialization_of<CastSpec, quantity_point_kind>;
|
||||
requires requires { quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative()); };
|
||||
requires equivalent<typename PK::origin, typename CastSpec::point_kind_type::origin>;
|
||||
} || requires { requires PointKind<CastSpec> && UnitOf<U, typename CastSpec::dimension>; } ||
|
||||
requires { quantity_kind_cast<CastSpec>(qpk.relative()); } // TODO: Simplify when Clang catches up.
|
||||
{
|
||||
if constexpr (is_specialization_of<CastSpec, quantity_point_kind>)
|
||||
return CastSpec(quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative()));
|
||||
else if constexpr (PointKind<CastSpec>)
|
||||
return quantity_point_kind(quantity_kind_cast<typename CastSpec::base_kind>(qpk.relative()));
|
||||
else
|
||||
return quantity_point_kind(quantity_kind_cast<CastSpec>(qpk.relative()));
|
||||
}
|
||||
// /**
|
||||
// * @brief Explicit cast of a quantity point kind
|
||||
// *
|
||||
// * Implicit conversions between quantity point kinds of different types are allowed only for "safe"
|
||||
// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
// *
|
||||
// * This cast gets the target (quantity) point kind type to cast to or anything that works for quantity_kind_cast. For
|
||||
// * example:
|
||||
// *
|
||||
// * auto q1 = units::quantity_point_kind_cast<decltype(ns::x_coordinate{1 * m))>(ns::x_coordinate{1 * mm});
|
||||
// * auto q1 = units::quantity_point_kind_cast<decltype(ns::width{1 * m})>(ns::x_coordinate{1 * mm});
|
||||
// * auto q1 = units::quantity_point_kind_cast<ns::y_coordinate_kind>(ns::x_coordinate{1 * m});
|
||||
// * auto q1 = units::quantity_point_kind_cast<ns::height_kind>(ns::x_coordinate{1 * m});
|
||||
// * auto q1 = units::quantity_point_kind_cast<units::isq::si::length<units::isq::si::metre>>(ns::x_coordinate{1 *
|
||||
// mm});
|
||||
// * auto q1 =
|
||||
// units::quantity_point_kind_cast<units::isq::si::dim_acceleration>(quantity_point_kind(ns::rate_of_climb{200
|
||||
// * * Gal})); auto q1 = units::quantity_point_kind_cast<units::isq::si::metre>(ns::x_coordinate{1 * mm}); auto q1 =
|
||||
// * units::quantity_point_kind_cast<int>(ns::x_coordinate{1.0 * mm});
|
||||
// *
|
||||
// * @tparam CastSpec a target (quantity) point kind type to cast to or anything that works for quantity_kind_cast
|
||||
// */
|
||||
// template<typename CastSpec, typename PK, typename U, typename Rep>
|
||||
// [[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind<PK, U, Rep>& qpk)
|
||||
// requires requires {
|
||||
// requires is_specialization_of<CastSpec, quantity_point_kind>;
|
||||
// requires requires { quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative()); };
|
||||
// requires equivalent<typename PK::origin, typename CastSpec::point_kind_type::origin>;
|
||||
// } || requires { requires PointKind<CastSpec> && UnitOf<U, typename CastSpec::dimension>; } ||
|
||||
// requires { quantity_kind_cast<CastSpec>(qpk.relative()); } // TODO: Simplify when Clang catches up.
|
||||
// {
|
||||
// if constexpr (is_specialization_of<CastSpec, quantity_point_kind>)
|
||||
// return CastSpec(quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative()));
|
||||
// else if constexpr (PointKind<CastSpec>)
|
||||
// return quantity_point_kind(quantity_kind_cast<typename CastSpec::base_kind>(qpk.relative()));
|
||||
// else
|
||||
// return quantity_point_kind(quantity_kind_cast<CastSpec>(qpk.relative()));
|
||||
// }
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity point kind
|
||||
*
|
||||
* Implicit conversions between quantity point kinds of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
*
|
||||
* This cast gets both the target point kind and unit to cast to. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_point_kind_cast<ns::y_coordinate_kind, units::isq::si::kilometre>(x);
|
||||
*
|
||||
* @note This cast is especially useful when working with quantity point kinds of unknown point kind.
|
||||
*
|
||||
* @tparam ToPK the point kind type to use for the target quantity
|
||||
* @tparam ToU the unit type to use for the target quantity
|
||||
*/
|
||||
template<PointKind ToPK, Unit ToU, typename PK, typename U, typename Rep>
|
||||
requires equivalent<typename ToPK::dimension, typename PK::dimension> && UnitOf<ToU, typename ToPK::dimension>
|
||||
[[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind<PK, U, Rep>& qpk)
|
||||
{
|
||||
return quantity_point_kind_cast<quantity_point_kind<ToPK, ToU, Rep>>(qpk);
|
||||
}
|
||||
// /**
|
||||
// * @brief Explicit cast of a quantity point kind
|
||||
// *
|
||||
// * Implicit conversions between quantity point kinds of different types are allowed only for "safe"
|
||||
// * (i.e. non-truncating) conversion. In other cases an explicit cast has to be used.
|
||||
// *
|
||||
// * This cast gets both the target point kind and unit to cast to. For example:
|
||||
// *
|
||||
// * auto q1 = units::quantity_point_kind_cast<ns::y_coordinate_kind, units::isq::si::kilometre>(x);
|
||||
// *
|
||||
// * @note This cast is especially useful when working with quantity point kinds of unknown point kind.
|
||||
// *
|
||||
// * @tparam ToPK the point kind type to use for the target quantity
|
||||
// * @tparam ToU the unit type to use for the target quantity
|
||||
// */
|
||||
// template<PointKind ToPK, Unit ToU, typename PK, typename U, typename Rep>
|
||||
// requires equivalent<typename ToPK::dimension, typename PK::dimension> && UnitOf<ToU, typename ToPK::dimension>
|
||||
// [[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind<PK, U, Rep>& qpk)
|
||||
// {
|
||||
// return quantity_point_kind_cast<quantity_point_kind<ToPK, ToU, Rep>>(qpk);
|
||||
// }
|
||||
|
||||
} // namespace units
|
||||
|
||||
|
@@ -136,13 +136,27 @@ template<Representation Rep, Reference R>
|
||||
|
||||
void /*Use `q * (1 * r)` rather than `q * r`.*/ operator*(Quantity auto, Reference auto) = delete;
|
||||
|
||||
template<Reference R1, Reference R2>
|
||||
[[nodiscard]] consteval bool equivalent(R1, R2)
|
||||
{
|
||||
return equivalent(R1::dimension, R2::dimension) && equivalent(R1::unit, R2::unit);
|
||||
}
|
||||
|
||||
template<Reference R1, Reference R2>
|
||||
[[nodiscard]] consteval bool convertible(R1, R2)
|
||||
{
|
||||
return convertible(R1::dimension, R2::dimension) && convertible(R1::unit, R2::unit);
|
||||
}
|
||||
|
||||
|
||||
template<Dimension auto Dim, Unit auto CoU>
|
||||
struct system_reference {
|
||||
static constexpr auto dimension = Dim;
|
||||
static constexpr auto coherent_unit = CoU;
|
||||
|
||||
template<Unit U>
|
||||
// requires same_unit_reference<CoU, U>
|
||||
// TODO enable that
|
||||
// requires(convertible(coherent_unit, U{}))
|
||||
[[nodiscard]] constexpr reference<std::remove_const_t<decltype(dimension)>, U> operator[](U) const
|
||||
{
|
||||
return {};
|
||||
@@ -153,3 +167,20 @@ inline constexpr struct dimensionless : system_reference<one_dim, one> {
|
||||
} dimensionless;
|
||||
|
||||
} // namespace units
|
||||
|
||||
namespace std {
|
||||
|
||||
template<units::Reference R1, units::Reference R2>
|
||||
requires requires {
|
||||
typename common_type_t<remove_const_t<decltype(R1::dimension)>, remove_const_t<decltype(R2::dimension)>>;
|
||||
typename common_type_t<remove_const_t<decltype(R1::unit)>, remove_const_t<decltype(R2::unit)>>;
|
||||
}
|
||||
struct common_type<R1, R2> {
|
||||
private:
|
||||
using dim = common_type_t<remove_const_t<decltype(R1::dimension)>, remove_const_t<decltype(R2::dimension)>>;
|
||||
using unit = common_type_t<remove_const_t<decltype(R1::unit)>, remove_const_t<decltype(R2::unit)>>;
|
||||
public:
|
||||
using type = units::reference<dim, unit>;
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
@@ -330,12 +330,6 @@ template<Unit U1, Unit U2>
|
||||
return is_same_v<U1, U2>;
|
||||
}
|
||||
|
||||
template<Unit U1, Unit U2>
|
||||
[[nodiscard]] consteval bool equivalent(U1, U2)
|
||||
{
|
||||
return true; // TODO implement this
|
||||
}
|
||||
|
||||
|
||||
// template<BaseDimension D1, BaseDimension D2>
|
||||
// constexpr bool operator==(D1, D2)
|
||||
@@ -366,6 +360,20 @@ template<Unit U1, Unit U2>
|
||||
// std::is_same_v<typename D1::normalized_den, typename D2::normalized_den>;
|
||||
// }
|
||||
|
||||
// TODO implement this
|
||||
// template<Dimension D1, Dimension D2>
|
||||
// [[nodiscard]] consteval bool equivalent(D1, D2)
|
||||
// {
|
||||
// return is_same_v<detail::dim_type<D1>, detail::dim_type<D2>>;
|
||||
// }
|
||||
|
||||
template<Unit U1, Unit U2>
|
||||
[[nodiscard]] consteval bool convertible(U1, U2)
|
||||
{
|
||||
// TODO implement this
|
||||
return std::derived_from<U1, U2> || std::derived_from<U2, U1>;
|
||||
}
|
||||
|
||||
template<Unit U>
|
||||
struct square_ : decltype(U{} * U{}) {};
|
||||
|
||||
@@ -379,3 +387,15 @@ template<Unit auto U>
|
||||
inline constexpr cubic_<std::remove_const_t<decltype(U)>> cubic;
|
||||
|
||||
} // namespace units
|
||||
|
||||
namespace std {
|
||||
|
||||
// TODO implement this
|
||||
template<units::Unit U1, units::Unit U2>
|
||||
requires(units::convertible(U1{}, U2{}))
|
||||
struct common_type<U1, U2> {
|
||||
using type = ::units::conditional<std::derived_from<std::remove_const_t<U1>, std::remove_const_t<U2>>,
|
||||
std::remove_const_t<U1>, std::remove_const_t<U2>>;
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
@@ -36,14 +36,14 @@ inline constexpr struct length_dim_ : base_dimension<"L"> {} length_dim;
|
||||
inline constexpr struct time_dim_ : base_dimension<"T"> {} time_dim;
|
||||
|
||||
inline constexpr struct frequency_dim_ : decltype(1 / time_dim) {} frequency_dim;
|
||||
inline constexpr struct action_dim_ : decltype(1 / time_dim) {} action_dim;
|
||||
inline constexpr struct area_dim_ : decltype(length_dim * length_dim) {} area_dim;
|
||||
inline constexpr struct volume_dim_ : decltype(area_dim * length_dim) {} volume_dim;
|
||||
inline constexpr struct speed_dim_ : decltype(length_dim / time_dim) {} speed_dim;
|
||||
inline constexpr struct velocity_dim_ : speed_dim_ {} velocity_dim;
|
||||
inline constexpr struct acceleration_dim_ : decltype(speed_dim / time_dim) {} acceleration_dim;
|
||||
// clang-format on
|
||||
|
||||
} // namespace
|
||||
|
||||
// concepts verification
|
||||
static_assert(BaseDimension<length_dim_>);
|
||||
static_assert(!BaseDimension<frequency_dim_>);
|
||||
@@ -107,13 +107,19 @@ static_assert(length_dim / length_dim == one_dim);
|
||||
|
||||
static_assert(1 / time_dim != frequency_dim);
|
||||
static_assert(equivalent(1 / time_dim, frequency_dim));
|
||||
static_assert(convertible(1 / time_dim, frequency_dim));
|
||||
static_assert(1 / frequency_dim == time_dim);
|
||||
static_assert(frequency_dim * time_dim == one_dim);
|
||||
static_assert(std::is_same_v<std::common_type_t<decltype(1 / time_dim), decltype(frequency_dim)>, frequency_dim_>);
|
||||
static_assert(std::is_same_v<std::common_type_t<decltype(frequency_dim), decltype(1 / time_dim)>, frequency_dim_>);
|
||||
|
||||
static_assert(length_dim * length_dim != area_dim);
|
||||
static_assert(equivalent(length_dim * length_dim, area_dim));
|
||||
static_assert(convertible(length_dim * length_dim, area_dim));
|
||||
static_assert(length_dim * length_dim != volume_dim);
|
||||
static_assert(area_dim / length_dim == length_dim);
|
||||
static_assert(std::is_same_v<std::common_type_t<decltype(length_dim * length_dim), decltype(area_dim)>, area_dim_>);
|
||||
static_assert(std::is_same_v<std::common_type_t<decltype(area_dim), decltype(length_dim * length_dim)>, area_dim_>);
|
||||
|
||||
static_assert(length_dim * length_dim * length_dim != volume_dim);
|
||||
static_assert(equivalent(length_dim * length_dim * length_dim, volume_dim));
|
||||
@@ -134,6 +140,10 @@ static_assert(length_dim * time_dim != speed_dim);
|
||||
static_assert(length_dim / time_dim / time_dim != speed_dim);
|
||||
static_assert(length_dim / speed_dim == time_dim);
|
||||
static_assert(speed_dim * time_dim == length_dim);
|
||||
static_assert(std::is_same_v<std::common_type_t<decltype(length_dim / time_dim), decltype(speed_dim)>, speed_dim_>);
|
||||
static_assert(std::is_same_v<std::common_type_t<decltype(speed_dim), decltype(length_dim / time_dim)>, speed_dim_>);
|
||||
static_assert(std::is_same_v<std::common_type_t<decltype(length_dim / time_dim), decltype(length_dim / time_dim)>,
|
||||
decltype(length_dim / time_dim)>);
|
||||
|
||||
static_assert(length_dim / time_dim / time_dim != acceleration_dim);
|
||||
static_assert(equivalent(length_dim / time_dim / time_dim, acceleration_dim));
|
||||
@@ -147,3 +157,21 @@ static_assert(equivalent(acceleration_dim * time_dim, speed_dim));
|
||||
static_assert(acceleration_dim * (time_dim * time_dim) == length_dim);
|
||||
static_assert(acceleration_dim / speed_dim != frequency_dim);
|
||||
static_assert(equivalent(acceleration_dim / speed_dim, frequency_dim));
|
||||
|
||||
static_assert(frequency_dim != action_dim);
|
||||
static_assert(equivalent(frequency_dim, action_dim));
|
||||
static_assert(!convertible(frequency_dim, action_dim));
|
||||
template<auto T1, auto T2>
|
||||
concept no_common_type = requires {
|
||||
requires !requires { typename std::common_type_t<decltype(T1), decltype(T2)>; };
|
||||
requires !requires { typename std::common_type_t<decltype(T2), decltype(T1)>; };
|
||||
};
|
||||
static_assert(no_common_type<frequency_dim, action_dim>);
|
||||
|
||||
static_assert(velocity_dim != speed_dim);
|
||||
static_assert(equivalent(velocity_dim, speed_dim));
|
||||
static_assert(convertible(speed_dim, velocity_dim));
|
||||
static_assert(std::is_same_v<std::common_type_t<decltype(velocity_dim), decltype(speed_dim)>, velocity_dim_>);
|
||||
static_assert(std::is_same_v<std::common_type_t<decltype(speed_dim), decltype(velocity_dim)>, velocity_dim_>);
|
||||
|
||||
} // namespace
|
||||
|
Reference in New Issue
Block a user