forked from mpusz/mp-units
feat: Added support for natural units-like systems + dimension_one cleanup
This commit is contained in:
@@ -472,23 +472,47 @@ template<std::intmax_t Num, std::intmax_t Den, template<typename...> typename To
|
||||
|
||||
|
||||
// expr_map
|
||||
template<typename T, template<typename> typename Proj>
|
||||
struct expr_type_map;
|
||||
|
||||
template<typename T, template<typename...> typename Proj>
|
||||
struct expr_type_map {
|
||||
template<typename T, template<typename> typename Proj>
|
||||
requires requires { typename Proj<T>; }
|
||||
struct expr_type_map<T, Proj> {
|
||||
using type = Proj<T>;
|
||||
};
|
||||
|
||||
template<typename F, int... Ints, template<typename...> typename Proj>
|
||||
template<typename F, int... Ints, template<typename> typename Proj>
|
||||
requires requires { typename Proj<F>; }
|
||||
struct expr_type_map<power<F, Ints...>, Proj> {
|
||||
using type = power<Proj<F>, Ints...>;
|
||||
};
|
||||
|
||||
template<template<typename...> typename Proj, template<typename...> typename To, typename OneType,
|
||||
template<typename, typename> typename Pred, typename... Nums, typename... Dens>
|
||||
template<typename T, template<typename> typename Proj>
|
||||
concept expr_type_projectable = (requires { typename Proj<T>; } ||
|
||||
(is_specialization_of_power<T> && requires { typename Proj<typename T::factor>; }));
|
||||
|
||||
template<typename T, template<typename> typename Proj>
|
||||
inline constexpr bool expr_projectable_impl = false;
|
||||
|
||||
template<typename... Ts, template<typename> typename Proj>
|
||||
inline constexpr bool expr_projectable_impl<type_list<Ts...>, Proj> = (... && expr_type_projectable<Ts, Proj>);
|
||||
|
||||
template<typename T, template<typename> typename Proj>
|
||||
concept expr_projectable = requires {
|
||||
typename T::_num_;
|
||||
typename T::_den_;
|
||||
requires type_list_size<typename T::_num_> + type_list_size<typename T::_den_> > 0;
|
||||
requires expr_projectable_impl<typename T::_num_, Proj>;
|
||||
requires expr_projectable_impl<typename T::_den_, Proj>;
|
||||
};
|
||||
|
||||
template<template<typename> typename Proj, template<typename...> typename To, typename OneType,
|
||||
template<typename, typename> typename Pred, expr_type_projectable<Proj>... Nums,
|
||||
expr_type_projectable<Proj>... Dens>
|
||||
[[nodiscard]] consteval auto expr_map_impl(type_list<Nums...>, type_list<Dens...>)
|
||||
{
|
||||
using nums = type_list_sort<type_list<typename expr_type_map<Nums, Proj>::type...>, Pred>;
|
||||
using dens = type_list_sort<type_list<typename expr_type_map<Dens, Proj>::type...>, Pred>;
|
||||
using nums = type_list_sort<type_list<typename expr_type_map<std::remove_const_t<Nums>, Proj>::type...>, Pred>;
|
||||
using dens = type_list_sort<type_list<typename expr_type_map<std::remove_const_t<Dens>, Proj>::type...>, Pred>;
|
||||
return detail::get_optimized_expression<nums, dens, OneType, Pred, To>();
|
||||
}
|
||||
|
||||
@@ -501,8 +525,8 @@ template<template<typename...> typename Proj, template<typename...> typename To,
|
||||
* @tparam Pred binary less then predicate
|
||||
* @tparam T expression template to map from
|
||||
*/
|
||||
template<template<typename...> typename Proj, template<typename...> typename To, typename OneType,
|
||||
template<typename, typename> typename Pred, typename T>
|
||||
template<template<typename> typename Proj, template<typename...> typename To, typename OneType,
|
||||
template<typename, typename> typename Pred, expr_projectable<Proj> T>
|
||||
[[nodiscard]] consteval auto expr_map(T)
|
||||
{
|
||||
return expr_map_impl<Proj, To, OneType, Pred>(typename T::_num_{}, typename T::_den_{});
|
||||
|
||||
@@ -53,13 +53,16 @@ concept DerivedDimension = detail::is_derived_dimension<T>;
|
||||
template<typename T>
|
||||
concept Dimension = BaseDimension<T> || DerivedDimension<T>;
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
[[nodiscard]] consteval Dimension auto get_dimension_for(Unit auto);
|
||||
template<Unit auto U, Dimension auto Dim>
|
||||
inline constexpr bool is_valid_unit_for_dimension = false;
|
||||
|
||||
}
|
||||
|
||||
template<typename U, typename Dim>
|
||||
concept valid_unit_for_dimension = Unit<U> && Dimension<Dim> && detail::is_valid_unit_for_dimension<U{}, Dim{}>;
|
||||
|
||||
template<Dimension D, Unit U>
|
||||
struct reference;
|
||||
|
||||
@@ -100,12 +103,10 @@ struct base_dimension {
|
||||
static constexpr auto symbol = Symbol; ///< Unique base dimension identifier
|
||||
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
template<Unit U, typename Self>
|
||||
requires(convertible(Self{}, detail::get_dimension_for(U{})))
|
||||
template<typename Self, valid_unit_for_dimension<Self> U>
|
||||
[[nodiscard]] constexpr reference<Self, U> operator[](this const Self, U)
|
||||
#else
|
||||
template<Unit U>
|
||||
requires(convertible(Self{}, detail::get_dimension_for(U{})))
|
||||
template<valid_unit_for_dimension<Self> U>
|
||||
[[nodiscard]] constexpr reference<Self, U> operator[](U) const
|
||||
#endif
|
||||
{
|
||||
@@ -122,13 +123,13 @@ template<typename T1, typename T2>
|
||||
using type_list_of_base_dimension_less = expr_less<T1, T2, base_dimension_less>;
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_dimensionless = false;
|
||||
inline constexpr bool is_dimension_one = false;
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_power_of_dim =
|
||||
requires {
|
||||
requires is_specialization_of_power<T> &&
|
||||
(BaseDimension<typename T::factor> || is_dimensionless<typename T::factor>);
|
||||
(BaseDimension<typename T::factor> || is_dimension_one<typename T::factor>);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@@ -136,13 +137,13 @@ inline constexpr bool is_per_of_dims = false;
|
||||
|
||||
template<typename... Ts>
|
||||
inline constexpr bool is_per_of_dims<per<Ts...>> =
|
||||
(... && (BaseDimension<Ts> || is_dimensionless<Ts> || is_power_of_dim<Ts>));
|
||||
(... && (BaseDimension<Ts> || is_dimension_one<Ts> || is_power_of_dim<Ts>));
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T>
|
||||
concept DerivedDimensionSpec =
|
||||
BaseDimension<T> || detail::is_dimensionless<T> || detail::is_power_of_dim<T> || detail::is_per_of_dims<T>;
|
||||
BaseDimension<T> || detail::is_dimension_one<T> || detail::is_power_of_dim<T> || detail::is_per_of_dims<T>;
|
||||
|
||||
template<typename...>
|
||||
struct derived_dimension;
|
||||
@@ -167,7 +168,7 @@ struct derived_dimension_impl : detail::expr_fractions<derived_dimension<>, Ds..
|
||||
* more digestable for the user. The positive exponents are ordered first and all negative exponents are put as a list
|
||||
* into the `per<...>` class template. If a power of exponent is different than `1` the dimension type is enclosed in
|
||||
* `power<Dim, Num, Den>` class template. Otherwise, it is just put directly in the list without any wrapper. There
|
||||
* is also one special case. In case all of the exponents are negative than the `dimensionless` being a dimension of
|
||||
* is also one special case. In case all of the exponents are negative than the `dimension_one` being a dimension of
|
||||
* a dimensionless quantity is put in the front to increase the readability.
|
||||
*
|
||||
* For example:
|
||||
@@ -182,7 +183,7 @@ struct derived_dimension_impl : detail::expr_fractions<derived_dimension<>, Ds..
|
||||
* DERIVED_DIMENSION(torque, moment_of_force);
|
||||
* @endcode
|
||||
*
|
||||
* - `frequency` will be derived from type `derived_dimension<dimensionless, per<time>>`
|
||||
* - `frequency` will be derived from type `derived_dimension<dimension_one, per<time>>`
|
||||
* - `speed` will be derived from type `derived_dimension<length, per<time>>`
|
||||
* - `acceleration` will be derived from type `derived_dimension<length, per<power<time, 2>>>`
|
||||
* - `force` will be derived from type `derived_dimension<length, mass, per<power<time, 2>>>`
|
||||
@@ -201,7 +202,7 @@ struct derived_dimension_impl : detail::expr_fractions<derived_dimension<>, Ds..
|
||||
* dimensionally equivalent quantities.
|
||||
*
|
||||
* @tparam Ds a parameter pack consisting tokens allowed in the dimension specification
|
||||
* (base dimensions, `dimensionless`, `power<Dim, Num, Den>`, `per<...>`)
|
||||
* (base dimensions, `dimension_one`, `power<Dim, Num, Den>`, `per<...>`)
|
||||
*
|
||||
* @note User should not instantiate this type! It is not exported from the C++ module. The library will
|
||||
* instantiate this type automatically based on the dimensional arithmetic equation provided by the user.
|
||||
@@ -210,8 +211,8 @@ struct derived_dimension_impl : detail::expr_fractions<derived_dimension<>, Ds..
|
||||
|
||||
template<DerivedDimensionSpec... Ds>
|
||||
struct derived_dimension : detail::derived_dimension_impl<Ds...> {
|
||||
template<Unit U, typename Self>
|
||||
requires(convertible(Self{}, detail::get_dimension_for(U{})))
|
||||
template<typename Self, Unit U>
|
||||
requires valid_unit_for_dimension<U, Self> || (sizeof...(Ds) == 0 && convertible(U{}, one))
|
||||
[[nodiscard]] constexpr reference<Self, U> operator[](this const Self, U)
|
||||
{
|
||||
return {};
|
||||
@@ -226,7 +227,7 @@ struct derived_dimension;
|
||||
template<DerivedDimensionSpec... Ds>
|
||||
struct derived_dimension<Ds...> : detail::derived_dimension_impl<Ds...> {
|
||||
template<Unit U>
|
||||
requires(convertible(derived_dimension{}, detail::get_dimension_for(U{})))
|
||||
requires valid_unit_for_dimension<U, derived_dimension> || (sizeof...(Ds) == 0 && convertible(U{}, one))
|
||||
[[nodiscard]] constexpr reference<derived_dimension, U> operator[](U) const
|
||||
{
|
||||
return {};
|
||||
@@ -236,7 +237,8 @@ struct derived_dimension<Ds...> : detail::derived_dimension_impl<Ds...> {
|
||||
template<typename Self, DerivedDimension D>
|
||||
struct derived_dimension<Self, D> : D {
|
||||
template<Unit U>
|
||||
requires(convertible(Self{}, detail::get_dimension_for(U{})))
|
||||
requires valid_unit_for_dimension<U, Self> ||
|
||||
(convertible(derived_dimension{}, derived_dimension<>{}) && convertible(U{}, one))
|
||||
[[nodiscard]] constexpr reference<Self, U> operator[](U) const
|
||||
{
|
||||
return {};
|
||||
@@ -266,13 +268,13 @@ inline constexpr bool is_derived_dimension<T> = true;
|
||||
* Dimension for which all the exponents of the factors corresponding to the base
|
||||
* dimensions are zero. Also commonly named as "dimensionless".
|
||||
*/
|
||||
inline constexpr struct dimensionless : derived_dimension<> {
|
||||
} dimensionless;
|
||||
inline constexpr struct dimension_one : derived_dimension<> {
|
||||
} dimension_one;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<>
|
||||
inline constexpr bool is_dimensionless<struct dimensionless> = true;
|
||||
inline constexpr bool is_dimension_one<struct dimension_one> = true;
|
||||
|
||||
template<Dimension T>
|
||||
struct dim_type_impl {
|
||||
@@ -295,14 +297,14 @@ using dim_type = dim_type_impl<T>::type;
|
||||
template<Dimension Lhs, Dimension Rhs>
|
||||
[[nodiscard]] consteval Dimension auto operator*(Lhs, Rhs)
|
||||
{
|
||||
return detail::expr_multiply<derived_dimension, struct dimensionless, detail::type_list_of_base_dimension_less>(
|
||||
return detail::expr_multiply<derived_dimension, struct dimension_one, detail::type_list_of_base_dimension_less>(
|
||||
detail::dim_type<Lhs>{}, detail::dim_type<Rhs>{});
|
||||
}
|
||||
|
||||
template<Dimension Lhs, Dimension Rhs>
|
||||
[[nodiscard]] consteval Dimension auto operator/(Lhs, Rhs)
|
||||
{
|
||||
return detail::expr_divide<derived_dimension, struct dimensionless, detail::type_list_of_base_dimension_less>(
|
||||
return detail::expr_divide<derived_dimension, struct dimension_one, detail::type_list_of_base_dimension_less>(
|
||||
detail::dim_type<Lhs>{}, detail::dim_type<Rhs>{});
|
||||
}
|
||||
|
||||
@@ -310,7 +312,7 @@ template<Dimension D>
|
||||
[[nodiscard]] consteval Dimension auto operator/(int value, D)
|
||||
{
|
||||
gsl_Expects(value == 1);
|
||||
return detail::expr_invert<derived_dimension, struct dimensionless>(detail::dim_type<D>{});
|
||||
return detail::expr_invert<derived_dimension, struct dimension_one>(detail::dim_type<D>{});
|
||||
}
|
||||
|
||||
template<Dimension D>
|
||||
@@ -347,7 +349,7 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Dimension D>
|
||||
else
|
||||
return derived_dimension<power<D, Num, Den>>{};
|
||||
} else
|
||||
return detail::expr_pow<Num, Den, derived_dimension, struct dimensionless,
|
||||
return detail::expr_pow<Num, Den, derived_dimension, struct dimension_one,
|
||||
detail::type_list_of_base_dimension_less>(d);
|
||||
}
|
||||
|
||||
@@ -366,16 +368,24 @@ using to_base_dimension = std::remove_const_t<decltype(U::base_dimension)>;
|
||||
|
||||
template<typename... Us>
|
||||
[[nodiscard]] consteval Dimension auto get_dimension_for_impl(const derived_unit<Us...>& u)
|
||||
requires detail::expr_projectable<derived_unit<Us...>, to_base_dimension>
|
||||
{
|
||||
return detail::expr_map<to_base_dimension, derived_dimension, struct dimensionless,
|
||||
return detail::expr_map<to_base_dimension, derived_dimension, struct dimension_one,
|
||||
detail::type_list_of_base_dimension_less>(u);
|
||||
}
|
||||
|
||||
[[nodiscard]] consteval Dimension auto get_dimension_for(Unit auto u)
|
||||
template<typename U>
|
||||
concept associated_unit = Unit<U> && requires(U u) { get_dimension_for_impl(get_canonical_unit(u).reference_unit); };
|
||||
|
||||
[[nodiscard]] consteval Dimension auto get_dimension_for(associated_unit auto u)
|
||||
{
|
||||
return get_dimension_for_impl(get_canonical_unit(u).reference_unit);
|
||||
}
|
||||
|
||||
template<Unit auto U, Dimension auto Dim>
|
||||
requires requires { detail::get_dimension_for(U); } && (convertible(Dim, detail::get_dimension_for(U)))
|
||||
inline constexpr bool is_valid_unit_for_dimension<U, Dim> = true;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ inline constexpr auto make_quantity = [](Representation auto&& v) {
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
concept quantity_one = Quantity<T> && (T::dimension == dimensionless) && (T::unit == one);
|
||||
concept quantity_one = Quantity<T> && (T::dimension == dimension_one) && (T::unit == one);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
@@ -419,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[::units::one] / reference>(v / q.number());
|
||||
return detail::make_quantity<dimension_one[::units::one] / reference>(v / q.number());
|
||||
}
|
||||
|
||||
template<typename Value>
|
||||
@@ -463,7 +463,7 @@ template<auto R, typename Rep>
|
||||
explicit(false) quantity(quantity<R, Rep>) -> quantity<R, Rep>;
|
||||
|
||||
template<Representation Rep>
|
||||
explicit(false) quantity(Rep)->quantity<dimensionless[one], Rep>;
|
||||
explicit(false) quantity(Rep)->quantity<dimension_one[one], Rep>;
|
||||
|
||||
template<QuantityLike Q>
|
||||
explicit quantity(Q)
|
||||
@@ -506,7 +506,7 @@ template<Quantity Q1, Quantity Q2>
|
||||
|
||||
template<Quantity Q1, Quantity Q2>
|
||||
requires(!floating_point_<typename Q1::rep>) && (!floating_point_<typename Q2::rep>) &&
|
||||
(std::convertible_to<Q2, Q1> || quantity_of<Q2, dimensionless>) &&
|
||||
(std::convertible_to<Q2, Q1> || quantity_of<Q2, dimension_one>) &&
|
||||
(quantity_value_for_<std::modulus<>, typename Q1::rep, typename Q2::rep>)
|
||||
[[nodiscard]] constexpr Quantity auto operator%(const Q1& lhs, const Q2& rhs)
|
||||
{
|
||||
|
||||
@@ -28,22 +28,6 @@
|
||||
|
||||
namespace units {
|
||||
|
||||
template<Dimension auto Dim, Unit auto CoU>
|
||||
struct system_reference;
|
||||
|
||||
// namespace detail {
|
||||
|
||||
// template<typename Child, auto Dim, auto CoU>
|
||||
// void to_base_specialization_of_inline constexpr struct const volatile system_reference<Child : system_reference<Dim,
|
||||
// CoU>*> {} const volatile system_reference<Child;
|
||||
|
||||
// template<typename T>
|
||||
// // inline constexpr bool // TODO: Replace with concept when it works with MSVC
|
||||
// concept is_derived_from_specialization_of_system_reference =
|
||||
// requires(T* t) { detail::to_base_specialization_of_system_reference(t); };
|
||||
|
||||
// } // namespace detail
|
||||
|
||||
/**
|
||||
* @brief The type for quantity references
|
||||
*
|
||||
@@ -83,15 +67,6 @@ struct system_reference;
|
||||
* The following syntaxes are not allowed:
|
||||
* `2 / s`, `km * 3`, `s / 4`, `70 * km / h`.
|
||||
*/
|
||||
// template<typename R, Unit U>
|
||||
// requires detail::is_derived_from_specialization_of_system_reference<R>
|
||||
// struct reference<R, U> {
|
||||
// using system_reference = R;
|
||||
// static constexpr auto dimension = R::dimension;
|
||||
// static constexpr U unit{};
|
||||
// // static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = dimension::mag * unit::mag;
|
||||
// };
|
||||
|
||||
template<Dimension D, Unit U>
|
||||
struct reference {
|
||||
static constexpr D dimension{};
|
||||
@@ -107,13 +82,6 @@ template<Magnitude M, Reference R>
|
||||
return {};
|
||||
}
|
||||
|
||||
// template<Magnitude M, Reference R>
|
||||
// requires requires { typename R::system_reference; }
|
||||
// [[nodiscard]] consteval reference<typename R::system_reference, decltype(M{} * R::unit)> operator*(M, R)
|
||||
// {
|
||||
// return {};
|
||||
// }
|
||||
|
||||
template<Reference R1, Reference R2>
|
||||
[[nodiscard]] consteval reference<decltype(R1::dimension * R2::dimension), decltype(R1::unit * R2::unit)> operator*(R1,
|
||||
R2)
|
||||
@@ -142,14 +110,9 @@ template<Reference R1, Reference R2>
|
||||
return convertible(R1::dimension, R2::dimension) && convertible(R1::unit, R2::unit);
|
||||
}
|
||||
|
||||
// template<Reference R1, Reference R2>
|
||||
// [[nodiscard]] consteval bool castable(R1, R2)
|
||||
// {
|
||||
// return equivalent(R1::dimension, R2::dimension) && convertible(R1::unit, R2::unit);
|
||||
// }
|
||||
|
||||
|
||||
template<Dimension auto Dim, Unit auto CoU>
|
||||
requires(!detail::associated_unit<std::remove_const_t<decltype(CoU)>>)
|
||||
struct system_reference {
|
||||
static constexpr auto dimension = Dim;
|
||||
static constexpr auto coherent_unit = CoU;
|
||||
|
||||
@@ -148,8 +148,10 @@ struct named_unit;
|
||||
/**
|
||||
* @brief Specialization for unit of a specified base dimension
|
||||
*
|
||||
* This is the preferred way to define a measurement unit for a base dimension. For example `si::metre`
|
||||
* is a unit to measure `isq::length` in the SI system.
|
||||
* Associates a unit with a specified base dimension.
|
||||
* For example `si::metre` is a unit to measure `isq::length` in the SI system.
|
||||
*
|
||||
* @note This is the preferred way to define a measurement unit for a specific base dimension.
|
||||
*
|
||||
* @note It does not have to (or sometimes even can't) be a proper system's base unit. For example
|
||||
* a base unit of mass in the SI is `si::kilogram` but here you are about to provide an `si::gram`
|
||||
@@ -170,9 +172,9 @@ struct named_unit<Symbol, D> {
|
||||
* @brief Specialization for a unit that can be reused by several base dimensions
|
||||
*
|
||||
* This specialization is used in rare cases where more than one base dimension in a specific
|
||||
* system of units uses the same unit. For example in a hypothetical system of units where
|
||||
* constant for speed of light `c = 1`, length and time could be measured in seconds. In such
|
||||
* cases `system_reference` has to be used to explicitly express such a binding.
|
||||
* system of units uses the same unit. For example in a hypothetical system of natural units
|
||||
* where constant for speed of light `c = 1`, length and time could be measured in seconds.
|
||||
* In such cases `system_reference` has to be used to explicitly express such a binding.
|
||||
*
|
||||
* @tparam Symbol a short text representation of the unit
|
||||
*/
|
||||
@@ -337,7 +339,6 @@ struct derived_unit : detail::expr_fractions<derived_unit<>, Us...> {};
|
||||
inline constexpr struct one : derived_unit<> {
|
||||
} one;
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<auto M, typename U>
|
||||
|
||||
Reference in New Issue
Block a user