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>
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; }

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

View File

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

View File

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

View File

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

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

View File

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

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