feat: quantity_kind and quantity_point_kind

This commit is contained in:
Johel Ernesto Guerrero Peña
2021-02-15 19:31:08 +01:00
committed by Mateusz Pusz
parent 384f4b2624
commit 6bf09aa646
32 changed files with 2757 additions and 202 deletions
+76 -4
View File
@@ -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
/**
+38 -12
View File
@@ -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>;
};
}
+17 -2
View File
@@ -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>
+37
View File
@@ -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
+64
View File
@@ -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
+126 -6
View File
@@ -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
+351
View File
@@ -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
+14 -4
View File
@@ -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 {
+195
View File
@@ -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