refactor: quantity arithmetics implemented

This commit is contained in:
Mateusz Pusz
2022-10-09 21:32:38 +01:00
parent 4a49bdda05
commit cf0a770d9b
9 changed files with 506 additions and 445 deletions

View File

@@ -23,6 +23,9 @@
#include <units/si/si.h> #include <units/si/si.h>
template<typename T>
consteval bool print();
template<typename T, typename Expr> template<typename T, typename Expr>
constexpr bool is_of_type(Expr) constexpr bool is_of_type(Expr)
{ {
@@ -34,33 +37,102 @@ namespace {
using namespace units; using namespace units;
using namespace units::si::unit_symbols; using namespace units::si::unit_symbols;
constexpr auto power = 5 * si::power[W]; // clang-format off
static_assert(is_of_type<quantity<reference<struct isq::power_dim, struct si::watt>{}, int>>(power)); 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( static_assert(
is_of_type<quantity<reference<struct isq::speed_dim, derived_unit<struct si::metre, per<struct si::second>>>{}, int>>( is_same_v<decltype(5 * si::power[W]), quantity<reference<struct isq::power_dim, struct si::watt>{}, int>>);
speed));
constexpr auto q = 10 * si::length[m] / (2 * si::time[s]); // Named quantity/dimension and derived (unnamed) unit
static_assert(is_of_type<quantity<reference<derived_dimension<struct isq::length_dim, per<struct isq::time_dim>>, 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>>);
// 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>>>{}, derived_unit<struct si::metre, per<struct si::second>>>{},
int>>(q)); int>>);
constexpr auto distance = 5 * si::speed[m / s] * (5 * si::time[s]); // 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>>);
static_assert(is_of_type<quantity<reference<struct isq::length_dim, struct si::metre>{}, int>>(distance)); // 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>>);
constexpr auto dimensionless = 20 * si::speed[m / s] / (10 * si::length[m]) * (5 * si::time[s]); // Comparisons
static_assert(is_of_type<quantity<reference<struct one_dim, struct one>{}, int>>(dimensionless)); // 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]));
// 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]));
// constexpr auto q1 = 10 * si::length[m] / (2 * si::time[s]) + 5 * si::speed[m / s]; // Different named dimensions
// static_assert(is_of_type<quantity<reference<derived_dimension<struct isq::length_dim, per<struct isq::time_dim>>, template<Reference auto R1, Reference auto R2>
// derived_unit<struct si::metre, per<struct si::second>>>{}, concept invalid_comparison = requires {
// int>>(q1)); 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])), 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[km / h]>);
// static_assert(!quantity_of<decltype(120 * si::length[km] / (2 * si::time[h])), si::speed[m / s]>); // 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<speed[km / s]> speed3(20);
// quantity<length[m] / si::time[s]> speed4(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; } // constexpr auto avg_speed(quantity<length[km]> d, quantity<si::time[h]> t) { return d / t; }

View File

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

View File

@@ -102,15 +102,7 @@ inline constexpr bool is_quantity<quantity<R, Rep>> = true;
*/ */
template<typename Q, auto V> template<typename Q, auto V>
concept quantity_of = Quantity<Q> && ((Dimension<std::remove_const_t<decltype(V)>> && Q::dimension == 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)); (Reference<std::remove_const_t<decltype(V)>> && Q::dimension == V.dimension &&
Q::unit == V.unit));
/**
* @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);
} // namespace units } // namespace units

View File

@@ -195,6 +195,23 @@ template<Dimension D1, Dimension D2>
return is_same_v<detail::dim_type<D1>, detail::dim_type<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 // TODO consider adding the support for text output of the dimensional equation
} // namespace units } // 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

View File

@@ -23,13 +23,9 @@
#pragma once #pragma once
// #include <units/bits/common_type.h>
// #include <units/generic/dimensionless.h>
// IWYU pragma: begin_exports // IWYU pragma: begin_exports
// #include <units/quantity_cast.h> #include <units/quantity_cast.h>
// #include <units/ratio.h> // #include <units/ratio.h>
#include <units/bits/common_type.h>
#include <units/concepts.h> #include <units/concepts.h>
#include <units/customization_points.h> #include <units/customization_points.h>
#include <units/dimension.h> #include <units/dimension.h>
@@ -38,8 +34,7 @@
#include <compare> #include <compare>
// IWYU pragma: end_exports // IWYU pragma: end_exports
// #include <units/reference.h> #include <utility>
// #include <utility>
namespace units { namespace units {
@@ -73,8 +68,9 @@ concept harmonic_ = // exposition only
// Quantity<QFrom> && Quantity<QTo> && is_integral(detail::quantity_magnitude<QFrom> / detail::quantity_magnitude<QTo>); // Quantity<QFrom> && Quantity<QTo> && is_integral(detail::quantity_magnitude<QFrom> / detail::quantity_magnitude<QTo>);
template<typename QFrom, typename QTo> template<typename QFrom, typename QTo>
concept safe_castable_to_ = // exposition only concept quantity_convertible_to_ = // exposition only
Quantity<QFrom> && quantity_of<QTo, QFrom::dimension> && scalable_with_<typename QFrom::rep, typename QTo::rep> && 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>)); (floating_point_<QTo> || (!floating_point_<QFrom> && harmonic_<QFrom, QTo>));
template<typename Func, typename T, typename U> 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()) constexpr explicit(false) quantity(const Q& q) : number_(quantity_cast<quantity>(q).number())
{ {
} }
template<QuantityLike Q> 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))) 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>) 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) [[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> template<typename Value>
@@ -475,20 +471,20 @@ explicit quantity(Q)
typename quantity_like_traits<Q>::rep>; typename quantity_like_traits<Q>::rep>;
// non-member binary operators // 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>) requires(quantity_value_for_<std::plus<>, typename Q1::rep, typename Q2::rep>)
[[nodiscard]] constexpr Quantity auto operator+(const Q1& lhs, const Q2& rhs) [[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())>; using ret = quantity<ref{}, decltype(lhs.number() + rhs.number())>;
return ret(ret(lhs).number() + ret(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>) requires(quantity_value_for_<std::minus<>, typename Q1::rep, typename Q2::rep>)
[[nodiscard]] constexpr Quantity auto operator-(const Q1& lhs, const Q2& rhs) [[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())>; using ret = quantity<ref{}, decltype(lhs.number() - rhs.number())>;
return ret(ret(lhs).number() - ret(rhs).number()); return ret(ret(lhs).number() - ret(rhs).number());
} }
@@ -510,7 +506,7 @@ template<Quantity Q1, Quantity Q2>
template<Quantity Q1, Quantity Q2> template<Quantity Q1, Quantity Q2>
requires(!floating_point_<typename Q1::rep>) && (!floating_point_<typename Q2::rep>) && 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>) (quantity_value_for_<std::modulus<>, typename Q1::rep, typename Q2::rep>)
[[nodiscard]] constexpr Quantity auto operator%(const Q1& lhs, const Q2& rhs) [[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()); 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> requires std::three_way_comparable_with<typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr auto operator<=>(const Q1& lhs, const Q2& rhs) [[nodiscard]] constexpr auto operator<=>(const Q1& lhs, const Q2& rhs)
{ {
using cq = std::common_type_t<Q1, Q2>; using ref = std::common_type_t<decltype(Q1::reference), decltype(Q2::reference)>;
return cq(lhs).number() <=> cq(rhs).number(); 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> requires std::equality_comparable_with<typename Q1::rep, typename Q2::rep>
[[nodiscard]] constexpr bool operator==(const Q1& lhs, const Q2& rhs) [[nodiscard]] constexpr bool operator==(const Q1& lhs, const Q2& rhs)
{ {
using cq = std::common_type_t<Q1, Q2>; using ref = std::common_type_t<decltype(Q1::reference), decltype(Q2::reference)>;
return cq(lhs).number() == cq(rhs).number(); return quantity_cast<ref{}>(lhs).number() == quantity_cast<ref{}>(rhs).number();
} }
} // namespace units } // 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

View File

@@ -22,12 +22,13 @@
#pragma once #pragma once
#include <units/bits/dimension_op.h>
#include <units/bits/external/type_traits.h> #include <units/bits/external/type_traits.h>
#include <units/concepts.h> #include <units/concepts.h>
#include <units/customization_points.h> #include <units/customization_points.h>
#include <units/dimension.h>
#include <units/magnitude.h> #include <units/magnitude.h>
#include <units/reference.h> #include <units/reference.h>
#include <units/unit.h>
UNITS_DIAGNOSTIC_PUSH UNITS_DIAGNOSTIC_PUSH
// warning C4244: 'argument': conversion from 'intmax_t' to 'T', possible loss of data with T=int // 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 { namespace units {
template<Dimension D, UnitOf<D> U, Representation Rep> template<Reference auto R, Representation Rep>
class quantity; class quantity;
template<PointOrigin O, UnitOf<typename O::dimension> U, Representation Rep> // template<PointOrigin O, UnitOf<typename O::dimension> U, Representation Rep>
class quantity_point; // class quantity_point;
template<Kind K, UnitOf<typename K::dimension> U, Representation Rep> // template<Kind K, UnitOf<typename K::dimension> U, Representation Rep>
class quantity_kind; // class quantity_kind;
template<PointKind PK, UnitOf<typename PK::dimension> U, Representation Rep> // template<PointKind PK, UnitOf<typename PK::dimension> U, Representation Rep>
class quantity_point_kind; // class quantity_point_kind;
namespace detail { namespace detail {
template<Quantity Q> // template<Quantity Q>
inline constexpr Magnitude auto quantity_magnitude = decltype(Q::reference)::mag; // inline constexpr Magnitude auto quantity_magnitude = decltype(Q::reference)::mag;
template<typename QFrom, typename QTo> // template<typename QFrom, typename QTo>
inline constexpr Magnitude auto cast_magnitude = [] { // inline constexpr Magnitude auto cast_magnitude = [] {
using FromU = TYPENAME QFrom::unit; // using FromU = TYPENAME QFrom::unit;
using ToU = TYPENAME QTo::unit; // using ToU = TYPENAME QTo::unit;
if constexpr (same_unit_reference<FromU, ToU>::value) { // if constexpr (same_unit_reference<FromU, ToU>::value) {
return FromU::mag / ToU::mag; // return FromU::mag / ToU::mag;
} else { // } else {
return quantity_magnitude<QFrom> / quantity_magnitude<QTo>; // return quantity_magnitude<QFrom> / quantity_magnitude<QTo>;
} // }
}(); // }();
template<typename From, typename To> // template<typename From, typename To>
struct cast_traits; // struct cast_traits;
template<typename From, typename To> // template<typename From, typename To>
requires common_type_with_<std::common_type_t<From, To>, std::intmax_t> // requires common_type_with_<std::common_type_t<From, To>, std::intmax_t>
struct cast_traits<From, To> { // struct cast_traits<From, To> {
using ratio_type = std::common_type_t<std::common_type_t<From, To>, std::intmax_t>; // using ratio_type = std::common_type_t<std::common_type_t<From, To>, std::intmax_t>;
using rep_type = ratio_type; // using rep_type = ratio_type;
}; // };
template<typename From, typename To> // template<typename From, typename To>
requires(!common_type_with_<std::common_type_t<From, To>, std::intmax_t> && // requires(!common_type_with_<std::common_type_t<From, To>, std::intmax_t> &&
scalable_number_<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; } && // 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>) // common_type_with_<typename std::common_type_t<From, To>::value_type, std::intmax_t>)
struct cast_traits<From, To> { // 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 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>; // using rep_type = std::common_type_t<From, To>;
}; // };
} // namespace detail } // namespace detail
@@ -97,59 +98,28 @@ struct cast_traits<From, To> {
* *
* @tparam To a target quantity type to cast to * @tparam To a target quantity type to cast to
*/ */
template<Quantity To, typename D, typename U, scalable_with_<typename To::rep> Rep> template<Quantity To, auto R, scalable_with_<typename To::rep> Rep>
requires QuantityOf<To, D> && (std::constructible_from<typename To::rep, std::common_type_t<typename To::rep, Rep>>) requires(convertible(R, To::reference))
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q) [[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
{ {
using traits = detail::cast_traits<Rep, typename To::rep>; // TODO implement same unit magnitude check
using ratio_type = TYPENAME traits::ratio_type; if constexpr (std::same_as<decltype(R.unit), decltype(To::unit)>) {
using rep_type = TYPENAME traits::rep_type; 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 c_mag = detail::cast_magnitude<quantity<D, U, Rep>, To>;
constexpr Magnitude auto num = numerator(c_mag); // constexpr Magnitude auto num = numerator(c_mag);
constexpr Magnitude auto den = denominator(c_mag); // constexpr Magnitude auto den = denominator(c_mag);
constexpr Magnitude auto irr = c_mag * (den / num); // constexpr Magnitude auto irr = c_mag * (den / num);
constexpr auto val = [](Magnitude auto m) { return get_value<ratio_type>(m); }; // 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))); // return To(static_cast<TYPENAME To::rep>(static_cast<rep_type>(q.number()) * val(num) / val(den) * val(irr)));
} // TODO implement that
return 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 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);
} }
/** /**
@@ -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 ToD a dimension type to use for a target quantity
* @tparam ToU a unit 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> template<Reference auto ToR, auto R, typename Rep>
requires equivalent<ToD, D> && UnitOf<ToU, ToD> requires(convertible(ToR, R))
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q) [[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 * @tparam ToRep a representation type to use for a target quantity
*/ */
template<Representation ToRep, typename D, typename U, scalable_with_<ToRep> Rep> template<Representation ToRep, auto R, scalable_with_<ToRep> Rep>
requires(std::constructible_from<ToRep, std::common_type_t<ToRep, Rep>>) // requires(std::constructible_from<ToRep, std::common_type_t<ToRep, Rep>>)
[[nodiscard]] constexpr auto quantity_cast(const quantity<D, U, Rep>& q) [[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 // * @brief Explicit cast of a quantity point
* // *
* Implicit conversions between quantity points of different types are allowed only for "safe" // * 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. // * (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: // * 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<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::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::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<units::isq::si::second>(quantity_point{1_q_ms});
* auto q1 = units::quantity_point_cast<int>(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 // * @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> // template<typename CastSpec, typename O, typename U, typename Rep>
[[nodiscard]] constexpr auto quantity_point_cast(const quantity_point<O, U, Rep>& qp) // [[nodiscard]] constexpr auto quantity_point_cast(const quantity_point<O, U, Rep>& qp)
requires requires { // requires requires {
requires is_specialization_of<CastSpec, quantity_point>; // requires is_specialization_of<CastSpec, quantity_point>;
requires requires { quantity_cast<typename CastSpec::quantity_type>(qp.relative()); }; // requires requires { quantity_cast<typename CastSpec::quantity_type>(qp.relative()); };
requires equivalent<O, typename CastSpec::origin>; // requires equivalent<O, typename CastSpec::origin>;
} || // TODO: Simplify when Clang catches up. // } || // TODO: Simplify when Clang catches up.
requires { quantity_cast<CastSpec>(qp.relative()); } // requires { quantity_cast<CastSpec>(qp.relative()); }
{ // {
if constexpr (is_specialization_of<CastSpec, quantity_point>) // if constexpr (is_specialization_of<CastSpec, quantity_point>)
return quantity_point(quantity_cast<typename CastSpec::quantity_type>(qp.relative())); // return quantity_point(quantity_cast<typename CastSpec::quantity_type>(qp.relative()));
else // else
return quantity_point(quantity_cast<CastSpec>(qp.relative())); // return quantity_point(quantity_cast<CastSpec>(qp.relative()));
} // }
/** // /**
* @brief Explicit cast of a quantity point // * @brief Explicit cast of a quantity point
* // *
* Implicit conversions between quantity points of different types are allowed only for "safe" // * 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. // * (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: // * 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); // * 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 // * @note This cast is especially useful when working with quantity points of unknown dimensions
* (@c unknown_dimension). // * (@c unknown_dimension).
* // *
* @tparam ToD a dimension type to use for a target quantity // * @tparam ToD a dimension type to use for a target quantity
* @tparam ToU a unit 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> // template<Dimension ToD, Unit ToU, typename O, typename U, typename Rep>
requires equivalent<ToD, typename O::dimension> && UnitOf<ToU, ToD> && RebindablePointOriginFor<O, ToD> // 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) // [[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); // return quantity_point_cast<quantity_point<rebind_point_origin_dimension<O, ToD>, ToU, Rep>>(q);
} // }
/** // /**
* @brief Explicit cast of a quantity kind // * @brief Explicit cast of a quantity kind
* // *
* Implicit conversions between quantity kinds of different types are allowed only for "safe" // * 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. // * (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: // * 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<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<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::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::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<units::isq::si::metre>(ns::width{1 * mm});
* auto q1 = units::quantity_kind_cast<int>(ns::width{1.0 * 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 // * @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> // template<typename CastSpec, typename K, typename U, typename Rep>
[[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind<K, U, Rep>& qk) // [[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind<K, U, Rep>& qk)
requires requires { // requires requires {
requires is_specialization_of<CastSpec, quantity_kind>; // requires is_specialization_of<CastSpec, quantity_kind>;
requires requires { quantity_cast<typename CastSpec::quantity_type>(qk.common()); }; // requires requires { quantity_cast<typename CastSpec::quantity_type>(qk.common()); };
} || requires { // } || requires {
requires Kind<CastSpec>; // requires Kind<CastSpec>;
requires UnitOf<U, typename CastSpec::dimension>; // requires UnitOf<U, typename CastSpec::dimension>;
} || requires { quantity_cast<CastSpec>(qk.common()); } // TODO: Simplify when Clang catches up. // } || requires { quantity_cast<CastSpec>(qk.common()); } // TODO: Simplify when Clang catches up.
{ // {
if constexpr (is_specialization_of<CastSpec, quantity_kind>) // if constexpr (is_specialization_of<CastSpec, quantity_kind>)
return CastSpec(quantity_cast<typename CastSpec::quantity_type>(qk.common())); // return CastSpec(quantity_cast<typename CastSpec::quantity_type>(qk.common()));
else if constexpr (Kind<CastSpec>) // else if constexpr (Kind<CastSpec>)
return quantity_kind<CastSpec, U, Rep>(qk.common()); // return quantity_kind<CastSpec, U, Rep>(qk.common());
else { // else {
auto q{quantity_cast<CastSpec>(qk.common())}; // auto q{quantity_cast<CastSpec>(qk.common())};
using Q = decltype(q); // using Q = decltype(q);
return quantity_kind<K, typename Q::unit, typename Q::rep>(static_cast<Q&&>(q)); // return quantity_kind<K, typename Q::unit, typename Q::rep>(static_cast<Q&&>(q));
} // }
} // }
/** // /**
* @brief Explicit cast of a quantity kind // * @brief Explicit cast of a quantity kind
* // *
* Implicit conversions between quantity kinds of different types are allowed only for "safe" // * 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. // * (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: // * 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); // * 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. // * @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 ToK the kind type to use for the target quantity
* @tparam ToU the unit 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> // template<Kind ToK, Unit ToU, typename K, typename U, typename Rep>
requires equivalent<typename ToK::dimension, typename K::dimension> && UnitOf<ToU, typename ToK::dimension> // 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) // [[nodiscard]] constexpr QuantityKind auto quantity_kind_cast(const quantity_kind<K, U, Rep>& qk)
{ // {
return quantity_kind_cast<quantity_kind<ToK, ToU, Rep>>(qk); // return quantity_kind_cast<quantity_kind<ToK, ToU, Rep>>(qk);
} // }
/** // /**
* @brief Explicit cast of a quantity point kind // * @brief Explicit cast of a quantity point kind
* // *
* Implicit conversions between quantity point kinds of different types are allowed only for "safe" // * 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. // * (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 // * This cast gets the target (quantity) point kind type to cast to or anything that works for quantity_kind_cast. For
* example: // * 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::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<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::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<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::length<units::isq::si::metre>>(ns::x_coordinate{1 *
* auto q1 = units::quantity_point_kind_cast<units::isq::si::dim_acceleration>(quantity_point_kind(ns::rate_of_climb{200 // mm});
* * Gal})); auto q1 = units::quantity_point_kind_cast<units::isq::si::metre>(ns::x_coordinate{1 * mm}); auto q1 = // * auto q1 =
* units::quantity_point_kind_cast<int>(ns::x_coordinate{1.0 * mm}); // 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 =
* @tparam CastSpec a target (quantity) point kind type to cast to or anything that works for quantity_kind_cast // * units::quantity_point_kind_cast<int>(ns::x_coordinate{1.0 * mm});
*/ // *
template<typename CastSpec, typename PK, typename U, typename Rep> // * @tparam CastSpec a target (quantity) point kind type to cast to or anything that works for quantity_kind_cast
[[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind<PK, U, Rep>& qpk) // */
requires requires { // template<typename CastSpec, typename PK, typename U, typename Rep>
requires is_specialization_of<CastSpec, quantity_point_kind>; // [[nodiscard]] constexpr QuantityPointKind auto quantity_point_kind_cast(const quantity_point_kind<PK, U, Rep>& qpk)
requires requires { quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative()); }; // requires requires {
requires equivalent<typename PK::origin, typename CastSpec::point_kind_type::origin>; // requires is_specialization_of<CastSpec, quantity_point_kind>;
} || requires { requires PointKind<CastSpec> && UnitOf<U, typename CastSpec::dimension>; } || // requires requires { quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative()); };
requires { quantity_kind_cast<CastSpec>(qpk.relative()); } // TODO: Simplify when Clang catches up. // requires equivalent<typename PK::origin, typename CastSpec::point_kind_type::origin>;
{ // } || requires { requires PointKind<CastSpec> && UnitOf<U, typename CastSpec::dimension>; } ||
if constexpr (is_specialization_of<CastSpec, quantity_point_kind>) // requires { quantity_kind_cast<CastSpec>(qpk.relative()); } // TODO: Simplify when Clang catches up.
return CastSpec(quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative())); // {
else if constexpr (PointKind<CastSpec>) // if constexpr (is_specialization_of<CastSpec, quantity_point_kind>)
return quantity_point_kind(quantity_kind_cast<typename CastSpec::base_kind>(qpk.relative())); // return CastSpec(quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative()));
else // else if constexpr (PointKind<CastSpec>)
return quantity_point_kind(quantity_kind_cast<CastSpec>(qpk.relative())); // 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 // * @brief Explicit cast of a quantity point kind
* // *
* Implicit conversions between quantity point kinds of different types are allowed only for "safe" // * 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. // * (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: // * 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); // * 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. // * @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 ToPK the point kind type to use for the target quantity
* @tparam ToU the unit 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> // template<PointKind ToPK, Unit ToU, typename PK, typename U, typename Rep>
requires equivalent<typename ToPK::dimension, typename PK::dimension> && UnitOf<ToU, typename ToPK::dimension> // 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) // [[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); // return quantity_point_kind_cast<quantity_point_kind<ToPK, ToU, Rep>>(qpk);
} // }
} // namespace units } // namespace units

View File

@@ -136,13 +136,27 @@ template<Representation Rep, Reference R>
void /*Use `q * (1 * r)` rather than `q * r`.*/ operator*(Quantity auto, Reference auto) = delete; 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> template<Dimension auto Dim, Unit auto CoU>
struct system_reference { struct system_reference {
static constexpr auto dimension = Dim; static constexpr auto dimension = Dim;
static constexpr auto coherent_unit = CoU; static constexpr auto coherent_unit = CoU;
template<Unit U> 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 [[nodiscard]] constexpr reference<std::remove_const_t<decltype(dimension)>, U> operator[](U) const
{ {
return {}; return {};
@@ -153,3 +167,20 @@ inline constexpr struct dimensionless : system_reference<one_dim, one> {
} dimensionless; } dimensionless;
} // namespace units } // 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

View File

@@ -330,12 +330,6 @@ template<Unit U1, Unit U2>
return is_same_v<U1, 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> // template<BaseDimension D1, BaseDimension D2>
// constexpr bool operator==(D1, 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>; // 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> template<Unit U>
struct square_ : decltype(U{} * U{}) {}; struct square_ : decltype(U{} * U{}) {};
@@ -379,3 +387,15 @@ template<Unit auto U>
inline constexpr cubic_<std::remove_const_t<decltype(U)>> cubic; inline constexpr cubic_<std::remove_const_t<decltype(U)>> cubic;
} // namespace units } // 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

View File

@@ -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 time_dim_ : base_dimension<"T"> {} time_dim;
inline constexpr struct frequency_dim_ : decltype(1 / time_dim) {} frequency_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 area_dim_ : decltype(length_dim * length_dim) {} area_dim;
inline constexpr struct volume_dim_ : decltype(area_dim * length_dim) {} volume_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 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; inline constexpr struct acceleration_dim_ : decltype(speed_dim / time_dim) {} acceleration_dim;
// clang-format on // clang-format on
} // namespace
// concepts verification // concepts verification
static_assert(BaseDimension<length_dim_>); static_assert(BaseDimension<length_dim_>);
static_assert(!BaseDimension<frequency_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(1 / time_dim != frequency_dim);
static_assert(equivalent(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(1 / frequency_dim == time_dim);
static_assert(frequency_dim * time_dim == one_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(length_dim * length_dim != area_dim);
static_assert(equivalent(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(length_dim * length_dim != volume_dim);
static_assert(area_dim / length_dim == length_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(length_dim * length_dim * length_dim != volume_dim);
static_assert(equivalent(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 / time_dim / time_dim != speed_dim);
static_assert(length_dim / speed_dim == time_dim); static_assert(length_dim / speed_dim == time_dim);
static_assert(speed_dim * time_dim == length_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(length_dim / time_dim / time_dim != acceleration_dim);
static_assert(equivalent(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 * (time_dim * time_dim) == length_dim);
static_assert(acceleration_dim / speed_dim != frequency_dim); static_assert(acceleration_dim / speed_dim != frequency_dim);
static_assert(equivalent(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