feat: Added support for natural units-like systems + dimension_one cleanup

This commit is contained in:
Mateusz Pusz
2022-11-04 19:29:05 +01:00
parent 9915ae22b5
commit 4938e9d5c0
12 changed files with 476 additions and 266 deletions
@@ -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_{});
+36 -26
View File
@@ -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
+4 -4
View File
@@ -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)
{
+1 -38
View File
@@ -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;
+7 -6
View File
@@ -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>