forked from mpusz/mp-units
feat: quantity_kind and quantity_point_kind
This commit is contained in:
committed by
Mateusz Pusz
parent
384f4b2624
commit
6bf09aa646
@@ -202,7 +202,51 @@ concept UnitOf =
|
||||
Dimension<D> &&
|
||||
std::same_as<typename U::reference, typename dimension_unit<D>::reference>;
|
||||
|
||||
// Quantity, QuantityPoint
|
||||
// Kind
|
||||
namespace detail {
|
||||
|
||||
template<typename, Dimension>
|
||||
struct _kind_base;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T, template<typename...> typename Base>
|
||||
concept kind_impl_ =
|
||||
is_derived_from_specialization_of<T, Base> &&
|
||||
requires(T* t) {
|
||||
typename T::base_kind;
|
||||
typename T::dimension;
|
||||
requires Dimension<typename T::dimension>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A concept matching all kind types
|
||||
*
|
||||
* Satisfied by all kind types derived from an specialization of :class:`kind`.
|
||||
*/
|
||||
template<typename T>
|
||||
concept Kind =
|
||||
kind_impl_<T, detail::_kind_base> &&
|
||||
kind_impl_<typename T::base_kind, detail::_kind_base> &&
|
||||
std::same_as<typename T::base_kind, typename T::base_kind::base_kind>;
|
||||
|
||||
// PointKind
|
||||
namespace detail {
|
||||
|
||||
template<Kind>
|
||||
struct _point_kind_base;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief A concept matching all point kind types
|
||||
*
|
||||
* Satisfied by all point kind types derived from an specialization of :class:`point_kind`.
|
||||
*/
|
||||
template<typename T>
|
||||
concept PointKind = kind_impl_<T, detail::_point_kind_base>;
|
||||
|
||||
// Quantity, QuantityPoint, QuantityKind, QuantityPointKind
|
||||
namespace detail {
|
||||
|
||||
template<typename T>
|
||||
@@ -211,6 +255,12 @@ inline constexpr bool is_quantity = false;
|
||||
template<typename T>
|
||||
inline constexpr bool is_quantity_point = false;
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_quantity_kind = false;
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_quantity_point_kind = false;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
@@ -229,9 +279,27 @@ concept Quantity = detail::is_quantity<T>;
|
||||
template<typename T>
|
||||
concept QuantityPoint = detail::is_quantity_point<T>;
|
||||
|
||||
/**
|
||||
* @brief A concept matching all quantity kinds in the library.
|
||||
*
|
||||
* Satisfied by all specializations of @c quantity_kind.
|
||||
*/
|
||||
template<typename T>
|
||||
concept QuantityKind = detail::is_quantity_kind<T>;
|
||||
|
||||
/**
|
||||
* @brief A concept matching all quantity point kinds in the library.
|
||||
*
|
||||
* Satisfied by all specializations of @c quantity_point_kind.
|
||||
*/
|
||||
template<typename T>
|
||||
concept QuantityPointKind = detail::is_quantity_point_kind<T>;
|
||||
|
||||
// QuantityLike
|
||||
|
||||
/**
|
||||
* @brief A concept matching all quantity-like types (other than specialization of @c quantity)
|
||||
*
|
||||
*
|
||||
* Satisfied by all types for which a correct specialization of `quantity_like_traits`
|
||||
* type trait is provided.
|
||||
*/
|
||||
@@ -261,12 +329,12 @@ concept castable_number_ = // exposition only
|
||||
common_type_with_<T, std::intmax_t> &&
|
||||
scalable_number_<std::common_type_t<T, std::intmax_t>>;
|
||||
|
||||
template<typename T>
|
||||
template<typename T>
|
||||
concept scalable_ = // exposition only
|
||||
castable_number_<T> ||
|
||||
(requires { typename T::value_type; } && castable_number_<typename T::value_type> && scalable_number_<T, std::common_type_t<typename T::value_type, std::intmax_t>>);
|
||||
|
||||
template<typename T, typename U>
|
||||
template<typename T, typename U>
|
||||
concept scalable_with_ = // exposition only
|
||||
common_type_with_<T, U> &&
|
||||
scalable_<std::common_type_t<T, U>>;
|
||||
@@ -281,6 +349,10 @@ template<typename T>
|
||||
requires requires { typename T::value_type; }
|
||||
inline constexpr bool is_wrapped_quantity<T> = Quantity<typename T::value_type> || QuantityLike<typename T::value_type> || is_wrapped_quantity<typename T::value_type>;
|
||||
|
||||
template<typename T>
|
||||
requires requires { typename T::quantity_type; }
|
||||
inline constexpr bool is_wrapped_quantity<T> = Quantity<typename T::quantity_type>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,6 +34,12 @@ class quantity;
|
||||
template<Dimension D, UnitOf<D> U, QuantityValue Rep>
|
||||
class quantity_point;
|
||||
|
||||
template<Kind K, UnitOf<typename K::dimension> U, QuantityValue Rep>
|
||||
class quantity_kind;
|
||||
|
||||
template<PointKind PK, UnitOf<typename PK::dimension> U, QuantityValue Rep>
|
||||
class quantity_point_kind;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename Q1, typename Q2, typename Rep>
|
||||
@@ -65,33 +71,53 @@ struct common_quantity_impl<quantity<D1, U1, Rep1>, quantity<D2, U2, Rep2>, Rep>
|
||||
using type = quantity<dimension, unit, Rep>;
|
||||
};
|
||||
|
||||
template<typename D, typename U, typename Rep>
|
||||
quantity_point<D, U, Rep> common_quantity_point_impl(quantity<D, U, Rep>);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<Quantity Q1, QuantityEquivalentTo<Q1> Q2, QuantityValue Rep = std::common_type_t<typename Q1::rep, typename Q2::rep>>
|
||||
using common_quantity = TYPENAME detail::common_quantity_impl<Q1, Q2, Rep>::type;
|
||||
|
||||
template<QuantityPoint QP1, QuantityPoint QP2>
|
||||
requires requires { typename common_quantity<typename QP1::quantity_type, typename QP2::quantity_type>; }
|
||||
using common_quantity_point = decltype(
|
||||
detail::common_quantity_point_impl(common_quantity<typename QP1::quantity_type, typename QP2::quantity_type>{}));
|
||||
template<QuantityPoint QP1, QuantityPointEquivalentTo<QP1> QP2>
|
||||
using common_quantity_point = std::common_type_t<QP1, QP2>;
|
||||
|
||||
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
|
||||
using common_quantity_kind = std::common_type_t<QK1, QK2>;
|
||||
|
||||
template<QuantityPointKind QPK1, QuantityPointKindEquivalentTo<QPK1> QPK2>
|
||||
using common_quantity_point_kind = std::common_type_t<QPK1, QPK2>;
|
||||
|
||||
} // namespace units
|
||||
|
||||
namespace std {
|
||||
|
||||
template<units::Quantity Q1, units::Quantity Q2>
|
||||
requires units::equivalent<typename Q1::dimension, typename Q2::dimension>
|
||||
template<units::Quantity Q1, units::QuantityEquivalentTo<Q1> Q2>
|
||||
requires requires { typename common_type_t<typename Q1::rep, typename Q2::rep>; }
|
||||
struct common_type<Q1, Q2> {
|
||||
using type = units::common_quantity<Q1, Q2>;
|
||||
};
|
||||
|
||||
template<units::QuantityPoint QP1, units::QuantityPoint QP2>
|
||||
requires requires { typename units::common_quantity_point<QP1, QP2>; }
|
||||
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::common_quantity_point<QP1, QP2>;
|
||||
using type = units::quantity_point<
|
||||
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>;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ struct equivalent_impl<T, T> : std::true_type {
|
||||
// units
|
||||
|
||||
template<Unit U1, Unit U2>
|
||||
struct equivalent_impl<U1, U2> : std::is_base_of<U1, U2>, std::is_base_of<U2, U1> {};
|
||||
struct equivalent_impl<U1, U2> : std::disjunction<std::is_base_of<U1, U2>, std::is_base_of<U2, U1>> {};
|
||||
|
||||
|
||||
// dimensions
|
||||
@@ -81,7 +81,17 @@ template<Unit U1, Dimension D1, Unit U2, Dimension D2>
|
||||
struct equivalent_unit : std::disjunction<equivalent_impl<U1, U2>,
|
||||
std::bool_constant<U1::ratio / dimension_unit<D1>::ratio == U2::ratio / dimension_unit<D2>::ratio>> {};
|
||||
|
||||
// quantities and quantity points
|
||||
|
||||
// (point) kinds
|
||||
|
||||
template<typename T, typename U>
|
||||
requires (Kind<T> && Kind<U>) || (PointKind<T> && PointKind<U>)
|
||||
struct equivalent_impl<T, U> :
|
||||
std::conjunction<std::is_same<typename T::base_kind, typename U::base_kind>,
|
||||
equivalent_impl<typename T::dimension, typename U::dimension>> {};
|
||||
|
||||
|
||||
// quantities, quantity points, quantity (point) kinds
|
||||
|
||||
template<typename Q1, typename Q2>
|
||||
requires (Quantity<Q1> && Quantity<Q2>) || (QuantityPoint<Q1> && QuantityPoint<Q2>)
|
||||
@@ -89,6 +99,11 @@ struct equivalent_impl<Q1, Q2> : std::conjunction<equivalent_impl<typename Q1::d
|
||||
equivalent_unit<typename Q1::unit, typename Q1::dimension,
|
||||
typename Q2::unit, typename Q2::dimension>> {};
|
||||
|
||||
template<typename QK1, typename QK2>
|
||||
requires (QuantityKind<QK1> && QuantityKind<QK2>) || (QuantityPointKind<QK1> && QuantityPointKind<QK2>)
|
||||
struct equivalent_impl<QK1, QK2> : std::conjunction<equivalent_impl<typename QK1::kind_type, typename QK2::kind_type>,
|
||||
equivalent_impl<typename QK1::quantity_type, typename QK2::quantity_type>> {};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T, typename U>
|
||||
|
||||
@@ -79,4 +79,41 @@ concept QuantityOf = Quantity<Q> && Dimension<Dim> && equivalent<typename Q::dim
|
||||
template<typename Q1, typename Q2>
|
||||
concept QuantityEquivalentTo = Quantity<Q1> && QuantityOf<Q2, typename Q1::dimension>;
|
||||
|
||||
/**
|
||||
* @brief A concept matching all quantity points with provided dimension
|
||||
*
|
||||
* Satisfied by all quantity points with a dimension being the instantiation derived from
|
||||
* the provided dimension type.
|
||||
*/
|
||||
template<typename QP, typename Dim>
|
||||
concept QuantityPointOf = QuantityPoint<QP> && Dimension<Dim> && equivalent<typename QP::dimension, Dim>;
|
||||
|
||||
template<typename QP1, typename QP2>
|
||||
concept QuantityPointEquivalentTo = QuantityPoint<QP1> && QuantityPointOf<QP2, typename QP1::dimension>;
|
||||
|
||||
/**
|
||||
* @brief A concept matching only quantity kinds of a specific kind.
|
||||
*
|
||||
* @tparam QK Quantity kind to verify.
|
||||
* @tparam K Kind type to use for verification.
|
||||
*/
|
||||
template<typename QK, typename K>
|
||||
concept QuantityKindOf = QuantityKind<QK> && Kind<K> && equivalent<typename QK::kind_type, K>;
|
||||
|
||||
template<typename QK1, typename QK2>
|
||||
concept QuantityKindEquivalentTo = QuantityKind<QK1> && QuantityKindOf<QK2, typename QK1::kind_type>;
|
||||
|
||||
/**
|
||||
* @brief A concept matching only quantity point kinds of a specific point kind.
|
||||
*
|
||||
* @tparam QPK Quantity point kind to verify.
|
||||
* @tparam PK Point kind type to use for verification.
|
||||
*/
|
||||
template<typename QPK, typename PK>
|
||||
concept QuantityPointKindOf = QuantityPointKind<QPK> && PointKind<PK> && equivalent<typename QPK::point_kind_type, PK>;
|
||||
|
||||
template<typename QPK1, typename QPK2>
|
||||
concept QuantityPointKindEquivalentTo =
|
||||
QuantityPointKind<QPK1> && QuantityPointKindOf<QPK2, typename QPK1::point_kind_type>;
|
||||
|
||||
} // namespace units
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// 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/bits/basic_concepts.h>
|
||||
#include <units/bits/external/downcasting.h>
|
||||
|
||||
namespace units {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename K, Dimension D>
|
||||
struct _kind_base : downcast_base<_kind_base<K, D>> {
|
||||
using base_kind = K;
|
||||
using dimension = D;
|
||||
};
|
||||
|
||||
template<Kind K>
|
||||
struct _point_kind_base : downcast_base<_point_kind_base<K>> {
|
||||
using base_kind = K;
|
||||
using dimension = typename K::dimension;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<Kind K, Dimension D>
|
||||
requires Kind<downcast<detail::_kind_base<typename K::base_kind, D>>>
|
||||
using downcast_kind = downcast<detail::_kind_base<typename K::base_kind, D>>;
|
||||
|
||||
template<Kind K>
|
||||
requires PointKind<downcast<detail::_point_kind_base<K>>>
|
||||
using downcast_point_kind = downcast<detail::_point_kind_base<K>>;
|
||||
|
||||
template<typename K, Dimension D>
|
||||
struct kind : downcast_dispatch<K, detail::_kind_base<K, D>> {};
|
||||
|
||||
template<typename DK, Kind BK, Dimension D>
|
||||
requires std::same_as<BK, typename BK::base_kind>
|
||||
struct derived_kind : downcast_dispatch<DK, detail::_kind_base<BK, D>> {};
|
||||
|
||||
template<typename DPK, Kind BK>
|
||||
struct point_kind : downcast_dispatch<DPK, detail::_point_kind_base<BK>> {};
|
||||
|
||||
} // namespace units
|
||||
@@ -42,6 +42,12 @@ class quantity;
|
||||
template<Dimension D, UnitOf<D> U, QuantityValue Rep>
|
||||
class quantity_point;
|
||||
|
||||
template<Kind K, UnitOf<typename K::dimension> U, QuantityValue Rep>
|
||||
class quantity_kind;
|
||||
|
||||
template<PointKind PK, UnitOf<typename PK::dimension> U, QuantityValue Rep>
|
||||
class quantity_point_kind;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename D, typename U, typename Rep>
|
||||
@@ -80,7 +86,7 @@ struct cast_traits<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> &&
|
||||
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> {
|
||||
@@ -103,7 +109,7 @@ 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>
|
||||
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)
|
||||
{
|
||||
using traits = detail::cast_traits<Rep, typename To::rep>;
|
||||
@@ -137,7 +143,7 @@ template<Quantity To, typename D, typename U, scalable_with_<typename To::rep> R
|
||||
*
|
||||
* This cast gets only the target dimension to cast to. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_cast<units::physical::si::acceleration>(200_q_Gal);
|
||||
* auto q1 = units::quantity_cast<units::physical::si::dim_acceleration>(200_q_Gal);
|
||||
*
|
||||
* @tparam ToD a dimension type to use for a target quantity
|
||||
*/
|
||||
@@ -179,7 +185,7 @@ template<Unit ToU, typename D, typename U, typename Rep>
|
||||
*
|
||||
* @note This cast is especially useful when working with quantities 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
|
||||
*/
|
||||
@@ -203,6 +209,7 @@ template<Dimension ToD, Unit ToU, typename D, typename U, typename Rep>
|
||||
* @tparam ToRep a representation type to use for a target quantity
|
||||
*/
|
||||
template<QuantityValue 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)
|
||||
{
|
||||
return quantity_cast<quantity<D, U, ToRep>>(q);
|
||||
@@ -218,7 +225,7 @@ template<QuantityValue ToRep, typename D, typename U, scalable_with_<ToRep> Rep>
|
||||
*
|
||||
* auto q1 = units::quantity_point_cast<decltype(quantity_point{0_q_s})>(quantity_point{1_q_ms});
|
||||
* auto q1 = units::quantity_point_cast<units::physical::si::time<units::physical::si::second>>(quantity_point{1_q_ms});
|
||||
* auto q1 = units::quantity_point_cast<units::physical::si::acceleration>(quantity_point{200_q_Gal});
|
||||
* auto q1 = units::quantity_point_cast<units::physical::si::dim_acceleration>(quantity_point{200_q_Gal});
|
||||
* auto q1 = units::quantity_point_cast<units::physical::si::second>(quantity_point{1_q_ms});
|
||||
* auto q1 = units::quantity_point_cast<int>(quantity_point{1_q_ms});
|
||||
*
|
||||
@@ -247,7 +254,7 @@ template<typename CastSpec, typename D, typename U, typename Rep>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@@ -258,6 +265,119 @@ template<Dimension ToD, Unit ToU, typename D, typename U, typename Rep>
|
||||
return quantity_point_cast<quantity_point<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::physical::si::length<units::physical::si::metre>>(ns::width{1 * mm});
|
||||
* auto q1 = units::quantity_kind_cast<units::physical::si::dim_acceleration>(ns::rate_of_climb{200 * Gal});
|
||||
* auto q1 = units::quantity_kind_cast<units::physical::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 (is_specialization_of<CastSpec, quantity_kind> &&
|
||||
requires { quantity_cast<typename CastSpec::quantity_type>(qk.common()); }) ||
|
||||
(Kind<CastSpec> && UnitOf<U, typename CastSpec::dimension>) ||
|
||||
requires { quantity_cast<CastSpec>(qk.common()); }
|
||||
{
|
||||
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::physical::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::physical::si::length<units::physical::si::metre>>(ns::x_coordinate{1 * mm});
|
||||
* auto q1 = units::quantity_point_kind_cast<units::physical::si::dim_acceleration>(quantity_point_kind(ns::rate_of_climb{200 * Gal}));
|
||||
* auto q1 = units::quantity_point_kind_cast<units::physical::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 (is_specialization_of<CastSpec, quantity_point_kind> &&
|
||||
requires { quantity_kind_cast<typename CastSpec::quantity_kind_type>(qpk.relative()); }) ||
|
||||
(PointKind<CastSpec> && UnitOf<U, typename CastSpec::dimension>) ||
|
||||
requires { quantity_kind_cast<CastSpec>(qpk.relative()); }
|
||||
{
|
||||
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::physical::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
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
@@ -0,0 +1,351 @@
|
||||
|
||||
// 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/kind.h>
|
||||
#include <units/quantity.h>
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace units {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<Kind K>
|
||||
inline constexpr auto make_quantity_kind_fn = [](auto q) {
|
||||
using Q = decltype(q);
|
||||
return quantity_kind<K, typename Q::unit, typename Q::rep>(std::move(q));
|
||||
};
|
||||
|
||||
template<QuantityKind QK>
|
||||
inline constexpr auto& make_quantity_kind = make_quantity_kind_fn<typename QK::kind_type>;
|
||||
|
||||
template<Kind K>
|
||||
inline constexpr auto downcasted_kind_fn = [](auto q) {
|
||||
using Q = decltype(q);
|
||||
return make_quantity_kind_fn<downcast_kind<K, typename Q::dimension>>(std::move(q));
|
||||
};
|
||||
|
||||
template<QuantityKind QK>
|
||||
inline constexpr auto& downcasted_kind = downcasted_kind_fn<typename QK::kind_type::base_kind>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief A quantity kind
|
||||
*
|
||||
* A quantity with more specific usage as determined by its kind.
|
||||
* See https://jcgm.bipm.org/vim/en/1.2.html and NOTE 1 at https://jcgm.bipm.org/vim/en/1.1.html.
|
||||
*
|
||||
* @tparam K the kind of quantity
|
||||
* @tparam U the measurement unit of the quantity kind
|
||||
* @tparam Rep the type to be used to represent values of the quantity kind
|
||||
*/
|
||||
template<Kind K, UnitOf<typename K::dimension> U, QuantityValue Rep = double>
|
||||
class quantity_kind {
|
||||
public:
|
||||
using kind_type = K;
|
||||
using quantity_type = quantity<typename K::dimension, U, Rep>;
|
||||
using dimension = typename quantity_type::dimension;
|
||||
using unit = typename quantity_type::unit;
|
||||
using rep = typename quantity_type::rep;
|
||||
|
||||
private:
|
||||
quantity_type q_;
|
||||
|
||||
public:
|
||||
quantity_kind() = default;
|
||||
quantity_kind(const quantity_kind&) = default;
|
||||
quantity_kind(quantity_kind&&) = default;
|
||||
|
||||
template<safe_convertible_to_<rep> Value>
|
||||
requires is_same_v<dimension, dim_one> && std::is_constructible_v<quantity_type, Value>
|
||||
constexpr explicit quantity_kind(const Value& v) : q_(v) {}
|
||||
|
||||
template<typename Q>
|
||||
requires (Quantity<Q> || QuantityLike<Q>) && std::is_constructible_v<quantity_type, Q>
|
||||
constexpr explicit quantity_kind(const Q& q) : q_{q} {}
|
||||
|
||||
template<QuantityKindEquivalentTo<quantity_kind> QK2>
|
||||
requires std::is_convertible_v<typename QK2::quantity_type, quantity_type>
|
||||
constexpr explicit(false) quantity_kind(const QK2& qk) : q_{qk.common()} {}
|
||||
|
||||
quantity_kind& operator=(const quantity_kind&) = default;
|
||||
quantity_kind& operator=(quantity_kind&&) = default;
|
||||
|
||||
[[nodiscard]] constexpr quantity_type common() const noexcept { return q_; }
|
||||
|
||||
[[nodiscard]] static constexpr quantity_kind zero() noexcept
|
||||
requires requires { quantity_type::zero(); }
|
||||
{
|
||||
return quantity_kind(quantity_type::zero());
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr quantity_kind one() noexcept
|
||||
requires requires { quantity_type::one(); }
|
||||
{
|
||||
return quantity_kind(quantity_type::one());
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr quantity_kind min() noexcept
|
||||
requires requires { quantity_type::min(); }
|
||||
{
|
||||
return quantity_kind(quantity_type::min());
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr quantity_kind max() noexcept
|
||||
requires requires { quantity_type::max(); }
|
||||
{
|
||||
return quantity_kind(quantity_type::max());
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr quantity_kind operator+() const
|
||||
requires requires(quantity_type q) { +q; }
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr QuantityKind auto operator-() const
|
||||
requires requires(quantity_type q) { -q; }
|
||||
{
|
||||
return detail::make_quantity_kind<quantity_kind>(-q_);
|
||||
}
|
||||
|
||||
constexpr quantity_kind& operator++()
|
||||
requires requires(quantity_type q) { ++q; }
|
||||
{
|
||||
++q_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr quantity_kind operator++(int)
|
||||
requires requires(quantity_type q) { q++; }
|
||||
{
|
||||
return quantity_kind(q_++);
|
||||
}
|
||||
|
||||
constexpr quantity_kind& operator--()
|
||||
requires requires(quantity_type q) { --q; }
|
||||
{
|
||||
--q_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr quantity_kind operator--(int)
|
||||
requires requires(quantity_type q) { q--; }
|
||||
{
|
||||
return quantity_kind(q_--);
|
||||
}
|
||||
|
||||
constexpr quantity_kind& operator+=(const quantity_kind& qk)
|
||||
requires requires(quantity_type q) { q += qk.common(); }
|
||||
{
|
||||
q_ += qk.common();
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr quantity_kind& operator-=(const quantity_kind& qk)
|
||||
requires requires(quantity_type q) { q -= qk.common(); }
|
||||
{
|
||||
q_ -= qk.common();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Rep2>
|
||||
constexpr quantity_kind& operator*=(const Rep2& rhs)
|
||||
requires requires(quantity_type q) { q *= rhs; }
|
||||
{
|
||||
q_ *= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Rep2>
|
||||
constexpr quantity_kind& operator/=(const Rep2& rhs)
|
||||
requires requires(quantity_type q) { q /= rhs; }
|
||||
{
|
||||
q_ /= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr quantity_kind& operator%=(const rep& rhs)
|
||||
requires requires(quantity_type q) { q %= rhs; }
|
||||
{
|
||||
q_ %= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr quantity_kind& operator%=(const quantity_kind& qk)
|
||||
requires requires(quantity_type q) { q %= qk.common(); }
|
||||
{
|
||||
q_ %= qk.common();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Hidden Friends
|
||||
// Below friend functions are to be found via argument-dependent lookup only
|
||||
[[nodiscard]] friend constexpr quantity_kind operator+(const quantity_kind& lhs, const quantity_kind& rhs)
|
||||
requires requires { lhs.common() + rhs.common(); }
|
||||
{
|
||||
return quantity_kind(lhs.common() + rhs.common());
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr quantity_kind operator-(const quantity_kind& lhs, const quantity_kind& rhs)
|
||||
requires requires { lhs.common() - rhs.common(); }
|
||||
{
|
||||
return quantity_kind(lhs.common() - rhs.common());
|
||||
}
|
||||
|
||||
template<QuantityValue Value>
|
||||
[[nodiscard]] friend constexpr QuantityKind auto operator*(const quantity_kind& qk, const Value& v)
|
||||
requires requires { { qk.common() * v } -> Quantity; }
|
||||
{
|
||||
return detail::make_quantity_kind<quantity_kind>(qk.common() * v);
|
||||
}
|
||||
|
||||
template<QuantityValue Value>
|
||||
[[nodiscard]] friend constexpr QuantityKind auto operator*(const Value& v, const quantity_kind& qk)
|
||||
requires requires { { v * qk.common() } -> Quantity; }
|
||||
{
|
||||
return detail::make_quantity_kind<quantity_kind>(v * qk.common());
|
||||
}
|
||||
|
||||
template<QuantityValue Value>
|
||||
[[nodiscard]] friend constexpr QuantityKind auto operator/(const quantity_kind& qk, const Value& v)
|
||||
requires requires { { qk.common() / v } -> Quantity; }
|
||||
{
|
||||
return detail::make_quantity_kind<quantity_kind>(qk.common() / v);
|
||||
}
|
||||
|
||||
template<QuantityValue Value>
|
||||
[[nodiscard]] friend constexpr QuantityKind auto operator/(const Value& v, const quantity_kind& qk)
|
||||
requires requires { { v / qk.common() } -> Quantity; }
|
||||
{
|
||||
return detail::downcasted_kind<quantity_kind>(v / qk.common());
|
||||
}
|
||||
|
||||
template<QuantityValue Value>
|
||||
[[nodiscard]] friend constexpr QuantityKind auto operator%(const quantity_kind& qk, const Value& v)
|
||||
requires requires { qk.common() % v; }
|
||||
{
|
||||
return detail::make_quantity_kind<quantity_kind>(qk.common() % v);
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr quantity_kind operator%(const quantity_kind& lhs, const quantity_kind& rhs)
|
||||
requires requires { lhs.common() % rhs.common(); }
|
||||
{
|
||||
return quantity_kind(lhs.common() % rhs.common());
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr auto operator<=>(const quantity_kind& lhs, const quantity_kind& rhs)
|
||||
requires std::three_way_comparable<quantity_type>
|
||||
{
|
||||
return lhs.common() <=> rhs.common();
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator==(const quantity_kind&, const quantity_kind&) = default;
|
||||
};
|
||||
|
||||
template<Kind K, QuantityValue V>
|
||||
explicit(false) quantity_kind(K, V) -> quantity_kind<K, one, V>;
|
||||
|
||||
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
|
||||
[[nodiscard]] constexpr QuantityKind auto operator+(const QK1& lhs, const QK2& rhs)
|
||||
requires requires { lhs.common() + rhs.common(); }
|
||||
{
|
||||
return detail::make_quantity_kind<QK1>(lhs.common() + rhs.common());
|
||||
}
|
||||
|
||||
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
|
||||
[[nodiscard]] constexpr QuantityKind auto operator-(const QK1& lhs, const QK2& rhs)
|
||||
requires requires { lhs.common() - rhs.common(); }
|
||||
{
|
||||
return detail::make_quantity_kind<QK1>(lhs.common() - rhs.common());
|
||||
}
|
||||
|
||||
template<QuantityKind QK, Quantity Q>
|
||||
[[nodiscard]] constexpr QuantityKind auto operator*(const QK& lhs, const Q& rhs)
|
||||
requires requires { lhs.common() * rhs; }
|
||||
{
|
||||
return detail::downcasted_kind<QK>(lhs.common() * rhs);
|
||||
}
|
||||
|
||||
template<Quantity Q, QuantityKind QK>
|
||||
[[nodiscard]] constexpr QuantityKind auto operator*(const Q& lhs, const QK& rhs)
|
||||
requires requires { lhs * rhs.common(); }
|
||||
{
|
||||
return detail::downcasted_kind<QK>(lhs * rhs.common());
|
||||
}
|
||||
|
||||
template<QuantityKind QK, Quantity Q>
|
||||
[[nodiscard]] constexpr QuantityKind auto operator/(const QK& lhs, const Q& rhs)
|
||||
requires requires { lhs.common() / rhs; }
|
||||
{
|
||||
return detail::downcasted_kind<QK>(lhs.common() / rhs);
|
||||
}
|
||||
|
||||
template<Quantity Q, QuantityKind QK>
|
||||
[[nodiscard]] constexpr QuantityKind auto operator/(const Q& lhs, const QK& rhs)
|
||||
requires requires { lhs / rhs.common(); }
|
||||
{
|
||||
return detail::downcasted_kind<QK>(lhs / rhs.common());
|
||||
}
|
||||
|
||||
template<QuantityKind QK, Dimensionless D>
|
||||
[[nodiscard]] constexpr QuantityKind auto operator%(const QK& lhs, const D& rhs)
|
||||
requires requires { lhs.common() % rhs; }
|
||||
{
|
||||
return detail::make_quantity_kind<QK>(lhs.common() % rhs);
|
||||
}
|
||||
|
||||
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
|
||||
[[nodiscard]] constexpr QuantityKind auto operator%(const QK1& lhs, const QK2& rhs)
|
||||
requires requires { lhs.common() % rhs.common(); }
|
||||
{
|
||||
return detail::make_quantity_kind<QK1>(lhs.common() % rhs.common());
|
||||
}
|
||||
|
||||
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
|
||||
requires std::three_way_comparable_with<typename QK1::quantity_type, typename QK2::quantity_type>
|
||||
[[nodiscard]] constexpr auto operator<=>(const QK1& lhs, const QK2& rhs)
|
||||
{
|
||||
return lhs.common() <=> rhs.common();
|
||||
}
|
||||
|
||||
template<QuantityKind QK1, QuantityKindEquivalentTo<QK1> QK2>
|
||||
requires std::equality_comparable_with<typename QK1::quantity_type, typename QK2::quantity_type>
|
||||
[[nodiscard]] constexpr bool operator==(const QK1& lhs, const QK2& rhs)
|
||||
{
|
||||
return lhs.common() == rhs.common();
|
||||
}
|
||||
|
||||
// type traits
|
||||
namespace detail {
|
||||
|
||||
template<typename K, typename U, typename Rep>
|
||||
inline constexpr bool is_quantity_kind<quantity_kind<K, U, Rep>> = true;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace units
|
||||
@@ -53,8 +53,14 @@ public:
|
||||
quantity_point(const quantity_point&) = default;
|
||||
quantity_point(quantity_point&&) = default;
|
||||
|
||||
template<Quantity Q>
|
||||
requires std::is_convertible_v<Q, quantity_type>
|
||||
template<safe_convertible_to_<rep> Value>
|
||||
requires is_same_v<dimension, dim_one> && std::is_constructible_v<quantity_type, Value>
|
||||
constexpr explicit quantity_point(const Value& v) : q_(v) {}
|
||||
|
||||
constexpr explicit quantity_point(const quantity_type& q) : q_{q} {}
|
||||
|
||||
template<QuantityLike Q>
|
||||
requires std::is_constructible_v<quantity_type, Q>
|
||||
constexpr explicit quantity_point(const Q& q) : q_{q} {}
|
||||
|
||||
template<QuantityPoint QP2>
|
||||
@@ -169,8 +175,12 @@ public:
|
||||
|
||||
};
|
||||
|
||||
template<typename D, typename U, typename Rep>
|
||||
quantity_point(quantity<D, U, Rep>) -> quantity_point<D, U, Rep>;
|
||||
template<QuantityValue V>
|
||||
explicit(false) quantity_point(V) -> quantity_point<dim_one, one, V>;
|
||||
|
||||
template<QuantityLike Q>
|
||||
quantity_point(Q) -> quantity_point<typename quantity_like_traits<Q>::dimension,
|
||||
typename quantity_like_traits<Q>::unit, typename quantity_like_traits<Q>::rep>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
|
||||
// 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/quantity_kind.h>
|
||||
#include <units/quantity_point.h>
|
||||
#include <compare>
|
||||
|
||||
namespace units {
|
||||
|
||||
/**
|
||||
* @brief A quantity point kind
|
||||
*
|
||||
* An absolute quantity kind with respect to zero (which represents some origin).
|
||||
*
|
||||
* @tparam PK the point kind of quantity point
|
||||
* @tparam U the measurement unit of the quantity point kind
|
||||
* @tparam Rep the type to be used to represent values of the quantity point kind
|
||||
*/
|
||||
template<PointKind PK, UnitOf<typename PK::dimension> U, QuantityValue Rep = double>
|
||||
class quantity_point_kind {
|
||||
public:
|
||||
using point_kind_type = PK;
|
||||
using kind_type = typename PK::base_kind;
|
||||
using quantity_kind_type = quantity_kind<kind_type, U, Rep>;
|
||||
using quantity_type = typename quantity_kind_type::quantity_type;
|
||||
using dimension = typename quantity_type::dimension;
|
||||
using unit = typename quantity_type::unit;
|
||||
using rep = typename quantity_type::rep;
|
||||
|
||||
private:
|
||||
quantity_kind_type qk_;
|
||||
|
||||
public:
|
||||
quantity_point_kind() = default;
|
||||
quantity_point_kind(const quantity_point_kind&) = default;
|
||||
quantity_point_kind(quantity_point_kind&&) = default;
|
||||
|
||||
template<safe_convertible_to_<rep> Value>
|
||||
requires std::is_constructible_v<quantity_kind_type, Value>
|
||||
constexpr explicit quantity_point_kind(const Value& v) : qk_(v) {}
|
||||
|
||||
constexpr explicit quantity_point_kind(const quantity_type& q) : qk_{q} {}
|
||||
|
||||
template<QuantityLike Q>
|
||||
requires std::is_constructible_v<quantity_type, Q>
|
||||
constexpr explicit quantity_point_kind(const Q& q) : qk_{q} {}
|
||||
|
||||
constexpr explicit quantity_point_kind(const quantity_point<dimension, U, Rep>& qp) : qk_{qp.relative()} {}
|
||||
|
||||
constexpr explicit quantity_point_kind(const quantity_kind_type& qk) : qk_{qk} {}
|
||||
|
||||
template<QuantityPointKindEquivalentTo<quantity_point_kind> QPK2>
|
||||
requires std::is_convertible_v<typename QPK2::quantity_kind_type, quantity_kind_type>
|
||||
constexpr explicit(false) quantity_point_kind(const QPK2& qpk) : qk_{qpk.relative()} {}
|
||||
|
||||
quantity_point_kind& operator=(const quantity_point_kind&) = default;
|
||||
quantity_point_kind& operator=(quantity_point_kind&&) = default;
|
||||
|
||||
[[nodiscard]] constexpr quantity_kind_type relative() const noexcept { return qk_; }
|
||||
|
||||
[[nodiscard]] static constexpr quantity_point_kind min() noexcept
|
||||
requires requires { quantity_kind_type::min(); }
|
||||
{
|
||||
return quantity_point_kind(quantity_kind_type::min());
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr quantity_point_kind max() noexcept
|
||||
requires requires { quantity_kind_type::max(); }
|
||||
{
|
||||
return quantity_point_kind(quantity_kind_type::max());
|
||||
}
|
||||
|
||||
constexpr quantity_point_kind& operator++()
|
||||
requires requires(quantity_kind_type qk) { ++qk; }
|
||||
{
|
||||
++qk_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr quantity_point_kind operator++(int)
|
||||
requires requires(quantity_kind_type qk) { qk++; }
|
||||
{
|
||||
return quantity_point_kind(qk_++);
|
||||
}
|
||||
|
||||
constexpr quantity_point_kind& operator--()
|
||||
requires requires(quantity_kind_type qk) { --qk; }
|
||||
{
|
||||
--qk_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr quantity_point_kind operator--(int)
|
||||
requires requires(quantity_kind_type qk) { qk--; }
|
||||
{
|
||||
return quantity_point_kind(qk_--);
|
||||
}
|
||||
|
||||
constexpr quantity_point_kind& operator+=(const quantity_kind_type& qk)
|
||||
requires requires(quantity_kind_type qk1, const quantity_kind_type qk2) { qk1 += qk2; }
|
||||
{
|
||||
qk_ += qk;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr quantity_point_kind& operator-=(const quantity_kind_type& qk)
|
||||
requires requires(quantity_kind_type qk1, const quantity_kind_type qk2) { qk1 -= qk2; }
|
||||
{
|
||||
qk_ -= qk;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Hidden Friends
|
||||
// Below friend functions are to be found via argument-dependent lookup only
|
||||
|
||||
template<QuantityKind QK>
|
||||
[[nodiscard]] friend constexpr QuantityPointKind auto operator+(const quantity_point_kind& lhs, const QK& rhs)
|
||||
requires requires { lhs.relative() + rhs; }
|
||||
{
|
||||
return units::quantity_point_kind(lhs.relative() + rhs);
|
||||
}
|
||||
|
||||
template<QuantityKind QK>
|
||||
[[nodiscard]] friend constexpr QuantityPointKind auto operator+(const QK& lhs, const quantity_point_kind& rhs)
|
||||
requires requires { lhs + rhs.relative(); }
|
||||
{
|
||||
return units::quantity_point_kind(lhs + rhs.relative());
|
||||
}
|
||||
|
||||
template<QuantityKind QK>
|
||||
[[nodiscard]] friend constexpr QuantityPointKind auto operator-(const quantity_point_kind& lhs, const QK& rhs)
|
||||
requires requires { lhs.relative() - rhs; }
|
||||
{
|
||||
return units::quantity_point_kind(lhs.relative() - rhs);
|
||||
}
|
||||
|
||||
[[nodiscard]] friend constexpr QuantityKind auto operator-(const quantity_point_kind& lhs, const quantity_point_kind& rhs)
|
||||
requires requires { lhs.relative() - rhs.relative(); }
|
||||
{
|
||||
return lhs.relative() - rhs.relative();
|
||||
}
|
||||
|
||||
template<QuantityPointKind QPK>
|
||||
requires std::three_way_comparable_with<quantity_kind_type, typename QPK::quantity_kind_type>
|
||||
[[nodiscard]] friend constexpr auto operator<=>(const quantity_point_kind& lhs, const QPK& rhs)
|
||||
{
|
||||
return lhs.relative() <=> rhs.relative();
|
||||
}
|
||||
|
||||
template<QuantityPointKind QPK>
|
||||
requires std::equality_comparable_with<quantity_kind_type, typename QPK::quantity_kind_type>
|
||||
[[nodiscard]] friend constexpr bool operator==(const quantity_point_kind& lhs, const QPK& rhs)
|
||||
{
|
||||
return lhs.relative() == rhs.relative();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<PointKind PK, QuantityValue V>
|
||||
explicit(false) quantity_point_kind(PK, V) -> quantity_point_kind<PK, one, V>;
|
||||
|
||||
template<QuantityKind QK>
|
||||
quantity_point_kind(QK) ->
|
||||
quantity_point_kind<downcast_point_kind<typename QK::kind_type>, typename QK::unit, typename QK::rep>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename PK, typename U, typename Rep>
|
||||
inline constexpr bool is_quantity_point_kind<quantity_point_kind<PK, U, Rep>> = true;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace units
|
||||
Reference in New Issue
Block a user