feat: quantity_spec support added
This commit is contained in:
1 parent
e0101f14d9
commit
92c70f1a4e
32 files changed
+1524
-827
No files matched your search
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <units/bits/external/hacks.h> // IWYU pragma: keep
|
||||
#include <compare>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <ranges>
|
||||
|
||||
@@ -111,6 +112,26 @@ constexpr auto lexicographical_compare_three_way(I1 f1, I1 l1, I2 f2, I2 l2)
|
||||
return ::units::detail::lexicographical_compare_three_way(f1, l1, f2, l2, std::compare_three_way());
|
||||
}
|
||||
|
||||
template<class ForwardIt>
|
||||
constexpr ForwardIt max_element(ForwardIt first, ForwardIt last)
|
||||
{
|
||||
if (first == last) return last;
|
||||
|
||||
ForwardIt largest = first;
|
||||
++first;
|
||||
|
||||
for (; first != last; ++first)
|
||||
if (*largest < *first) largest = first;
|
||||
|
||||
return largest;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr T max(std::initializer_list<T> ilist)
|
||||
{
|
||||
return *max_element(ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
template<class I, class O>
|
||||
struct in_out_result {
|
||||
[[no_unique_address]] I in;
|
||||
|
||||
@@ -104,6 +104,19 @@ struct power {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename T>
|
||||
struct expr_type_impl : std::type_identity<T> {};
|
||||
|
||||
template<typename T, int... Ints>
|
||||
struct expr_type_impl<power<T, Ints...>> : std::type_identity<T> {};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T>
|
||||
using expr_type = TYPENAME detail::expr_type_impl<T>::type;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_specialization_of_power = false;
|
||||
|
||||
@@ -256,16 +269,7 @@ struct expr_simplify<type_list<power<T, Ints1...>, NRest...>, type_list<power<T,
|
||||
|
||||
// expr_less
|
||||
template<typename Lhs, typename Rhs, template<typename, typename> typename Pred>
|
||||
struct expr_less_impl : Pred<Lhs, Rhs> {};
|
||||
|
||||
template<typename Lhs, int... Ints1, typename Rhs, int... Ints2, template<typename, typename> typename Pred>
|
||||
struct expr_less_impl<power<Lhs, Ints1...>, power<Rhs, Ints2...>, Pred> : Pred<Lhs, Rhs> {};
|
||||
|
||||
template<typename Lhs, int... Ints, typename Rhs, template<typename, typename> typename Pred>
|
||||
struct expr_less_impl<power<Lhs, Ints...>, Rhs, Pred> : Pred<Lhs, Rhs> {};
|
||||
|
||||
template<typename Lhs, typename Rhs, int... Ints, template<typename, typename> typename Pred>
|
||||
struct expr_less_impl<Lhs, power<Rhs, Ints...>, Pred> : Pred<Lhs, Rhs> {};
|
||||
struct expr_less_impl : Pred<expr_type<Lhs>, expr_type<Rhs>> {};
|
||||
|
||||
template<typename T, int... Ints, template<typename, typename> typename Pred>
|
||||
struct expr_less_impl<T, power<T, Ints...>, Pred> : std::true_type {};
|
||||
@@ -504,19 +508,29 @@ 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<typename T>
|
||||
[[nodiscard]] consteval auto map_power(T t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
template<typename T, auto... Ints>
|
||||
[[nodiscard]] consteval auto map_power(power<T, Ints...>)
|
||||
{
|
||||
return pow<Ints...>(T{});
|
||||
}
|
||||
|
||||
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<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>();
|
||||
return (OneType{} * ... * map_power(typename expr_type_map<std::remove_const_t<Nums>, Proj>::type{})) /
|
||||
(OneType{} * ... * map_power(typename expr_type_map<std::remove_const_t<Dens>, Proj>::type{}));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -532,7 +546,10 @@ template<template<typename> typename Proj, template<typename...> typename To, ty
|
||||
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_{});
|
||||
if constexpr (type_list_size<typename T::_num_> + type_list_size<typename T::_den_> == 0)
|
||||
return OneType{};
|
||||
else
|
||||
return expr_map_impl<Proj, To, OneType, Pred>(typename T::_num_{}, typename T::_den_{});
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
+1
-1
@@ -78,6 +78,6 @@ template<typename T, template<typename...> typename Type>
|
||||
concept is_derived_from_specialization_of = requires(T* t) { detail::to_base_specialization_of<Type>(t); };
|
||||
|
||||
template<typename T, typename... Ts>
|
||||
concept one_of = (... || std::same_as<T, Ts>);
|
||||
concept one_of = (false || ... || std::same_as<T, Ts>);
|
||||
|
||||
} // namespace units
|
||||
@@ -22,17 +22,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
// #include <units/bits/basic_concepts.h>
|
||||
// #include <units/bits/quantity_of.h>
|
||||
// IWYU pragma: end_exports
|
||||
#include <units/bits/external/type_traits.h>
|
||||
#include <units/dimension.h>
|
||||
#include <units/quantity_spec.h>
|
||||
#include <units/unit.h>
|
||||
|
||||
namespace units {
|
||||
|
||||
template<Dimension auto D, Unit auto U>
|
||||
template<QuantitySpec auto Q, Unit auto U>
|
||||
struct reference;
|
||||
|
||||
namespace detail {
|
||||
@@ -40,8 +37,8 @@ namespace detail {
|
||||
template<typename T>
|
||||
inline constexpr bool is_specialization_of_reference = false;
|
||||
|
||||
template<Dimension auto D, Unit auto U>
|
||||
inline constexpr bool is_specialization_of_reference<reference<D, U>> = true;
|
||||
template<auto Q, auto U>
|
||||
inline constexpr bool is_specialization_of_reference<reference<Q, U>> = true;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
@@ -92,11 +89,15 @@ class quantity;
|
||||
|
||||
namespace detail {
|
||||
|
||||
// TODO make the below code from the comment to compile and replace it
|
||||
template<auto R, typename Rep>
|
||||
inline constexpr bool is_quantity<quantity<R, Rep>> = true;
|
||||
|
||||
// template<auto R, typename Rep>
|
||||
// void to_base_specialization_of_quantity(const volatile quantity<R, Rep>*);
|
||||
|
||||
// template<typename T>
|
||||
// requires units::is_derived_from_specialization_of<T, units::quantity>
|
||||
// requires requires(T* t) { to_base_specialization_of_quantity(t); }
|
||||
// inline constexpr bool is_quantity<T> = true;
|
||||
|
||||
} // namespace detail
|
||||
@@ -109,6 +110,7 @@ inline constexpr bool is_quantity<quantity<R, Rep>> = true;
|
||||
*/
|
||||
template<typename Q, auto V>
|
||||
concept quantity_of = Quantity<Q> && ((Dimension<std::remove_const_t<decltype(V)>> && Q::dimension == V) ||
|
||||
(QuantitySpec<std::remove_const_t<decltype(V)>> && Q::quantity_spec == V) ||
|
||||
(Reference<std::remove_const_t<decltype(V)>> && Q::reference == V));
|
||||
|
||||
/**
|
||||
@@ -119,8 +121,9 @@ concept quantity_of = Quantity<Q> && ((Dimension<std::remove_const_t<decltype(V)
|
||||
*/
|
||||
template<typename Q, auto V>
|
||||
concept weak_quantity_of = Quantity<Q> &&
|
||||
((Dimension<std::remove_const_t<decltype(V)>> && interconvertible(Q::dimension, V)) ||
|
||||
(Reference<std::remove_const_t<decltype(V)>> &&
|
||||
interconvertible(Q::dimension, V.dimension) && Q::unit == V.unit));
|
||||
((Dimension<std::remove_const_t<decltype(V)>> && Q::dimension == V) ||
|
||||
(QuantitySpec<std::remove_const_t<decltype(V)>> && interconvertible(Q::quantity_spec, V)) ||
|
||||
(Reference<std::remove_const_t<decltype(V)>> && Q::dimension == V.dimension &&
|
||||
Q::unit == V.unit));
|
||||
|
||||
} // namespace units
|
||||
@@ -23,49 +23,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <units/bits/expression_template.h>
|
||||
#include <units/bits/external/fixed_string.h>
|
||||
#include <units/bits/external/type_traits.h>
|
||||
#include <units/unit.h>
|
||||
#include <units/symbol_text.h>
|
||||
|
||||
namespace units {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_derived_dimension = false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A concept matching all derived dimensions in the library.
|
||||
*
|
||||
* Satisfied by all dimension types either being a specialization of `derived_dimension`
|
||||
* or derived from it.
|
||||
*/
|
||||
template<typename T>
|
||||
concept DerivedDimension = detail::is_derived_dimension<T>;
|
||||
|
||||
/**
|
||||
* @brief A concept matching all dimensions in the library.
|
||||
*
|
||||
* Satisfied by all dimension types for which either `BaseDimension<T>` or `DerivedDimension<T>` is `true`.
|
||||
*/
|
||||
template<typename T>
|
||||
concept Dimension = BaseDimension<T> || DerivedDimension<T>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
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 auto D, Unit auto U>
|
||||
struct reference;
|
||||
|
||||
/**
|
||||
* @brief A dimension of a base quantity
|
||||
*
|
||||
@@ -74,17 +36,17 @@ struct reference;
|
||||
* being mutually independent since a base quantity cannot be expressed as a product of powers of the other base
|
||||
* quantities.
|
||||
*
|
||||
* Symbol template parameters is an unique identifier of the base dimension. The same identifiers can be multiplied
|
||||
* and divided which will result with an adjustment of its factor in an exponent of a derived_dimension
|
||||
* `Symbol` template parameter is an unique identifier of the base dimension. The same identifiers can be multiplied
|
||||
* and divided which will result with an adjustment of its factor in an exponent of a `derived_dimension`
|
||||
* (in case of zero the dimension will be simplified and removed from further analysis of current expresion).
|
||||
*
|
||||
* User should derive a strong type from this class template rather than use it directly in the source code.
|
||||
* For example:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* inline constexpr struct length : base_dimension<"L"> {} length;
|
||||
* inline constexpr struct time : base_dimension<"T"> {} time;
|
||||
* inline constexpr struct mass : base_dimension<"M"> {} mass;
|
||||
* inline constexpr struct dim_length : base_dimension<"L"> {} dim_length;
|
||||
* inline constexpr struct dim_time : base_dimension<"T"> {} dim_time;
|
||||
* inline constexpr struct dim_mass : base_dimension<"M"> {} dim_mass;
|
||||
* @endcode
|
||||
*
|
||||
* @note A common convention in this library is to assign the same name for a type and an object of this type.
|
||||
@@ -94,28 +56,35 @@ struct reference;
|
||||
*
|
||||
* @tparam Symbol an unique identifier of the base dimension used to provide dimensional analysis support
|
||||
*/
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
template<basic_fixed_string Symbol>
|
||||
#else
|
||||
template<typename Self, basic_fixed_string Symbol>
|
||||
#endif
|
||||
template<basic_symbol_text Symbol>
|
||||
struct base_dimension {
|
||||
static constexpr auto symbol = Symbol; ///< Unique base dimension identifier
|
||||
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
template<typename Self, valid_unit_for_dimension<Self> U>
|
||||
[[nodiscard]] constexpr auto operator[](this const Self, U)
|
||||
#else
|
||||
template<valid_unit_for_dimension<Self> U>
|
||||
[[nodiscard]] constexpr auto operator[](U) const
|
||||
#endif
|
||||
{
|
||||
return reference<Self{}, U{}>{};
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<basic_symbol_text Symbol>
|
||||
void to_base_base_dimension(const volatile base_dimension<Symbol>*);
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_specialization_of_base_dimension = false;
|
||||
|
||||
template<basic_symbol_text Symbol>
|
||||
inline constexpr bool is_specialization_of_base_dimension<base_dimension<Symbol>> = true;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief A concept matching all named base dimensions in the library.
|
||||
*
|
||||
* Satisfied by all dimension types derived from a specialization of `base_dimension`.
|
||||
*/
|
||||
template<typename T>
|
||||
concept BaseDimension = requires(T* t) { detail::to_base_base_dimension(t); } &&
|
||||
(!detail::is_specialization_of_base_dimension<T>);
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<BaseDimension Lhs, BaseDimension Rhs>
|
||||
struct base_dimension_less : std::bool_constant<(Lhs::symbol < Rhs::symbol)> {};
|
||||
|
||||
@@ -140,22 +109,9 @@ inline constexpr bool is_per_of_dims<per<Ts...>> =
|
||||
} // namespace detail
|
||||
|
||||
template<typename T>
|
||||
concept DerivedDimensionSpec =
|
||||
concept DerivedDimensionExpr =
|
||||
BaseDimension<T> || detail::is_dimension_one<T> || detail::is_power_of_dim<T> || detail::is_per_of_dims<T>;
|
||||
|
||||
template<typename...>
|
||||
struct derived_dimension;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename... Ds>
|
||||
struct derived_dimension_impl : detail::expr_fractions<derived_dimension<>, Ds...> {
|
||||
using _type_ = derived_dimension<Ds...>; // exposition only
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
/**
|
||||
* @brief A dimension of a derived quantity
|
||||
*
|
||||
@@ -172,99 +128,41 @@ struct derived_dimension_impl : detail::expr_fractions<derived_dimension<>, Ds..
|
||||
* For example:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* inline constexpr struct frequency : decltype(1 / time) {} frequency;
|
||||
* inline constexpr struct speed : decltype(length / time) {} speed;
|
||||
* inline constexpr struct acceleration : decltype(speed / time) {} acceleration;
|
||||
* inline constexpr struct force : decltype(mass * acceleration) {} force;
|
||||
* inline constexpr struct energy : decltype(force * length) {} energy;
|
||||
* inline constexpr struct moment_of_force : decltype(length * force) {} moment_of_force;
|
||||
* inline constexpr struct torque : decltype(moment_of_force) {} torque;
|
||||
* using frequency = decltype(1 / dim_time);
|
||||
* using speed = decltype(dim_length / dim_time);
|
||||
* using acceleration = decltype(dim_speed / dim_time);
|
||||
* using force = decltype(dim_mass * dim_acceleration);
|
||||
* using energy = decltype(dim_force * dim_length);
|
||||
* using moment_of_force = decltype(dim_length * dim_force);
|
||||
* using torque = decltype(dim_moment_of_force);
|
||||
* @endcode
|
||||
*
|
||||
* - `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>>>`
|
||||
* - `energy` will be derived from type `derived_dimension<power<length, 2>, mass, per<power<time, 2>>>`
|
||||
* - `frequency` will be derived from type `derived_dimension<dimension_one, per<dim_time>>`
|
||||
* - `speed` will be derived from type `derived_dimension<dim_length, per<dim_time>>`
|
||||
* - `acceleration` will be derived from type `derived_dimension<dim_length, per<power<dim_time, 2>>>`
|
||||
* - `force` will be derived from type `derived_dimension<dim_length, dim_mass, per<power<dim_time, 2>>>`
|
||||
* - `energy` will be derived from type `derived_dimension<power<dim_length, 2>, dim_mass, per<power<dim_time, 2>>>`
|
||||
*
|
||||
* @note A common convention in this library is to assign the same name for a type and an object of this type.
|
||||
* Besides defining them user never works with the dimension types in the source code. All operations
|
||||
* are done on the objects. Contrarily, the dimension types are the only one visible in the compilation
|
||||
* errors. Having them of the same names improves user experience and somehow blurs those separate domains.
|
||||
*
|
||||
* Two dimensions are deemed equal when they are of the same type. With that strong type `speed` and
|
||||
* `derived_dimension<length, per<time>>` are considered not equal. They are convertible though.
|
||||
* User can implicitly convert up and down the inheritance hierarchy between those two.
|
||||
* `torque` and `moment_of_force` are convertible as well. However, `energy` and `torque`
|
||||
* are not convertible as they do not inherit from each other. They are from two separate branches of
|
||||
* dimensionally equivalent quantities.
|
||||
*
|
||||
* @tparam Ds a parameter pack consisting tokens allowed in the dimension specification
|
||||
* (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.
|
||||
*/
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
|
||||
template<DerivedDimensionSpec... Ds>
|
||||
struct derived_dimension : detail::derived_dimension_impl<Ds...> {
|
||||
template<typename Self, Unit U>
|
||||
requires valid_unit_for_dimension<U, Self> || (sizeof...(Ds) == 0 && interconvertible(U{}, one))
|
||||
[[nodiscard]] constexpr auto operator[](this const Self, U)
|
||||
{
|
||||
return reference<Self{}, U{}>{};
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
template<typename...>
|
||||
struct derived_dimension;
|
||||
|
||||
template<DerivedDimensionSpec... Ds>
|
||||
struct derived_dimension<Ds...> : detail::derived_dimension_impl<Ds...> {
|
||||
template<Unit U>
|
||||
requires valid_unit_for_dimension<U, derived_dimension> || (sizeof...(Ds) == 0 && interconvertible(U{}, one))
|
||||
[[nodiscard]] constexpr auto operator[](U) const
|
||||
{
|
||||
return reference<derived_dimension{}, U{}>{};
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Self, DerivedDimension D>
|
||||
struct derived_dimension<Self, D> : D {
|
||||
template<Unit U>
|
||||
requires valid_unit_for_dimension<U, Self> ||
|
||||
(interconvertible(derived_dimension{}, derived_dimension<>{}) && interconvertible(U{}, one))
|
||||
[[nodiscard]] constexpr auto operator[](U) const
|
||||
{
|
||||
return reference<Self{}, U{}>{};
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename... Ds>
|
||||
void to_base_specialization_of_derived_dimension(const volatile derived_dimension<Ds...>*);
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_derived_from_specialization_of_derived_dimension =
|
||||
requires(T * t) { to_base_specialization_of_derived_dimension(t); };
|
||||
|
||||
template<typename T>
|
||||
requires is_derived_from_specialization_of_derived_dimension<T>
|
||||
inline constexpr bool is_derived_dimension<T> = true;
|
||||
|
||||
} // namespace detail
|
||||
template<DerivedDimensionExpr... Ds>
|
||||
struct derived_dimension : detail::expr_fractions<derived_dimension<>, Ds...> {};
|
||||
|
||||
/**
|
||||
* @brief Dimension one
|
||||
*
|
||||
* Dimension for which all the exponents of the factors corresponding to the base
|
||||
* dimensions are zero. Also commonly named as "dimensionless".
|
||||
* dimensions are zero. It is a dimension of a quantity of dimension one also known as
|
||||
* "dimensionless".
|
||||
*/
|
||||
inline constexpr struct dimension_one : derived_dimension<> {
|
||||
} dimension_one;
|
||||
@@ -274,21 +172,32 @@ namespace detail {
|
||||
template<>
|
||||
inline constexpr bool is_dimension_one<struct dimension_one> = true;
|
||||
|
||||
template<Dimension T>
|
||||
struct dim_type_impl {
|
||||
using type = T;
|
||||
};
|
||||
template<typename... Ds>
|
||||
void to_base_specialization_of_derived_dimension(const volatile derived_dimension<Ds...>*);
|
||||
|
||||
template<DerivedDimension T>
|
||||
struct dim_type_impl<T> {
|
||||
using type = TYPENAME T::_type_;
|
||||
};
|
||||
|
||||
template<Dimension T>
|
||||
using dim_type = TYPENAME dim_type_impl<T>::type;
|
||||
template<typename T>
|
||||
inline constexpr bool is_derived_from_specialization_of_derived_dimension =
|
||||
requires(T * t) { to_base_specialization_of_derived_dimension(t); };
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief A concept matching all derived dimensions in the library.
|
||||
*
|
||||
* Satisfied by all dimension types either being a specialization of `derived_dimension`
|
||||
* or derived from it (inheritance needed to properly handle `dimension_one`).
|
||||
*/
|
||||
template<typename T>
|
||||
concept DerivedDimension = detail::is_derived_from_specialization_of_derived_dimension<T>;
|
||||
|
||||
/**
|
||||
* @brief A concept matching all dimensions in the library.
|
||||
*
|
||||
* Satisfied by all dimension types for which either `BaseDimension<T>` or `DerivedDimension<T>` is `true`.
|
||||
*/
|
||||
template<typename T>
|
||||
concept Dimension = BaseDimension<T> || DerivedDimension<T>;
|
||||
|
||||
|
||||
// Operators
|
||||
|
||||
@@ -296,21 +205,21 @@ template<Dimension Lhs, Dimension Rhs>
|
||||
[[nodiscard]] consteval Dimension auto operator*(Lhs, Rhs)
|
||||
{
|
||||
return detail::expr_multiply<derived_dimension, struct dimension_one, detail::type_list_of_base_dimension_less>(
|
||||
detail::dim_type<Lhs>{}, detail::dim_type<Rhs>{});
|
||||
Lhs{}, Rhs{});
|
||||
}
|
||||
|
||||
template<Dimension Lhs, Dimension Rhs>
|
||||
[[nodiscard]] consteval Dimension auto operator/(Lhs, Rhs)
|
||||
{
|
||||
return detail::expr_divide<derived_dimension, struct dimension_one, detail::type_list_of_base_dimension_less>(
|
||||
detail::dim_type<Lhs>{}, detail::dim_type<Rhs>{});
|
||||
return detail::expr_divide<derived_dimension, struct dimension_one, detail::type_list_of_base_dimension_less>(Lhs{},
|
||||
Rhs{});
|
||||
}
|
||||
|
||||
template<Dimension D>
|
||||
[[nodiscard]] consteval Dimension auto operator/(int value, D)
|
||||
{
|
||||
gsl_Expects(value == 1);
|
||||
return detail::expr_invert<derived_dimension, struct dimension_one>(detail::dim_type<D>{});
|
||||
return detail::expr_invert<derived_dimension, struct dimension_one>(D{});
|
||||
}
|
||||
|
||||
template<Dimension D>
|
||||
@@ -322,31 +231,6 @@ template<Dimension Lhs, Dimension Rhs>
|
||||
return is_same_v<Lhs, Rhs>;
|
||||
}
|
||||
|
||||
template<Dimension D1, Dimension D2>
|
||||
[[nodiscard]] consteval bool interconvertible(D1, D2)
|
||||
{
|
||||
return std::derived_from<D1, D2> || std::derived_from<D2, D1>;
|
||||
}
|
||||
|
||||
[[nodiscard]] consteval auto common_dimension(Dimension auto d) { return d; }
|
||||
|
||||
template<Dimension D1, Dimension D2>
|
||||
[[nodiscard]] consteval auto common_dimension(D1 d1, D2 d2)
|
||||
requires(interconvertible(d1, d2))
|
||||
{
|
||||
if constexpr (std::derived_from<D1, D2>)
|
||||
return d1;
|
||||
else
|
||||
return d2;
|
||||
}
|
||||
|
||||
[[nodiscard]] consteval auto common_dimension(Dimension auto d1, Dimension auto d2, Dimension auto d3,
|
||||
Dimension auto... rest)
|
||||
requires requires { common_dimension(common_dimension(d1, d2), d3, rest...); }
|
||||
{
|
||||
return common_dimension(common_dimension(d1, d2), d3, rest...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Computes the value of a dimension raised to the `Num/Den` power
|
||||
*
|
||||
@@ -370,63 +254,6 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Dimension D>
|
||||
detail::type_list_of_base_dimension_less>(d);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<Unit U>
|
||||
[[nodiscard]] consteval Dimension auto get_dimension_for_impl(U)
|
||||
requires requires { U::base_dimension; }
|
||||
{
|
||||
return U::base_dimension;
|
||||
}
|
||||
|
||||
template<Unit U>
|
||||
requires requires { U::base_dimension; }
|
||||
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 dimension_one,
|
||||
detail::type_list_of_base_dimension_less>(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); } && (interconvertible(Dim, detail::get_dimension_for(U)))
|
||||
inline constexpr bool is_valid_unit_for_dimension<U, Dim> = true;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// TODO consider adding the support for text output of the dimensional equation
|
||||
|
||||
} // namespace units
|
||||
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
|
||||
#define BASE_DIMENSION(name, symbol) \
|
||||
inline constexpr struct name : base_dimension<symbol> { \
|
||||
} name
|
||||
|
||||
#define DERIVED_DIMENSION(name, base) \
|
||||
inline constexpr struct name : base { \
|
||||
} name
|
||||
|
||||
#else
|
||||
|
||||
#define BASE_DIMENSION(name, symbol) \
|
||||
inline constexpr struct name : base_dimension<name, symbol> { \
|
||||
} name
|
||||
|
||||
#define DERIVED_DIMENSION(name, base) \
|
||||
inline constexpr struct name : derived_dimension<name, base> { \
|
||||
} name
|
||||
|
||||
#endif
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
namespace units {
|
||||
|
||||
struct dimension_one; // defined in <units/dimension.h>
|
||||
struct dimensionless; // defined in <units/quantity_spec.h>
|
||||
struct one; // defined in <units/unit.h>
|
||||
|
||||
// clang-format off
|
||||
|
||||
+71
-173
@@ -22,10 +22,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <units/bits/dimension_op.h>
|
||||
#include <units/bits/external/hacks.h>
|
||||
#include <units/generic/angle.h>
|
||||
#include <units/generic/dimensionless.h>
|
||||
#include <units/quantity.h>
|
||||
#include <units/unit.h>
|
||||
|
||||
@@ -39,9 +36,9 @@
|
||||
namespace units {
|
||||
|
||||
/**
|
||||
* @brief Computes the value of a quantity raised to the power `N`
|
||||
* @brief Computes the value of a quantity raised to the `Num/Den` power
|
||||
*
|
||||
* Both the quantity value and its dimension are the base of the operation.
|
||||
* Both the quantity value and its quantity specification are the base of the operation.
|
||||
*
|
||||
* @tparam Num Exponent numerator
|
||||
* @tparam Den Exponent denominator
|
||||
@@ -56,11 +53,11 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Quantity Q>
|
||||
using rep = TYPENAME Q::rep;
|
||||
if constexpr (Num == 0) {
|
||||
return rep(1);
|
||||
} else if constexpr (ratio{Num, Den} == 1) {
|
||||
return q;
|
||||
} else {
|
||||
using dim = dimension_pow<typename Q::dimension, Num, Den>;
|
||||
using unit = downcast_unit<dim, pow<ratio{Num, Den}>(Q::unit::mag)>;
|
||||
using std::pow;
|
||||
return quantity<dim, unit, rep>(
|
||||
return quantity<reference<pow<Num, Den>(Q::quantity_spec), pow<Num, Den>(Q::unit)>{}, rep>(
|
||||
static_cast<rep>(pow(q.number(), static_cast<double>(Num) / static_cast<double>(Den))));
|
||||
}
|
||||
}
|
||||
@@ -68,7 +65,7 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Quantity Q>
|
||||
/**
|
||||
* @brief Computes the square root of a quantity
|
||||
*
|
||||
* Both the quantity value and its dimension are the base of the operation.
|
||||
* Both the quantity value and its quantity specification are the base of the operation.
|
||||
*
|
||||
* @param q Quantity being the base of the operation
|
||||
* @return Quantity The result of computation
|
||||
@@ -77,17 +74,16 @@ template<Quantity Q>
|
||||
[[nodiscard]] inline Quantity auto sqrt(const Q& q) noexcept
|
||||
requires requires { sqrt(q.number()); } || requires { std::sqrt(q.number()); }
|
||||
{
|
||||
using dim = dimension_pow<typename Q::dimension, 1, 2>;
|
||||
using unit = downcast_unit<dim, pow<ratio{1, 2}>(Q::unit::mag)>;
|
||||
using rep = TYPENAME Q::rep;
|
||||
using std::sqrt;
|
||||
return quantity<dim, unit, rep>(static_cast<rep>(sqrt(q.number())));
|
||||
return quantity<reference<pow<1, 2>(Q::quantity_spec), pow<1, 2>(Q::unit)>{}, rep>(
|
||||
static_cast<rep>(sqrt(q.number())));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Computes the cubic root of a quantity
|
||||
*
|
||||
* Both the quantity value and its dimension are the base of the operation.
|
||||
* Both the quantity value and its quantity specification are the base of the operation.
|
||||
*
|
||||
* @param q Quantity being the base of the operation
|
||||
* @return Quantity The result of computation
|
||||
@@ -96,11 +92,10 @@ template<Quantity Q>
|
||||
[[nodiscard]] inline Quantity auto cbrt(const Q& q) noexcept
|
||||
requires requires { cbrt(q.number()); } || requires { std::cbrt(q.number()); }
|
||||
{
|
||||
using dim = dimension_pow<typename Q::dimension, 1, 3>;
|
||||
using unit = downcast_unit<dim, pow<ratio{1, 3}>(Q::unit::mag)>;
|
||||
using rep = TYPENAME Q::rep;
|
||||
using std::cbrt;
|
||||
return quantity<dim, unit, rep>(static_cast<rep>(cbrt(q.number())));
|
||||
return quantity<reference<pow<1, 3>(Q::quantity_spec), pow<1, 3>(Q::unit)>{}, rep>(
|
||||
static_cast<rep>(cbrt(q.number())));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,12 +106,12 @@ template<Quantity Q>
|
||||
* @param q Quantity being the base of the operation
|
||||
* @return Quantity The value of the same quantity type
|
||||
*/
|
||||
template<typename U, typename Rep>
|
||||
[[nodiscard]] inline dimensionless<U, Rep> exp(const dimensionless<U, Rep>& q)
|
||||
template<quantity_of<dimensionless> Q, typename Rep>
|
||||
[[nodiscard]] inline Q exp(const Q& q)
|
||||
requires requires { exp(q.number()); } || requires { std::exp(q.number()); }
|
||||
{
|
||||
using std::exp;
|
||||
return quantity_cast<U>(dimensionless<one, Rep>(exp(quantity_cast<one>(q).number())));
|
||||
return quantity_cast<Q::unit>(exp(quantity_cast<one>(q).number()) * dimensionless[one]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,12 +120,12 @@ template<typename U, typename Rep>
|
||||
* @param q Quantity being the base of the operation
|
||||
* @return Quantity The absolute value of a provided quantity
|
||||
*/
|
||||
template<typename D, typename U, typename Rep>
|
||||
[[nodiscard]] inline quantity<D, U, Rep> abs(const quantity<D, U, Rep>& q) noexcept
|
||||
template<Quantity Q>
|
||||
[[nodiscard]] inline Q abs(const Q& q) noexcept
|
||||
requires requires { abs(q.number()); } || requires { std::abs(q.number()); }
|
||||
{
|
||||
using std::abs;
|
||||
return quantity<D, U, Rep>(abs(q.number()));
|
||||
return Q(abs(q.number()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,11 +136,11 @@ template<typename D, typename U, typename Rep>
|
||||
* @tparam Q Quantity type being the base of the operation
|
||||
* @return Quantity The epsilon value for quantity's representation type
|
||||
*/
|
||||
template<Quantity Q>
|
||||
requires requires { std::numeric_limits<typename Q::rep>::epsilon(); }
|
||||
[[nodiscard]] constexpr Quantity auto epsilon() noexcept
|
||||
template<Representation Rep, Reference R>
|
||||
requires requires { std::numeric_limits<Rep>::epsilon(); }
|
||||
[[nodiscard]] constexpr Quantity auto epsilon(R r) noexcept
|
||||
{
|
||||
return Q(std::numeric_limits<typename Q::rep>::epsilon());
|
||||
return std::numeric_limits<Rep>::epsilon() * r;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,14 +149,14 @@ template<Quantity Q>
|
||||
* @tparam q Quantity being the base of the operation
|
||||
* @return Quantity The rounded quantity with unit type To
|
||||
*/
|
||||
template<Unit To, typename D, typename U, typename Rep>
|
||||
[[nodiscard]] constexpr quantity<D, To, Rep> floor(const quantity<D, U, Rep>& q) noexcept
|
||||
template<Unit auto To, auto R, typename Rep>
|
||||
[[nodiscard]] constexpr quantity<reference<R.quantity_spec, To>{}, Rep> floor(const quantity<R, Rep>& q) noexcept
|
||||
requires((!treat_as_floating_point<Rep>) || requires { floor(q.number()); } ||
|
||||
requires { std::floor(q.number()); }) &&
|
||||
(std::same_as<To, U> || requires {
|
||||
::units::quantity_cast<To>(q);
|
||||
quantity<D, To, Rep>::one();
|
||||
})
|
||||
(To == R.unit || requires {
|
||||
::units::quantity_cast<To>(q);
|
||||
quantity<reference<R.quantity_spec, To>{}, Rep>::one();
|
||||
})
|
||||
{
|
||||
const auto handle_signed_results = [&]<typename T>(const T& res) {
|
||||
if (res > q) {
|
||||
@@ -171,46 +166,34 @@ template<Unit To, typename D, typename U, typename Rep>
|
||||
};
|
||||
if constexpr (treat_as_floating_point<Rep>) {
|
||||
using std::floor;
|
||||
if constexpr (std::is_same_v<To, U>) {
|
||||
return quantity<D, To, Rep>(floor(q.number()));
|
||||
if constexpr (To == R.unit) {
|
||||
return quantity<reference<R.quantity_spec, To>{}, Rep>(floor(q.number()));
|
||||
} else {
|
||||
return handle_signed_results(quantity<D, To, Rep>(floor(quantity_cast<To>(q).number())));
|
||||
return handle_signed_results(
|
||||
quantity<reference<R.quantity_spec, To>{}, Rep>(floor(quantity_cast<To>(q).number())));
|
||||
}
|
||||
} else {
|
||||
if constexpr (std::is_same_v<To, U>) {
|
||||
return q;
|
||||
if constexpr (To == R.unit) {
|
||||
return quantity_cast<To>(q);
|
||||
} else {
|
||||
return handle_signed_results(quantity_cast<To>(q));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Overload of @c ::units::floor<Unit>() using the unit type of To
|
||||
*
|
||||
* @tparam q Quantity being the base of the operation
|
||||
* @return Quantity The rounded quantity with unit type of quantity To
|
||||
*/
|
||||
template<Quantity To, std::same_as<typename To::dimension> D, typename U, std::same_as<typename To::rep> Rep>
|
||||
[[nodiscard]] constexpr quantity<D, typename To::unit, Rep> floor(const quantity<D, U, Rep>& q) noexcept
|
||||
requires requires { ::units::floor<typename To::unit>(q); }
|
||||
{
|
||||
return ::units::floor<typename To::unit>(q);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Computes the smallest quantity with integer representation and unit type To with its number not less than q
|
||||
*
|
||||
* @tparam q Quantity being the base of the operation
|
||||
* @return Quantity The rounded quantity with unit type To
|
||||
*/
|
||||
template<Unit To, typename D, typename U, typename Rep>
|
||||
[[nodiscard]] constexpr quantity<D, To, Rep> ceil(const quantity<D, U, Rep>& q) noexcept
|
||||
template<Unit auto To, auto R, typename Rep>
|
||||
[[nodiscard]] constexpr quantity<reference<R.quantity_spec, To>{}, Rep> ceil(const quantity<R, Rep>& q) noexcept
|
||||
requires((!treat_as_floating_point<Rep>) || requires { ceil(q.number()); } || requires { std::ceil(q.number()); }) &&
|
||||
(std::same_as<To, U> || requires {
|
||||
::units::quantity_cast<To>(q);
|
||||
quantity<D, To, Rep>::one();
|
||||
})
|
||||
(To == R.unit || requires {
|
||||
::units::quantity_cast<To>(q);
|
||||
quantity<reference<R.quantity_spec, To>{}, Rep>::one();
|
||||
})
|
||||
{
|
||||
const auto handle_signed_results = [&]<typename T>(const T& res) {
|
||||
if (res < q) {
|
||||
@@ -220,33 +203,21 @@ template<Unit To, typename D, typename U, typename Rep>
|
||||
};
|
||||
if constexpr (treat_as_floating_point<Rep>) {
|
||||
using std::ceil;
|
||||
if constexpr (std::is_same_v<To, U>) {
|
||||
return quantity<D, To, Rep>(ceil(q.number()));
|
||||
if constexpr (To == R.unit) {
|
||||
return quantity<reference<R.quantity_spec, To>{}, Rep>(ceil(q.number()));
|
||||
} else {
|
||||
return handle_signed_results(quantity<D, To, Rep>(ceil(quantity_cast<To>(q).number())));
|
||||
return handle_signed_results(
|
||||
quantity<reference<R.quantity_spec, To>{}, Rep>(ceil(quantity_cast<To>(q).number())));
|
||||
}
|
||||
} else {
|
||||
if constexpr (std::is_same_v<To, U>) {
|
||||
return q;
|
||||
if constexpr (To == R.unit) {
|
||||
return quantity_cast<To>(q);
|
||||
} else {
|
||||
return handle_signed_results(quantity_cast<To>(q));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Overload of @c ::units::ceil<Unit>() using the unit type of To
|
||||
*
|
||||
* @tparam q Quantity being the base of the operation
|
||||
* @return Quantity The rounded quantity with unit type of quantity To
|
||||
*/
|
||||
template<Quantity To, std::same_as<typename To::dimension> D, typename U, std::same_as<typename To::rep> Rep>
|
||||
[[nodiscard]] constexpr quantity<D, typename To::unit, Rep> ceil(const quantity<D, U, Rep>& q) noexcept
|
||||
requires requires { ::units::ceil<typename To::unit>(q); }
|
||||
{
|
||||
return ::units::ceil<typename To::unit>(q);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Computes the nearest quantity with integer representation and unit type To to q
|
||||
*
|
||||
@@ -255,25 +226,25 @@ template<Quantity To, std::same_as<typename To::dimension> D, typename U, std::s
|
||||
* @tparam q Quantity being the base of the operation
|
||||
* @return Quantity The rounded quantity with unit type To
|
||||
*/
|
||||
template<Unit To, typename D, typename U, typename Rep>
|
||||
[[nodiscard]] constexpr quantity<D, To, Rep> round(const quantity<D, U, Rep>& q) noexcept
|
||||
template<Unit auto To, auto R, typename Rep>
|
||||
[[nodiscard]] constexpr quantity<reference<R.quantity_spec, To>{}, Rep> round(const quantity<R, Rep>& q) noexcept
|
||||
requires((!treat_as_floating_point<Rep>) || requires { round(q.number()); } ||
|
||||
requires { std::round(q.number()); }) &&
|
||||
(std::same_as<To, U> || requires {
|
||||
::units::floor<To>(q);
|
||||
quantity<D, To, Rep>::one();
|
||||
})
|
||||
(To == R.unit || requires {
|
||||
::units::floor<To>(q);
|
||||
quantity<reference<R.quantity_spec, To>{}, Rep>::one();
|
||||
})
|
||||
{
|
||||
if constexpr (std::is_same_v<To, U>) {
|
||||
if constexpr (To == R.unit) {
|
||||
if constexpr (treat_as_floating_point<Rep>) {
|
||||
using std::round;
|
||||
return quantity<D, To, Rep>(round(q.number()));
|
||||
return quantity<reference<R.quantity_spec, To>{}, Rep>(round(q.number()));
|
||||
} else {
|
||||
return q;
|
||||
return quantity_cast<To>(q);
|
||||
}
|
||||
} else {
|
||||
const auto res_low = units::floor<To>(q);
|
||||
const auto res_high = res_low + decltype(res_low)::one();
|
||||
const auto res_high = res_low + res_low.one();
|
||||
const auto diff0 = q - res_low;
|
||||
const auto diff1 = res_high - q;
|
||||
if (diff0 == diff1) {
|
||||
@@ -289,35 +260,20 @@ template<Unit To, typename D, typename U, typename Rep>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Overload of @c ::units::round<Unit>() using the unit type of To
|
||||
*
|
||||
* @tparam q Quantity being the base of the operation
|
||||
* @return Quantity The rounded quantity with unit type of quantity To
|
||||
*/
|
||||
template<Quantity To, std::same_as<typename To::dimension> D, typename U, std::same_as<typename To::rep> Rep>
|
||||
[[nodiscard]] constexpr quantity<D, typename To::unit, Rep> round(const quantity<D, U, Rep>& q) noexcept
|
||||
requires requires { ::units::round<typename To::unit>(q); }
|
||||
{
|
||||
return ::units::round<typename To::unit>(q);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Computes the square root of the sum of the squares of x and y,
|
||||
* without undue overflow or underflow at intermediate stages of the computation
|
||||
*/
|
||||
template<Quantity Q1, Quantity Q2>
|
||||
[[nodiscard]] inline std::common_type_t<Q1, Q2> hypot(const Q1& x, const Q2& y) noexcept
|
||||
requires requires { typename std::common_type_t<Q1, Q2>; } &&
|
||||
requires(std::common_type_t<Q1, Q2> q) {
|
||||
requires requires { hypot(q.number(), q.number()); } || requires { std::hypot(q.number(), q.number()); };
|
||||
}
|
||||
[[nodiscard]] inline quantity_of<common_reference(Q1::reference, Q2::reference)> auto hypot(const Q1& x,
|
||||
const Q2& y) noexcept
|
||||
requires requires { common_reference(Q1::reference, Q2::reference); } &&
|
||||
(
|
||||
requires { hypot(x.number(), y.number()); } || requires { std::hypot(x.number(), y.number()); })
|
||||
{
|
||||
using type = std::common_type_t<Q1, Q2>;
|
||||
type xx = x;
|
||||
type yy = y;
|
||||
using std::hypot;
|
||||
return type(hypot(xx.number(), yy.number()));
|
||||
using type = quantity<common_reference(Q1::reference, Q2::reference), decltype(hypot(x.number(), y.number()))>;
|
||||
return type{hypot(x.number(), y.number())};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -325,75 +281,17 @@ template<Quantity Q1, Quantity Q2>
|
||||
* without undue overflow or underflow at intermediate stages of the computation
|
||||
*/
|
||||
template<Quantity Q1, Quantity Q2, Quantity Q3>
|
||||
[[nodiscard]] inline std::common_type_t<Q1, Q2, Q3> hypot(const Q1& x, const Q2& y, const Q3& z) noexcept
|
||||
requires requires { typename std::common_type_t<Q1, Q2, Q3>; } &&
|
||||
requires(std::common_type_t<Q1, Q2, Q3> q) {
|
||||
requires requires { hypot(q.number(), q.number(), q.number()); } ||
|
||||
requires { std::hypot(q.number(), q.number(), q.number()); };
|
||||
}
|
||||
[[nodiscard]] inline quantity_of<common_reference(Q1::reference, Q2::reference, Q3::reference)> auto hypot(
|
||||
const Q1& x, const Q2& y, const Q3& z) noexcept
|
||||
requires requires { common_reference(Q1::reference, Q2::reference, Q3::reference); } &&
|
||||
(
|
||||
requires { hypot(x.number(), y.number(), z.number()); } ||
|
||||
requires { std::hypot(x.number(), y.number(), z.number()); })
|
||||
{
|
||||
using type = std::common_type_t<Q1, Q2, Q3>;
|
||||
type xx = x;
|
||||
type yy = y;
|
||||
type zz = z;
|
||||
using std::hypot;
|
||||
return type(hypot(xx.number(), yy.number(), zz.number()));
|
||||
}
|
||||
|
||||
|
||||
template<typename U, typename Rep>
|
||||
requires treat_as_floating_point<Rep>
|
||||
[[nodiscard]] inline dimensionless<one, Rep> sin(const angle<U, Rep>& q) noexcept
|
||||
requires requires { sin(q.number()); } || requires { std::sin(q.number()); }
|
||||
{
|
||||
using std::sin;
|
||||
return sin(quantity_cast<radian>(q).number());
|
||||
}
|
||||
|
||||
template<typename U, typename Rep>
|
||||
requires treat_as_floating_point<Rep>
|
||||
[[nodiscard]] inline dimensionless<one, Rep> cos(const angle<U, Rep>& q) noexcept
|
||||
requires requires { cos(q.number()); } || requires { std::cos(q.number()); }
|
||||
{
|
||||
using std::cos;
|
||||
return cos(quantity_cast<radian>(q).number());
|
||||
}
|
||||
|
||||
template<typename U, typename Rep>
|
||||
requires treat_as_floating_point<Rep>
|
||||
[[nodiscard]] inline dimensionless<one, Rep> tan(const angle<U, Rep>& q) noexcept
|
||||
requires requires { tan(q.number()); } || requires { std::tan(q.number()); }
|
||||
{
|
||||
using std::tan;
|
||||
return tan(quantity_cast<radian>(q).number());
|
||||
}
|
||||
|
||||
|
||||
template<typename U, typename Rep>
|
||||
requires treat_as_floating_point<Rep>
|
||||
[[nodiscard]] inline angle<radian, Rep> asin(const dimensionless<U, Rep>& q) noexcept
|
||||
requires requires { asin(q.number()); } || requires { std::asin(q.number()); }
|
||||
{
|
||||
using std::asin;
|
||||
return angle<radian, Rep>(asin(quantity_cast<one>(q).number()));
|
||||
}
|
||||
|
||||
template<typename U, typename Rep>
|
||||
requires treat_as_floating_point<Rep>
|
||||
[[nodiscard]] inline angle<radian, Rep> acos(const dimensionless<U, Rep>& q) noexcept
|
||||
requires requires { acos(q.number()); } || requires { std::acos(q.number()); }
|
||||
{
|
||||
using std::acos;
|
||||
return angle<radian, Rep>(acos(quantity_cast<one>(q).number()));
|
||||
}
|
||||
|
||||
template<typename U, typename Rep>
|
||||
requires treat_as_floating_point<Rep>
|
||||
[[nodiscard]] inline angle<radian, Rep> atan(const dimensionless<U, Rep>& q) noexcept
|
||||
requires requires { atan(q.number()); } || requires { std::atan(q.number()); }
|
||||
{
|
||||
using std::atan;
|
||||
return angle<radian, Rep>(atan(quantity_cast<one>(q).number()));
|
||||
using type = quantity<common_reference(Q1::reference, Q2::reference, Q3::reference),
|
||||
decltype(hypot(x.number(), y.number(), z.number()))>;
|
||||
return type{hypot(x.number(), y.number(), z.number())};
|
||||
}
|
||||
|
||||
} // namespace units
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <units/concepts.h>
|
||||
#include <units/customization_points.h>
|
||||
#include <units/dimension.h>
|
||||
#include <units/quantity_spec.h>
|
||||
#include <units/reference.h>
|
||||
#include <units/unit.h>
|
||||
#include <compare>
|
||||
@@ -115,9 +116,10 @@ class quantity {
|
||||
public:
|
||||
// member types and values
|
||||
using rep = Rep;
|
||||
static constexpr auto reference = R;
|
||||
static constexpr auto dimension = R.dimension;
|
||||
static constexpr auto unit = R.unit;
|
||||
static constexpr Reference auto reference = R;
|
||||
static constexpr QuantitySpec auto quantity_spec = R.quantity_spec;
|
||||
static constexpr Dimension auto dimension = R.dimension;
|
||||
static constexpr Unit auto unit = R.unit;
|
||||
|
||||
// static member functions
|
||||
[[nodiscard]] static constexpr quantity zero() noexcept
|
||||
@@ -176,10 +178,10 @@ public:
|
||||
[[nodiscard]] constexpr const rep&& number() const&& noexcept { return std::move(number_); }
|
||||
|
||||
template<Unit U>
|
||||
requires quantity_convertible_to_<quantity, quantity<::units::reference<dimension, U{}>{}, Rep>>
|
||||
[[nodiscard]] constexpr quantity<::units::reference<dimension, U{}>{}, Rep> operator[](U) const
|
||||
requires quantity_convertible_to_<quantity, quantity<::units::reference<quantity_spec, U{}>{}, Rep>>
|
||||
[[nodiscard]] constexpr quantity<::units::reference<quantity_spec, U{}>{}, Rep> operator[](U) const
|
||||
{
|
||||
return quantity<::units::reference<dimension, U{}>{}, Rep>{*this};
|
||||
return quantity<::units::reference<quantity_spec, U{}>{}, Rep>{*this};
|
||||
}
|
||||
|
||||
// member unary operators
|
||||
@@ -424,7 +426,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<dimension_one[::units::one] / reference>(v / q.number());
|
||||
return detail::make_quantity<dimensionless[::units::one] / reference>(v / q.number());
|
||||
}
|
||||
|
||||
template<typename Value>
|
||||
@@ -459,16 +461,11 @@ public:
|
||||
};
|
||||
|
||||
// CTAD
|
||||
#if !UNITS_COMP_CLANG || UNITS_COMP_CLANG > 16
|
||||
template<auto R, typename Rep>
|
||||
explicit(false) quantity(Rep&&) -> quantity<R, Rep>;
|
||||
#endif
|
||||
|
||||
template<auto R, typename Rep>
|
||||
explicit(false) quantity(quantity<R, Rep>) -> quantity<R, Rep>;
|
||||
|
||||
template<Representation Rep>
|
||||
explicit(false) quantity(Rep)->quantity<dimension_one[one], Rep>;
|
||||
explicit(false) quantity(Rep)->quantity<dimensionless[one], Rep>;
|
||||
|
||||
template<QuantityLike Q>
|
||||
explicit quantity(Q) -> quantity<reference<quantity_like_traits<Q>::dimension, quantity_like_traits<Q>::unit>{},
|
||||
|
||||
@@ -52,11 +52,12 @@ class quantity;
|
||||
* @brief Explicit cast of a quantity
|
||||
*
|
||||
* Implicit conversions between quantities of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
|
||||
* (i.e. non-truncating) conversion. In truncating cases an explicit cast have to be used.
|
||||
*
|
||||
* This cast gets the target quantity type to cast to. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_cast<units::isq::si::time<units::isq::si::second>>(1_q_ms);
|
||||
* auto q1 = 1234. * isq::length[mm];
|
||||
* auto q2 = quantity_cast<quantity<isq::height[m], int>>(q1);
|
||||
*
|
||||
* @tparam To a target quantity type to cast to
|
||||
*/
|
||||
@@ -110,17 +111,13 @@ template<Quantity To, auto R, typename Rep>
|
||||
* @brief Explicit cast of a quantity
|
||||
*
|
||||
* Implicit conversions between quantities of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
|
||||
* (i.e. non-truncating) conversion. In truncating cases an explicit cast have to be used.
|
||||
*
|
||||
* This cast gets both the target dimension and unit to cast to. For example:
|
||||
* This cast gets a target reference to cast to. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_cast<units::isq::si::dim_speed, units::isq::si::kilometre_per_hour>(v1);
|
||||
* auto v = quantity_cast<isq::velocity[km / h]>(q);
|
||||
*
|
||||
* @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
|
||||
* @tparam ToR a reference to use for a target quantity
|
||||
*/
|
||||
template<Reference auto ToR, auto R, typename Rep>
|
||||
requires(interconvertible(ToR, R))
|
||||
@@ -134,19 +131,19 @@ template<Reference auto ToR, auto R, typename Rep>
|
||||
* @brief Explicit cast of a quantity
|
||||
*
|
||||
* Implicit conversions between quantities of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
|
||||
* (i.e. non-truncating) conversion. In truncating cases an explicit cast have to be used.
|
||||
*
|
||||
* This cast gets only the target dimension to cast to. For example:
|
||||
* This cast gets only the target quantity specification to cast to. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_cast<units::isq::si::dim_acceleration>(200_q_Gal);
|
||||
* auto v = quantity_cast<isq::velocity>(120 * isq::length[km] / (2 * isq::time[h]));
|
||||
*
|
||||
* @tparam ToD a dimension type to use for a target quantity
|
||||
* @tparam ToQS a quantity specification to use for a target quantity
|
||||
*/
|
||||
template<Dimension auto ToD, auto R, typename Rep>
|
||||
requires(interconvertible(ToD, R.dimension))
|
||||
template<QuantitySpec auto ToQS, auto R, typename Rep>
|
||||
requires(interconvertible(ToQS, R.quantity_spec))
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
|
||||
{
|
||||
constexpr reference<ToD, quantity<R, Rep>::unit> r;
|
||||
constexpr reference<ToQS, quantity<R, Rep>::unit> r;
|
||||
return quantity_cast<quantity<r, Rep>>(q);
|
||||
}
|
||||
|
||||
@@ -154,19 +151,19 @@ template<Dimension auto ToD, auto R, typename Rep>
|
||||
* @brief Explicit cast of a quantity
|
||||
*
|
||||
* Implicit conversions between quantities of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
|
||||
* (i.e. non-truncating) conversion. In truncating cases an explicit cast have to be used.
|
||||
*
|
||||
* This cast gets only the target unit to cast to. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_cast<units::isq::si::second>(1_q_ms);
|
||||
* auto d = quantity_cast<si::second>(1234 * isq::time[ms]);
|
||||
*
|
||||
* @tparam ToU a unit type to use for a target quantity
|
||||
* @tparam ToU a unit to use for a target quantity
|
||||
*/
|
||||
template<Unit auto ToU, auto R, typename Rep>
|
||||
requires(interconvertible(ToU, R.unit))
|
||||
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q)
|
||||
{
|
||||
constexpr reference<quantity<R, Rep>::dimension, ToU> r;
|
||||
constexpr reference<quantity<R, Rep>::quantity_spec, ToU> r;
|
||||
return quantity_cast<quantity<r, Rep>>(q);
|
||||
}
|
||||
|
||||
@@ -174,11 +171,11 @@ template<Unit auto ToU, auto R, typename Rep>
|
||||
* @brief Explicit cast of a quantity
|
||||
*
|
||||
* Implicit conversions between quantities of different types are allowed only for "safe"
|
||||
* (i.e. non-truncating) conversion. In such cases an explicit cast have to be used.
|
||||
* (i.e. non-truncating) conversion. In truncating cases an explicit cast have to be used.
|
||||
*
|
||||
* This cast gets only representation to cast to. For example:
|
||||
*
|
||||
* auto q1 = units::quantity_cast<int>(1_q_ms);
|
||||
* auto q = quantity_cast<int>(1.23 * isq::time[ms]);
|
||||
*
|
||||
* @tparam ToRep a representation type to use for a target quantity
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,518 @@
|
||||
// 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/algorithm.h>
|
||||
#include <units/bits/expression_template.h>
|
||||
#include <units/bits/external/type_name.h>
|
||||
#include <units/bits/external/type_traits.h>
|
||||
#include <units/dimension.h>
|
||||
#include <units/unit.h>
|
||||
#include <tuple>
|
||||
|
||||
namespace units {
|
||||
|
||||
/**
|
||||
* @brief Quantity character
|
||||
*
|
||||
* Scalars, vectors and tensors are mathematical objects that can be used to
|
||||
* denote certain physical quantities and their values. They are as such
|
||||
* independent of the particular choice of a coordinate system, whereas
|
||||
* each scalar component of a vector or a tensor and each component vector and
|
||||
* component tensor depend on that choice.
|
||||
*
|
||||
* A scalar is a physical quantity that has magnitude but no direction.
|
||||
*
|
||||
* Vectors are physical quantities that possess both magnitude and direction
|
||||
* and whose operations obey the axioms of a vector space.
|
||||
*
|
||||
* Tensors can be used to describe more general physical quantities.
|
||||
* For example, the Cauchy stress tensor possess magnitude, direction,
|
||||
* and orientation qualities.
|
||||
*/
|
||||
enum class quantity_character { scalar, vector, tensor };
|
||||
|
||||
namespace detail {
|
||||
|
||||
// TODO revise the note in the below comment
|
||||
/**
|
||||
* @brief Returns the most restrictive character from the list
|
||||
*
|
||||
* @note `vector * vector` returns vector (not tensor)
|
||||
*/
|
||||
template<std::same_as<quantity_character>... Ts>
|
||||
[[nodiscard]] consteval quantity_character common_quantity_character(Ts... args)
|
||||
{
|
||||
return detail::max({args...});
|
||||
}
|
||||
|
||||
template<typename... Qs1, typename... Qs2>
|
||||
[[nodiscard]] consteval quantity_character derived_quantity_character(const type_list<Qs1...>&,
|
||||
const type_list<Qs2...>&)
|
||||
{
|
||||
quantity_character num = common_quantity_character(quantity_character::scalar, expr_type<Qs1>::character...);
|
||||
quantity_character den = common_quantity_character(quantity_character::scalar, expr_type<Qs2>::character...);
|
||||
return common_quantity_character(num, den);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes quantity character
|
||||
*
|
||||
* If a quantity character value is present in template parameters, this value will be used.
|
||||
* Otherwise, an inherited/derived value provided through the function argument is returned.
|
||||
*/
|
||||
template<auto... Args>
|
||||
[[nodiscard]] consteval quantity_character quantity_character_init(quantity_character ch)
|
||||
{
|
||||
if constexpr (one_of<quantity_character, std::remove_const_t<decltype(Args)>...>)
|
||||
return std::get<quantity_character>(std::make_tuple(Args...));
|
||||
else
|
||||
return ch;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_specialization_of_derived_quantity_spec = false;
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_dimensionless = false;
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_power_of_quantity_spec = requires {
|
||||
requires is_specialization_of_power<T> &&
|
||||
(NamedQuantitySpec<typename T::factor> || is_dimensionless<typename T::factor>);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_per_of_quantity_specs = false;
|
||||
|
||||
template<typename... Ts>
|
||||
inline constexpr bool is_per_of_quantity_specs<per<Ts...>> =
|
||||
(... && (NamedQuantitySpec<Ts> || is_dimensionless<Ts> || is_power_of_quantity_spec<Ts>));
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief Concept matching quantity specification types
|
||||
*
|
||||
* Satisfied by all `derived_quantity_spec` specializations.
|
||||
*/
|
||||
template<typename T>
|
||||
concept DerivedQuantitySpec = detail::is_specialization_of_derived_quantity_spec<T>;
|
||||
|
||||
template<typename T>
|
||||
concept QuantitySpec = NamedQuantitySpec<T> || DerivedQuantitySpec<T>;
|
||||
|
||||
template<typename T>
|
||||
concept DerivedQuantitySpecExpr = NamedQuantitySpec<T> || detail::is_dimensionless<T> ||
|
||||
detail::is_power_of_quantity_spec<T> || detail::is_per_of_quantity_specs<T>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<NamedQuantitySpec Lhs, NamedQuantitySpec Rhs>
|
||||
struct quantity_spec_less : std::bool_constant<type_name<Lhs>() < type_name<Rhs>()> {};
|
||||
|
||||
template<typename T1, typename T2>
|
||||
using type_list_of_quantity_spec_less = expr_less<T1, T2, quantity_spec_less>;
|
||||
|
||||
template<NamedQuantitySpec Q>
|
||||
requires requires { Q::dimension; }
|
||||
using to_dimension = std::remove_const_t<decltype(Q::dimension)>;
|
||||
|
||||
template<Unit U>
|
||||
requires requires { U::base_quantity.dimension; }
|
||||
using to_base_dimension = std::remove_const_t<decltype(U::base_quantity.dimension)>;
|
||||
|
||||
template<Unit U>
|
||||
[[nodiscard]] consteval Dimension auto get_dimension_for_impl(U)
|
||||
requires requires { U::base_quantity.dimension; }
|
||||
{
|
||||
return U::base_quantity.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 dimension_one,
|
||||
detail::type_list_of_base_dimension_less>(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);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<QuantitySpec auto Q, Unit auto U>
|
||||
struct reference;
|
||||
|
||||
/**
|
||||
* @brief A specification of a derived quantity
|
||||
*
|
||||
* Derived quantity is a quantity, in a system of quantities, defined in terms of other quantities of that system.
|
||||
* Its dimension is an expression of the dependence of a quantity on the base quantities of a system of
|
||||
* quantities as a product of powers of factors corresponding to the base quantities, omitting any numerical factors.
|
||||
*
|
||||
* Instead of using a raw list of exponents this library decided to use expression template syntax to make types
|
||||
* more digestable for the user both for quantity specification and its dimension. 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. In case all of the exponents are negative than the
|
||||
* `dimensionless`/`dimension_one` is put in the front to increase the readability.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* auto frequency = 1 / period_duration;
|
||||
* auto area = pow<2>(length);
|
||||
* auto speed = distance / duration;
|
||||
* auto velocity = position_vector / duration;
|
||||
* auto acceleration = velocity / duration;
|
||||
* @endcode
|
||||
*
|
||||
* - the type of `frequency` is `derived_quantity_spec<dimensionless, per<period_duration>>`
|
||||
* - the dimension type of `frequency` is `derived_dimension<dimension_one, per<dim_time>>`
|
||||
* - the type of `area` is `derived_quantity_spec<power<length, 2>>`
|
||||
* - the dimension type of `area` is `derived_dimension<power<dim_length, 2>>`
|
||||
* - the type of `speed` is `derived_quantity_spec<distance, per<duration>>`
|
||||
* - the dimension type of `speed` is `derived_dimension<dim_length, per<dim_time>>`
|
||||
* - the type of `velocity` is `derived_quantity_spec<position_vector, per<duration>>`
|
||||
* - the dimension type of `velocity` is `derived_dimension<dim_length, per<dim_time>>`
|
||||
* - the type of `acceleration` is `derived_quantity_spec<velocity, per<duration>>`
|
||||
* - the dimension type of `acceleration` is `derived_dimension<dim_length, per<power<dim_time, 2>>>`
|
||||
*
|
||||
* @note A common convention in this library is to assign the same name for a type and an object of this type.
|
||||
* Besides defining them user never works with the dimension types in the source code. All operations
|
||||
* are done on the objects. Contrarily, the dimension types are the only one visible in the compilation
|
||||
* errors. Having them of the same names improves user experience and somehow blurs those separate domains.
|
||||
*
|
||||
* Derived quantity specification will have a character being the strongest ones from its ingredients.
|
||||
*
|
||||
* Binding a proper unit to a quantity specification via an indexing operator (`operator[]`) results
|
||||
* in a quantity reference.
|
||||
*
|
||||
* @tparam Qs a parameter pack consisting tokens allowed in the quantity specification
|
||||
* (named quantity specification, `dimensionless`, `power<Q, 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.
|
||||
*/
|
||||
template<DerivedQuantitySpecExpr... Qs>
|
||||
struct derived_quantity_spec : detail::expr_fractions<derived_quantity_spec<>, Qs...> {
|
||||
using base = detail::expr_fractions<derived_quantity_spec<>, Qs...>;
|
||||
|
||||
static constexpr Dimension auto dimension =
|
||||
detail::expr_map<detail::to_dimension, derived_dimension, struct dimension_one,
|
||||
detail::type_list_of_base_dimension_less>(base{});
|
||||
static constexpr quantity_character character =
|
||||
detail::derived_quantity_character(typename base::_num_{}, typename base::_num_{});
|
||||
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
template<typename Self, Unit U>
|
||||
[[nodiscard]] constexpr auto operator[](this const Self, U u)
|
||||
requires(dimension == detail::get_dimension_for(u))
|
||||
{
|
||||
return reference<Self{}, u>{};
|
||||
}
|
||||
#else
|
||||
template<Unit U>
|
||||
[[nodiscard]] constexpr auto operator[](U u) const
|
||||
requires(dimension == detail::get_dimension_for(u))
|
||||
{
|
||||
return reference<derived_quantity_spec{}, u>{};
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename... Args>
|
||||
inline constexpr bool is_specialization_of_derived_quantity_spec<derived_quantity_spec<Args...>> = true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Quantity Specification
|
||||
*
|
||||
* This type specifies all the properties of a quantity and allow modeling most of the quantities in the ISO 80000.
|
||||
* It serves to define base and derived quantities as well as quantity kinds. Each quantity specification
|
||||
* provides an information on how this quantity relates to other quantities, specifies its dimension and character.
|
||||
*
|
||||
* Quantity character can be derived from other quantities or explicitly overriden through a template parameter.
|
||||
*
|
||||
* Binding a proper unit to a quantity specification via an indexing operator (`operator[]`) results
|
||||
* in a quantity reference.
|
||||
*/
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
template<auto...>
|
||||
#else
|
||||
template<typename, auto...>
|
||||
#endif
|
||||
struct quantity_spec;
|
||||
|
||||
/**
|
||||
* @brief Specialization defining a base quantity
|
||||
*
|
||||
* Base quantity is a quantity in a conventionally chosen subset of a given system of quantities, where no quantity
|
||||
* in the subset can be expressed in terms of the other quantities within that subset. They are referred to as
|
||||
* being mutually independent since a base quantity cannot be expressed as a product of powers of the other base
|
||||
* quantities.
|
||||
*
|
||||
* Base quantities have scalar character by default.
|
||||
*
|
||||
* User should derive a strong type from this class template rather than use it directly in the source code.
|
||||
* For example:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* inline constexpr struct dim_length : base_dimension<"L"> {} dim_length;
|
||||
* inline constexpr struct dim_mass : base_dimension<"M"> {} dim_mass;
|
||||
* inline constexpr struct dim_time : base_dimension<"T"> {} dim_time;
|
||||
*
|
||||
* inline constexpr struct length : quantity_spec<dim_length> {} length;
|
||||
* inline constexpr struct mass : quantity_spec<dim_mass> {} mass;
|
||||
* inline constexpr struct time : quantity_spec<dim_time> {} time;
|
||||
* @endcode
|
||||
*
|
||||
* @note A common convention in this library is to assign the same name for a type and an object of this type.
|
||||
* Besides defining them user never works with the dimension types in the source code. All operations
|
||||
* are done on the objects. Contrarily, the dimension types are the only one visible in the compilation
|
||||
* errors. Having them of the same names improves user experience and somehow blurs those separate domains.
|
||||
*
|
||||
* @tparam BaseDimension base dimension for which a base quantity is being defined
|
||||
* @tparam Args optionally a value of a `quantity_character` in case the base quantity should not be scalar
|
||||
*/
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
template<BaseDimension auto Dim, one_of<quantity_character> auto... Args>
|
||||
struct quantity_spec<Dim, Args...> {
|
||||
#else
|
||||
template<typename Self, BaseDimension auto Dim, one_of<quantity_character> auto... Args>
|
||||
struct quantity_spec<Self, Dim, Args...> {
|
||||
#endif
|
||||
static constexpr BaseDimension auto dimension = Dim;
|
||||
static constexpr quantity_character character = detail::quantity_character_init<Args...>(quantity_character::scalar);
|
||||
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
template<typename Self, Unit U>
|
||||
[[nodiscard]] constexpr auto operator[](this const Self, U u)
|
||||
requires(dimension == detail::get_dimension_for(u))
|
||||
#else
|
||||
template<Unit U>
|
||||
[[nodiscard]] constexpr auto operator[](U u) const
|
||||
requires(dimension == detail::get_dimension_for(u))
|
||||
#endif
|
||||
{
|
||||
return reference<Self{}, u>{};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Specialization defining a named derived quantity or a quantity kind
|
||||
*
|
||||
* The division of the concept "quantity" into several kinds is to some extent arbitrary.
|
||||
* For example the quantities diameter, circumference, and wavelength are generally considered
|
||||
* to be quantities of the same kind, namely, of the kind of quantity called length.
|
||||
*
|
||||
* Quantities of the same kind within a given system of quantities have the same quantity dimension.
|
||||
* However, quantities of the same dimension are not necessarily of the same kind.
|
||||
*
|
||||
* The character of those quantities is derived from ingredients or overriden with a template parameter.
|
||||
*
|
||||
* User should derive a strong type from this class template rather than use it directly in the source code.
|
||||
* For example:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* // quantity kinds
|
||||
* inline constexpr struct width : quantity_spec<length> {} width;
|
||||
* inline constexpr struct height : quantity_spec<length> {} height;
|
||||
* inline constexpr struct thickness : quantity_spec<width> {} thickness;
|
||||
* inline constexpr struct diameter : quantity_spec<width> {} diameter;
|
||||
* inline constexpr struct position_vector : quantity_spec<length, quantity_character::vector> {} position_vector;
|
||||
*
|
||||
* // derived quantities
|
||||
* inline constexpr struct area : quantity_spec<pow<2>(length)> {} area;
|
||||
* inline constexpr struct volume : quantity_spec<pow<3>(length)> {} volume;
|
||||
* inline constexpr struct velocity : quantity_spec<position_vector / duration> {} velocity; // vector
|
||||
* inline constexpr struct speed : quantity_spec<distance / duration> {} speed;
|
||||
* inline constexpr struct force : quantity_spec<mass * acceleration, quantity_character::vector> {} force;
|
||||
* inline constexpr struct power : quantity_spec<force * velocity, quantity_character::scalar> {} power;
|
||||
* @endcode
|
||||
*
|
||||
* Two quantity specifications are deemed equal when they are of the same type. With that, both strong
|
||||
* types `speed` and `velocity` are considered not equal to `derived_dimension<length, per<time>>` or
|
||||
* to each other. However, they are both convertible to `derived_dimension<length, per<time>>`.
|
||||
* User can implicitly convert up and down the inheritance hierarchy between them. However, `speed` and
|
||||
* `velocity` are not convertible as they do not inherit from each other. They are from two separate branches
|
||||
* of dimensionally equivalent quantities.
|
||||
*
|
||||
* @tparam Q quantity specification of a parent quantity
|
||||
* @tparam Args optionally a value of a `quantity_character` in case the base quantity should not be scalar
|
||||
*/
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
template<QuantitySpec auto Q, one_of<quantity_character> auto... Args>
|
||||
struct quantity_spec<Q, Args...> : std::remove_const_t<decltype(Q)> {
|
||||
#else
|
||||
template<typename Self, QuantitySpec auto Q, one_of<quantity_character> auto... Args>
|
||||
struct quantity_spec<Self, Q, Args...> : std::remove_const_t<decltype(Q)> {
|
||||
#endif
|
||||
static constexpr auto kind_of = Q;
|
||||
static constexpr quantity_character character = detail::quantity_character_init<Args...>(Q.character);
|
||||
|
||||
#ifndef __cpp_explicit_this_parameter
|
||||
template<Unit U>
|
||||
[[nodiscard]] constexpr auto operator[](U u) const
|
||||
requires(this->dimension == detail::get_dimension_for(u))
|
||||
{
|
||||
return reference<Self{}, u>{};
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
|
||||
#define QUANTITY_SPEC(name, ...) \
|
||||
inline constexpr struct name : quantity_spec<##__VA_ARGS__> { \
|
||||
} name
|
||||
|
||||
#else
|
||||
|
||||
#define QUANTITY_SPEC(name, ...) \
|
||||
inline constexpr struct name : quantity_spec<name, ##__VA_ARGS__> { \
|
||||
} name
|
||||
|
||||
#endif
|
||||
|
||||
// TODO Should a quantity_cast be allowed to cast between speed and velocity?
|
||||
|
||||
|
||||
/**
|
||||
* @brief Quantity of dimension one
|
||||
*
|
||||
* Quantity of dimension one also commonly named as "dimensionless" is a quantity with a dimension
|
||||
* for which all the exponents of the factors corresponding to the base dimensions are zero.
|
||||
*/
|
||||
QUANTITY_SPEC(dimensionless, derived_quantity_spec<>{});
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<>
|
||||
inline constexpr bool is_dimensionless<struct dimensionless> = true;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
// Operators
|
||||
|
||||
template<QuantitySpec Lhs, QuantitySpec Rhs>
|
||||
[[nodiscard]] consteval QuantitySpec auto operator*(Lhs lhs, Rhs rhs)
|
||||
{
|
||||
return detail::expr_multiply<derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(
|
||||
lhs, rhs);
|
||||
}
|
||||
|
||||
template<QuantitySpec Lhs, QuantitySpec Rhs>
|
||||
[[nodiscard]] consteval QuantitySpec auto operator/(Lhs lhs, Rhs rhs)
|
||||
{
|
||||
return detail::expr_divide<derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(lhs,
|
||||
rhs);
|
||||
}
|
||||
|
||||
template<QuantitySpec Q>
|
||||
[[nodiscard]] consteval QuantitySpec auto operator/(int value, Q q)
|
||||
{
|
||||
gsl_Expects(value == 1);
|
||||
return detail::expr_invert<derived_quantity_spec, struct dimensionless>(q);
|
||||
}
|
||||
|
||||
template<QuantitySpec Q>
|
||||
[[nodiscard]] consteval QuantitySpec auto operator/(Q, int) = delete;
|
||||
|
||||
template<QuantitySpec Lhs, QuantitySpec Rhs>
|
||||
[[nodiscard]] consteval bool operator==(Lhs, Rhs)
|
||||
{
|
||||
return is_same_v<Lhs, Rhs>;
|
||||
}
|
||||
|
||||
template<QuantitySpec Q1, QuantitySpec Q2>
|
||||
[[nodiscard]] consteval bool interconvertible(Q1, Q2)
|
||||
{
|
||||
if constexpr (NamedQuantitySpec<Q1> && NamedQuantitySpec<Q2>)
|
||||
// check if quantity kinds are convertible (across one hierarchy)
|
||||
return std::derived_from<Q1, Q2> || std::derived_from<Q2, Q1>;
|
||||
else {
|
||||
// check weather the quantity spec's dimension is the same
|
||||
// TODO Can we improve that to account for quantity kinds (i.e. `altitude` / `time` -> `sink_rate`)
|
||||
return Q1::dimension == Q2::dimension;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] consteval auto common_quantity_spec(QuantitySpec auto q) { return q; }
|
||||
|
||||
template<QuantitySpec Q1, QuantitySpec Q2>
|
||||
[[nodiscard]] consteval auto common_quantity_spec(Q1 q1, Q2 q2)
|
||||
requires(interconvertible(q1, q2))
|
||||
{
|
||||
if constexpr (std::derived_from<Q1, Q2>)
|
||||
return q1;
|
||||
else if constexpr (std::derived_from<Q2, Q1>)
|
||||
return q2;
|
||||
else if constexpr (NamedQuantitySpec<Q1>)
|
||||
return q1;
|
||||
else
|
||||
return q2;
|
||||
}
|
||||
|
||||
[[nodiscard]] consteval auto common_quantity_spec(QuantitySpec auto q1, QuantitySpec auto q2, QuantitySpec auto q3,
|
||||
QuantitySpec auto... rest)
|
||||
requires requires { common_quantity_spec(common_quantity_spec(q1, q2), q3, rest...); }
|
||||
{
|
||||
return common_quantity_spec(common_quantity_spec(q1, q2), q3, rest...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Computes the value of a quantity specification raised to the `Num/Den` power
|
||||
*
|
||||
* @tparam Num Exponent numerator
|
||||
* @tparam Den Exponent denominator
|
||||
* @param q Quantity specification being the base of the operation
|
||||
*
|
||||
* @return QuantitySpec The result of computation
|
||||
*/
|
||||
template<std::intmax_t Num, std::intmax_t Den = 1, QuantitySpec Q>
|
||||
requires detail::non_zero<Den>
|
||||
[[nodiscard]] consteval QuantitySpec auto pow(Q q)
|
||||
{
|
||||
if constexpr (BaseQuantitySpec<Q>) {
|
||||
if constexpr (Den == 1)
|
||||
return derived_quantity_spec<power<Q, Num>>{};
|
||||
else
|
||||
return derived_quantity_spec<power<Q, Num, Den>>{};
|
||||
} else
|
||||
return detail::expr_pow<Num, Den, derived_quantity_spec, struct dimensionless,
|
||||
detail::type_list_of_quantity_spec_less>(q);
|
||||
}
|
||||
|
||||
} // namespace units
|
||||
@@ -23,72 +23,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <units/concepts.h>
|
||||
#include <units/dimension.h>
|
||||
#include <units/quantity_spec.h>
|
||||
#include <units/unit.h>
|
||||
|
||||
namespace units {
|
||||
|
||||
/**
|
||||
* @brief The type for quantity references
|
||||
* @brief Quantity reference type
|
||||
*
|
||||
* This type is intended to be used in the quantity references definition:
|
||||
* Quantity reference describes all the properties of a quantity besides its
|
||||
* representation type.
|
||||
*
|
||||
* In most cases this class template is not explicitly instantiated by the user.
|
||||
* It is implicitly instantiated by the library's framework while binding a quantity
|
||||
* specification with a compatible unit.
|
||||
*
|
||||
* @code{.cpp}
|
||||
* namespace length_references {
|
||||
*
|
||||
* inline constexpr auto m = reference<dim_length, metre>{};
|
||||
* inline constexpr auto km = reference<dim_length, kilometre>{};
|
||||
*
|
||||
* }
|
||||
*
|
||||
* namespace references {
|
||||
*
|
||||
* using namespace length_references;
|
||||
*
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* Quantity references simplify quantity creation:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* using namespace units::isq::si::references;
|
||||
*
|
||||
* auto d = 123 * m;
|
||||
* auto v = 70 * (km / h);
|
||||
* @endcode
|
||||
*
|
||||
* Also, it is allowed to define custom quantity references from existing ones:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* constexpr auto Nm = N * m;
|
||||
* constexpr auto mph = mi / h;
|
||||
* Reference auto kmph = isq::speed[km / h];
|
||||
* quantity_of<isq::speed[km / h]> auto speed = 90 * kmph;
|
||||
* @endcode
|
||||
*
|
||||
* The following syntaxes are not allowed:
|
||||
* `2 / s`, `km * 3`, `s / 4`, `70 * km / h`.
|
||||
* `2 / kmph`, `kmph * 3`, `kmph / 4`, `70 * isq::length[km] / isq:time[h]`.
|
||||
*/
|
||||
template<Dimension auto D, Unit auto U>
|
||||
template<QuantitySpec auto Q, Unit auto U>
|
||||
struct reference {
|
||||
static constexpr auto dimension = D;
|
||||
static constexpr auto unit = U;
|
||||
static constexpr QuantitySpec auto quantity_spec = Q;
|
||||
static constexpr Dimension auto dimension = Q.dimension;
|
||||
static constexpr Unit auto unit = U;
|
||||
};
|
||||
|
||||
// Reference
|
||||
|
||||
template<Magnitude M, Reference R>
|
||||
[[nodiscard]] consteval reference<R::dimension, M{} * R::unit> operator*(M, R)
|
||||
[[nodiscard]] consteval reference<R::quantity_spec, M{} * R::unit> operator*(M, R)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reference R1, Reference R2>
|
||||
[[nodiscard]] consteval reference<R1::dimension * R2::dimension, R1::unit * R2::unit> operator*(R1, R2)
|
||||
[[nodiscard]] consteval reference<R1::quantity_spec * R2::quantity_spec, R1::unit * R2::unit> operator*(R1, R2)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
template<Reference R1, Reference R2>
|
||||
[[nodiscard]] consteval reference<R1::dimension / R2::dimension, R1::unit / R2::unit> operator/(R1, R2)
|
||||
[[nodiscard]] consteval reference<R1::quantity_spec / R2::quantity_spec, R1::unit / R2::unit> operator/(R1, R2)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
@@ -104,26 +84,26 @@ void /*Use `q * (1 * r)` rather than `q * r`.*/ operator*(Quantity auto, Referen
|
||||
template<Reference R1, Reference R2>
|
||||
[[nodiscard]] consteval bool operator==(R1, R2)
|
||||
{
|
||||
return R1::dimension == R2::dimension && R1::unit == R2::unit;
|
||||
return R1::quantity_spec == R2::quantity_spec && R1::unit == R2::unit;
|
||||
}
|
||||
|
||||
template<Reference R1, Reference R2>
|
||||
[[nodiscard]] consteval bool interconvertible(R1, R2)
|
||||
{
|
||||
return interconvertible(R1::dimension, R2::dimension) && interconvertible(R1::unit, R2::unit);
|
||||
return interconvertible(R1::quantity_spec, R2::quantity_spec) && interconvertible(R1::unit, R2::unit);
|
||||
}
|
||||
|
||||
[[nodiscard]] consteval auto common_reference(Reference auto r1, Reference auto r2, Reference auto... rest)
|
||||
requires requires {
|
||||
{
|
||||
common_dimension(r1.dimension, r2.dimension, rest.dimension...)
|
||||
} -> Dimension;
|
||||
common_quantity_spec(r1.quantity_spec, r2.quantity_spec, rest.quantity_spec...)
|
||||
} -> QuantitySpec;
|
||||
{
|
||||
common_unit(r1.unit, r2.unit, rest.unit...)
|
||||
} -> Unit;
|
||||
}
|
||||
{
|
||||
return reference<common_dimension(r1.dimension, r2.dimension, rest.dimension...),
|
||||
return reference<common_quantity_spec(r1.quantity_spec, r2.quantity_spec, rest.quantity_spec...),
|
||||
common_unit(r1.unit, r2.unit, rest.unit...)>{};
|
||||
}
|
||||
|
||||
|
||||
@@ -23,20 +23,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <units/concepts.h>
|
||||
#include <units/quantity_spec.h>
|
||||
#include <units/reference.h>
|
||||
#include <units/unit.h>
|
||||
|
||||
namespace units {
|
||||
|
||||
template<Dimension auto Dim, Unit auto CoU>
|
||||
requires(!detail::associated_unit<std::remove_const_t<decltype(CoU)>>)
|
||||
/**
|
||||
* @brief System-specific reference
|
||||
*
|
||||
* This type is used in rare cases where more than one base quantity in a specific
|
||||
* 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.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* // hypothetical natural system of units for c=1
|
||||
*
|
||||
* inline constexpr struct second : named_unit<"s"> {} second;
|
||||
* inline constexpr struct minute : named_unit<"min", mag<60> * second> {} minute;
|
||||
* inline constexpr struct gram : named_unit<"g"> {} gram;
|
||||
* inline constexpr struct kilogram : decltype(si::kilo<gram>) {} kilogram;
|
||||
*
|
||||
* inline constexpr struct time : system_reference<isq::time, second> {} time;
|
||||
* inline constexpr struct length : system_reference<isq::length, second> {} length;
|
||||
* inline constexpr struct speed : system_reference<isq::speed, second / second> {} speed;
|
||||
* inline constexpr struct force : system_reference<isq::force, kilogram / second> {} force;
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Q quantity for which a unit is being assigned
|
||||
* @tparam CoU coherent unit for a quantity in this system
|
||||
*/
|
||||
template<QuantitySpec auto Q, Unit auto CoU>
|
||||
requires(!detail::associated_unit<std::remove_const_t<decltype(CoU)>>) || (CoU == one)
|
||||
struct system_reference {
|
||||
static constexpr auto dimension = Dim;
|
||||
static constexpr auto quantity_spec = Q;
|
||||
static constexpr auto coherent_unit = CoU;
|
||||
|
||||
template<Unit U>
|
||||
requires(interconvertible(coherent_unit, U{}))
|
||||
[[nodiscard]] constexpr reference<dimension, U{}> operator[](U) const
|
||||
[[nodiscard]] constexpr reference<quantity_spec, U{}> operator[](U) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <units/bits/external/text_tools.h>
|
||||
#include <units/bits/external/type_name.h>
|
||||
#include <units/bits/external/type_traits.h>
|
||||
#include <units/dimension.h>
|
||||
#include <units/magnitude.h>
|
||||
#include <units/ratio.h>
|
||||
#include <units/symbol_text.h>
|
||||
@@ -37,43 +38,57 @@
|
||||
namespace units {
|
||||
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
template<basic_fixed_string Symbol>
|
||||
template<auto...>
|
||||
#else
|
||||
template<typename Self, basic_fixed_string Symbol>
|
||||
template<typename, auto...>
|
||||
#endif
|
||||
struct base_dimension;
|
||||
struct quantity_spec;
|
||||
|
||||
namespace detail {
|
||||
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
template<basic_fixed_string Symbol>
|
||||
void to_base_base_dimension(const volatile base_dimension<Symbol>*);
|
||||
template<auto... Args>
|
||||
void to_base_specialization_of_quantity_spec(const volatile quantity_spec<Args...>*);
|
||||
#else
|
||||
template<typename Self, basic_fixed_string Symbol>
|
||||
void to_base_base_dimension(const volatile base_dimension<Self, Symbol>*);
|
||||
template<typename T, auto... Args>
|
||||
void to_base_specialization_of_quantity_spec(const volatile quantity_spec<T, Args...>*);
|
||||
#endif
|
||||
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
template<BaseDimension auto Dim, auto... Args>
|
||||
template<auto... Args>
|
||||
void to_base_specialization_of_base_quantity_spec(const volatile quantity_spec<Dim, Args...>*);
|
||||
#else
|
||||
template<typename Self, BaseDimension auto Dim, auto... Args>
|
||||
void to_base_specialization_of_base_quantity_spec(const volatile quantity_spec<Self, Dim, Args...>*);
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_specialization_of_base_dimension = false;
|
||||
inline constexpr bool is_specialization_of_quantity_spec = false;
|
||||
|
||||
#ifdef __cpp_explicit_this_parameter
|
||||
template<basic_fixed_string Symbol>
|
||||
inline constexpr bool is_specialization_of_base_dimension<base_dimension<Symbol>> = true;
|
||||
template<auto... Args>
|
||||
inline constexpr bool is_specialization_of_quantity_spec<quantity_spec<Args...>> = true;
|
||||
#else
|
||||
template<typename Self, basic_fixed_string Symbol>
|
||||
inline constexpr bool is_specialization_of_base_dimension<base_dimension<Self, Symbol>> = true;
|
||||
template<typename T, auto... Args>
|
||||
inline constexpr bool is_specialization_of_quantity_spec<quantity_spec<T, Args...>> = true;
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief A concept matching all named base dimensions in the library.
|
||||
* @brief Concept matching quantity specification types
|
||||
*
|
||||
* Satisfied by all dimension types derived from a specialization of `base_dimension`.
|
||||
* Satisfied by all types that derive from `quantity_spec`.
|
||||
*/
|
||||
template<typename T>
|
||||
concept BaseDimension = requires(T* t) { detail::to_base_base_dimension(t); } &&
|
||||
(!detail::is_specialization_of_base_dimension<T>);
|
||||
concept NamedQuantitySpec = requires(T* t) { detail::to_base_specialization_of_quantity_spec(t); } &&
|
||||
(!detail::is_specialization_of_quantity_spec<T>);
|
||||
|
||||
template<typename T>
|
||||
concept BaseQuantitySpec =
|
||||
NamedQuantitySpec<T> && requires(T* t) { detail::to_base_specialization_of_base_quantity_spec(t); };
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
@@ -146,12 +161,12 @@ template<basic_symbol_text Symbol, auto...>
|
||||
struct named_unit;
|
||||
|
||||
/**
|
||||
* @brief Specialization for unit of a specified base dimension
|
||||
* @brief Specialization for unit of a specified base quantity
|
||||
*
|
||||
* Associates a unit with a specified base dimension.
|
||||
* Associates a unit with a specified base quantity.
|
||||
* 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 This is the preferred way to define a measurement unit for a specific base quantity.
|
||||
*
|
||||
* @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`
|
||||
@@ -159,19 +174,19 @@ struct named_unit;
|
||||
* the `cgs::centimetre` that is a base unit for `isq::length` in the CGS system.
|
||||
*
|
||||
* @tparam Symbol a short text representation of the unit
|
||||
* @tparam BaseDimension base dimension measured with this unit
|
||||
* @tparam BaseQuantitySpec base quantity measured with this unit
|
||||
*/
|
||||
template<basic_symbol_text Symbol, BaseDimension auto D>
|
||||
template<basic_symbol_text Symbol, BaseQuantitySpec auto Q>
|
||||
requires(!Symbol.empty())
|
||||
struct named_unit<Symbol, D> {
|
||||
struct named_unit<Symbol, Q> {
|
||||
static constexpr auto symbol = Symbol; ///< Unique base unit identifier
|
||||
static constexpr auto base_dimension = D;
|
||||
static constexpr auto base_quantity = Q;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Specialization for a unit that can be reused by several base dimensions
|
||||
* @brief Specialization for a unit that can be reused by several base quantities
|
||||
*
|
||||
* This specialization is used in rare cases where more than one base dimension in a specific
|
||||
* This specialization is used in rare cases where more than one base quantity in a specific
|
||||
* 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.
|
||||
@@ -328,7 +343,7 @@ inline constexpr bool is_per_of_units<per<Ts...>> = (... && (Unit<Ts> || is_powe
|
||||
} // namespace detail
|
||||
|
||||
template<typename T>
|
||||
concept DerivedUnitSpec = Unit<T> || detail::is_power_of_unit<T> || detail::is_per_of_units<T>;
|
||||
concept DerivedUnitExpr = Unit<T> || detail::is_power_of_unit<T> || detail::is_per_of_units<T>;
|
||||
|
||||
/**
|
||||
* @brief Measurement unit for a derived quantity
|
||||
@@ -375,7 +390,7 @@ concept DerivedUnitSpec = Unit<T> || detail::is_power_of_unit<T> || detail::is_p
|
||||
* @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 unit arithmetic equation provided by the user.
|
||||
*/
|
||||
template<DerivedUnitSpec... Us>
|
||||
template<DerivedUnitExpr... Us>
|
||||
struct derived_unit : detail::expr_fractions<derived_unit<>, Us...> {};
|
||||
|
||||
/**
|
||||
@@ -424,8 +439,8 @@ struct canonical_unit {
|
||||
U reference_unit;
|
||||
};
|
||||
|
||||
template<Unit T, basic_symbol_text Symbol, BaseDimension auto D>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, D>&);
|
||||
template<Unit T, basic_symbol_text Symbol, BaseQuantitySpec auto Q>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q>&);
|
||||
|
||||
template<Unit T, basic_symbol_text Symbol>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol>&);
|
||||
@@ -446,8 +461,8 @@ template<Unit T, auto M, typename U>
|
||||
return canonical_unit{M * base.mag, base.reference_unit};
|
||||
}
|
||||
|
||||
template<Unit T, basic_symbol_text Symbol, BaseDimension auto D>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, D>&)
|
||||
template<Unit T, basic_symbol_text Symbol, BaseQuantitySpec auto Q>
|
||||
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q>&)
|
||||
{
|
||||
return canonical_unit{mag<1>, t};
|
||||
}
|
||||
@@ -498,6 +513,7 @@ template<Unit Lhs, Unit Rhs>
|
||||
(!is_derived_from_specialization_of_constant_unit<Lhs> && !is_derived_from_specialization_of_constant_unit<Rhs>))
|
||||
return type_name<Lhs>() < type_name<Rhs>();
|
||||
else
|
||||
// put constants at the front of units list in the expression
|
||||
return is_derived_from_specialization_of_constant_unit<Lhs>;
|
||||
}
|
||||
|
||||
@@ -804,21 +820,21 @@ constexpr auto unit_symbol_impl(Out out, const power<F, Num, Den...>&, unit_symb
|
||||
}
|
||||
}
|
||||
|
||||
template<typename CharT, std::output_iterator<CharT> Out, DerivedUnitSpec M>
|
||||
template<typename CharT, std::output_iterator<CharT> Out, DerivedUnitExpr M>
|
||||
constexpr Out unit_symbol_impl(Out out, M m, std::size_t Idx, unit_symbol_formatting fmt, bool negative_power)
|
||||
{
|
||||
if (Idx > 0) out = print_separator<CharT>(out, fmt);
|
||||
return unit_symbol_impl<CharT>(out, m, fmt, negative_power);
|
||||
}
|
||||
|
||||
template<typename CharT, std::output_iterator<CharT> Out, DerivedUnitSpec... Ms, std::size_t... Idxs>
|
||||
template<typename CharT, std::output_iterator<CharT> Out, DerivedUnitExpr... Ms, std::size_t... Idxs>
|
||||
constexpr Out unit_symbol_impl(Out out, const type_list<Ms...>&, std::index_sequence<Idxs...>,
|
||||
unit_symbol_formatting fmt, bool negative_power)
|
||||
{
|
||||
return (..., (out = unit_symbol_impl<CharT>(out, Ms{}, Idxs, fmt, negative_power)));
|
||||
}
|
||||
|
||||
template<typename CharT, std::output_iterator<CharT> Out, DerivedUnitSpec... Nums, DerivedUnitSpec... Dens>
|
||||
template<typename CharT, std::output_iterator<CharT> Out, DerivedUnitExpr... Nums, DerivedUnitExpr... Dens>
|
||||
constexpr Out unit_symbol_impl(Out out, const type_list<Nums...>& nums, const type_list<Dens...>& dens,
|
||||
unit_symbol_formatting fmt)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user