mirror of
https://github.com/mpusz/mp-units.git
synced 2025-07-31 19:04:27 +02:00
feat: quantity_spec
support added
This commit is contained in:
@@ -34,15 +34,14 @@
|
||||
namespace {
|
||||
|
||||
using namespace units;
|
||||
using namespace units::si;
|
||||
using namespace units::si::unit_symbols;
|
||||
|
||||
inline constexpr auto g = 1 * standard_gravity;
|
||||
inline constexpr auto g = 1 * si::standard_gravity;
|
||||
inline constexpr auto air_density = 1.225 * isq::mass_density[kg / m3];
|
||||
|
||||
class Box {
|
||||
quantity<isq::area[m2]> base_;
|
||||
quantity<isq::length[m]> height_;
|
||||
quantity<isq::height[m]> height_;
|
||||
quantity<isq::mass_density[kg / m3]> density_ = air_density;
|
||||
public:
|
||||
constexpr Box(const quantity<isq::length[m]>& length, const quantity<isq::length[m]>& width,
|
||||
@@ -51,11 +50,11 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto filled_weight() const
|
||||
[[nodiscard]] constexpr quantity_of<isq::weight> auto filled_weight() const
|
||||
{
|
||||
const weak_quantity_of<isq::volume> auto volume = base_ * height_;
|
||||
const quantity_of<isq::mass> auto mass = density_ * volume;
|
||||
return mass * g;
|
||||
const weak_quantity_of<isq::mass> auto mass = density_ * volume;
|
||||
return quantity_cast<isq::weight>(mass * g);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr quantity<isq::length[m]> fill_level(const quantity<isq::mass[kg]>& measured_mass) const
|
||||
@@ -80,7 +79,9 @@ public:
|
||||
|
||||
int main()
|
||||
{
|
||||
const auto mm = isq::length[unit_symbols::mm]; // helper reference value
|
||||
using namespace units::si;
|
||||
|
||||
constexpr auto mm = isq::length[unit_symbols::mm]; // helper reference object
|
||||
|
||||
const auto height = (200.0 * mm)[metre];
|
||||
auto box = Box(1000.0 * mm, 500.0 * mm, height);
|
||||
|
@@ -152,7 +152,7 @@ public:
|
||||
leg(const waypoint& b, const waypoint& e) noexcept : begin_(&b), end_(&e) {}
|
||||
constexpr const waypoint& begin() const { return *begin_; };
|
||||
constexpr const waypoint& end() const { return *end_; }
|
||||
constexpr const distance get_length() const { return length_; }
|
||||
constexpr distance get_length() const { return length_; }
|
||||
};
|
||||
using legs = std::vector<leg>;
|
||||
|
||||
|
@@ -30,7 +30,7 @@
|
||||
|
||||
using namespace units;
|
||||
|
||||
constexpr quantity_of<isq::speed> auto avg_speed(quantity_of<isq::length> auto d, quantity_of<isq::time> auto t)
|
||||
constexpr quantity_of<isq::speed> auto avg_speed(quantity_of<isq::distance> auto d, quantity_of<isq::duration> auto t)
|
||||
{
|
||||
return quantity_cast<isq::speed>(d / t);
|
||||
}
|
||||
@@ -42,8 +42,8 @@ int main()
|
||||
|
||||
constexpr auto v1 = 110 * isq::speed[km / h];
|
||||
constexpr auto v2 = 70. * isq::speed[mph];
|
||||
constexpr auto v3 = avg_speed(220 * isq::length[km], 2 * isq::time[h]);
|
||||
constexpr auto v4 = avg_speed(quantity<isq::length[mi]>{140}, quantity<isq::time[h]>{2});
|
||||
constexpr auto v3 = avg_speed(220 * isq::distance[km], 2 * isq::duration[h]);
|
||||
constexpr auto v4 = avg_speed(quantity<isq::distance[mi]>{140}, quantity<isq::duration[h]>{2});
|
||||
constexpr auto v5 = quantity_cast<quantity<isq::speed[m / s]>>(v3);
|
||||
constexpr auto v6 = quantity_cast<m / s>(v4);
|
||||
constexpr auto v7 = quantity_cast<int>(v6);
|
||||
|
@@ -42,18 +42,19 @@ add_library(
|
||||
include/units/customization_points.h
|
||||
include/units/dimension.h
|
||||
# include/units/generic/angle.h
|
||||
# include/units/generic/dimensionless.h
|
||||
include/units/generic/dimensionless.h
|
||||
# include/units/generic/solid_angle.h
|
||||
# include/units/kind.h
|
||||
include/units/magnitude.h
|
||||
# include/units/math.h
|
||||
include/units/math.h
|
||||
# include/units/point_origin.h
|
||||
include/units/quantity.h
|
||||
# include/units/quantity_cast.h
|
||||
include/units/quantity_cast.h
|
||||
# include/units/quantity_kind.h
|
||||
# include/units/quantity_point.h
|
||||
# include/units/quantity_point_kind.h
|
||||
# include/units/random.h
|
||||
include/units/quantity_spec.h
|
||||
include/units/random.h
|
||||
include/units/ratio.h
|
||||
include/units/reference.h
|
||||
include/units/symbol_text.h
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
*/
|
||||
|
518
src/core/include/units/quantity_spec.h
Normal file
518
src/core/include/units/quantity_spec.h
Normal file
@@ -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)
|
||||
{
|
||||
|
@@ -23,6 +23,7 @@
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
add_units_module(
|
||||
isq DEPENDENCIES mp-units::core HEADERS include/units/isq/base_dimensions.h include/units/isq/isq.h
|
||||
include/units/isq/mechanics.h include/units/isq/space_and_time.h
|
||||
isq DEPENDENCIES mp-units::core
|
||||
HEADERS include/units/isq/base_quantities.h include/units/isq/isq.h include/units/isq/mechanics.h
|
||||
include/units/isq/space_and_time.h include/units/isq/thermodynamics.h
|
||||
)
|
||||
|
@@ -23,16 +23,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <units/dimension.h>
|
||||
#include <units/quantity_spec.h>
|
||||
|
||||
namespace units::isq {
|
||||
|
||||
BASE_DIMENSION(length, "L");
|
||||
BASE_DIMENSION(mass, "M");
|
||||
BASE_DIMENSION(time, "T");
|
||||
BASE_DIMENSION(electric_current, "I");
|
||||
// TODO Should the below use basic_symbol_text? How to name it for ASCII?
|
||||
BASE_DIMENSION(thermodynamic_temperature, "Θ");
|
||||
BASE_DIMENSION(amount_of_substance, "N");
|
||||
BASE_DIMENSION(luminous_intensity, "J");
|
||||
// clang-format off
|
||||
// dimensions of base quantities
|
||||
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 dim_electric_current : base_dimension<"I"> {} dim_electric_current;
|
||||
inline constexpr struct dim_thermodynamic_temperature : base_dimension<basic_symbol_text{"Θ", "O"}> {} dim_thermodynamic_temperature;
|
||||
inline constexpr struct dim_amount_of_substance : base_dimension<"N"> {} dim_amount_of_substance;
|
||||
inline constexpr struct dim_luminous_intensity : base_dimension<"J"> {} dim_luminous_intensity;
|
||||
// clang-format on
|
||||
|
||||
// base quantities
|
||||
QUANTITY_SPEC(length, dim_length);
|
||||
QUANTITY_SPEC(mass, dim_mass);
|
||||
QUANTITY_SPEC(time, dim_time);
|
||||
QUANTITY_SPEC(electric_current, dim_electric_current);
|
||||
QUANTITY_SPEC(thermodynamic_temperature, dim_thermodynamic_temperature);
|
||||
QUANTITY_SPEC(amount_of_substance, dim_amount_of_substance);
|
||||
QUANTITY_SPEC(luminous_intensity, dim_luminous_intensity);
|
||||
|
||||
} // namespace units::isq
|
@@ -23,6 +23,8 @@
|
||||
#pragma once
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include <units/isq/base_dimensions.h>
|
||||
#include <units/isq/base_quantities.h>
|
||||
#include <units/isq/mechanics.h>
|
||||
#include <units/isq/space_and_time.h>
|
||||
#include <units/isq/thermodynamics.h>
|
||||
// IWYU pragma: end_exports
|
||||
|
@@ -23,50 +23,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <units/dimension.h>
|
||||
#include <units/isq/base_dimensions.h>
|
||||
#include <units/isq/base_quantities.h>
|
||||
#include <units/isq/space_and_time.h>
|
||||
|
||||
namespace units::isq {
|
||||
|
||||
// inline constexpr struct mass : base_dimension<"M"> {} mass;
|
||||
DERIVED_DIMENSION(mass_density, decltype(mass / volume));
|
||||
DERIVED_DIMENSION(specific_volume, decltype(1 / mass_density));
|
||||
DERIVED_DIMENSION(relative_mass_density, decltype(mass_density / mass_density));
|
||||
DERIVED_DIMENSION(surface_mass_density, decltype(mass / area));
|
||||
DERIVED_DIMENSION(linear_mass_density, decltype(mass / length));
|
||||
DERIVED_DIMENSION(momentum, decltype(mass * speed)); // TODO velocity?
|
||||
DERIVED_DIMENSION(force, decltype(mass * acceleration)); // TODO what is a correct equation here?
|
||||
// DERIVED_DIMENSION(weight, decltype(mass * acceleration)); // TODO should we add it as a quantity or should it be a
|
||||
// quantity_kind?
|
||||
// TODO Should we add other forces as well: static_friction_force, kinematic_friction_force, rolling_resistance,
|
||||
// drag_force
|
||||
DERIVED_DIMENSION(impulse, decltype(force / time));
|
||||
DERIVED_DIMENSION(angular_momentum, decltype(length * momentum)); // TODO position_vector
|
||||
DERIVED_DIMENSION(moment_of_inertia, decltype(angular_momentum * angular_velocity));
|
||||
DERIVED_DIMENSION(moment_of_force, decltype(length * force)); // TODO position_vector
|
||||
DERIVED_DIMENSION(torque, decltype(moment_of_force)); // TODO angle?
|
||||
DERIVED_DIMENSION(angular_impulse, decltype(moment_of_force * time));
|
||||
DERIVED_DIMENSION(pressure, decltype(force / area));
|
||||
DERIVED_DIMENSION(stress, decltype(pressure)); // TODO tensor?
|
||||
DERIVED_DIMENSION(normal_stress, decltype(force / area));
|
||||
DERIVED_DIMENSION(strain, decltype(stress / stress)); // TODO what is a correct equation here?
|
||||
DERIVED_DIMENSION(poisson_number, decltype(length / length)); // TODO width?
|
||||
// TODO modulus quantities
|
||||
DERIVED_DIMENSION(compressibility, decltype(volume / volume / pressure));
|
||||
DERIVED_DIMENSION(second_axial_moment_of_area, decltype(area * area)); // TODO what is a correct equation here?
|
||||
DERIVED_DIMENSION(section_modulus, decltype(second_axial_moment_of_area / length)); // TODO radial distance
|
||||
// TODO friction coefficients?
|
||||
DERIVED_DIMENSION(dynamic_viscosity, decltype(stress * length / speed)); // TODO shear stress, velocity
|
||||
DERIVED_DIMENSION(kinematic_viscosity, decltype(dynamic_viscosity / mass_density));
|
||||
DERIVED_DIMENSION(surface_tension, decltype(force / length)); // TODO what is a correct equation here?
|
||||
DERIVED_DIMENSION(power, decltype(force * speed));
|
||||
// TODO what about energy (potential and kinetic as separate quantities will prevent an equation for mechanical one, is
|
||||
// it expected?)
|
||||
DERIVED_DIMENSION(efficiency, decltype(power / power));
|
||||
DERIVED_DIMENSION(mass_flow, decltype(mass_density * speed)); // TODO velocity
|
||||
DERIVED_DIMENSION(mass_flow_rate, decltype(mass_flow * area));
|
||||
DERIVED_DIMENSION(mass_change_rate, decltype(mass / time));
|
||||
DERIVED_DIMENSION(volume_flow_rate, decltype(speed * area)); // TODO velocity
|
||||
// DERIVED_DIMENSION(action, decltype(energy * time)); // TODO make it compile
|
||||
QUANTITY_SPEC(mass_density, mass / volume);
|
||||
inline constexpr auto density = mass_density;
|
||||
QUANTITY_SPEC(specific_volume, 1 / mass_density);
|
||||
QUANTITY_SPEC(relative_mass_density, mass_density / mass_density);
|
||||
inline constexpr auto relative_density = relative_mass_density;
|
||||
QUANTITY_SPEC(surface_mass_density, mass / area);
|
||||
inline constexpr auto surface_density = surface_mass_density;
|
||||
QUANTITY_SPEC(linear_mass_density, mass / length);
|
||||
inline constexpr auto linear_density = linear_mass_density;
|
||||
QUANTITY_SPEC(momentum, mass* velocity);
|
||||
QUANTITY_SPEC(force, mass* acceleration); // vector // TODO what is a correct equation here?
|
||||
QUANTITY_SPEC(weight, force); // vector // TODO g?
|
||||
QUANTITY_SPEC(static_friction_force, force); // vector
|
||||
inline constexpr auto static_friction = static_friction_force;
|
||||
QUANTITY_SPEC(kinetic_friction_force, force); // vector
|
||||
inline constexpr auto dynamic_friction_force = kinetic_friction_force;
|
||||
QUANTITY_SPEC(rolling_resistance, force); // vector
|
||||
inline constexpr auto rolling_drag = rolling_resistance;
|
||||
inline constexpr auto rolling_friction_force = rolling_resistance;
|
||||
QUANTITY_SPEC(drag_force, force); // vector
|
||||
QUANTITY_SPEC(impulse, force* time); // vector
|
||||
QUANTITY_SPEC(angular_momentum, position_vector* momentum); // vector
|
||||
QUANTITY_SPEC(moment_of_inertia, angular_momentum / angular_velocity, quantity_character::tensor);
|
||||
QUANTITY_SPEC(moment_of_force, position_vector* force); // vector
|
||||
QUANTITY_SPEC(torque, moment_of_force, quantity_character::scalar);
|
||||
QUANTITY_SPEC(angular_impulse, moment_of_force* time); // vector
|
||||
QUANTITY_SPEC(pressure, force / area, quantity_character::scalar);
|
||||
QUANTITY_SPEC(gauge_pressure, pressure);
|
||||
QUANTITY_SPEC(stress, pressure, quantity_character::tensor);
|
||||
QUANTITY_SPEC(normal_stress, pressure, quantity_character::scalar);
|
||||
QUANTITY_SPEC(shear_stress, pressure, quantity_character::scalar);
|
||||
QUANTITY_SPEC(strain, dimensionless, quantity_character::tensor);
|
||||
QUANTITY_SPEC(relative_linear_strain, length / length);
|
||||
QUANTITY_SPEC(shear_strain, displacement / thickness);
|
||||
QUANTITY_SPEC(relative_volume_strain, volume / volume);
|
||||
QUANTITY_SPEC(Poisson_number, width / length);
|
||||
QUANTITY_SPEC(modulus_of_elasticity, normal_stress / relative_linear_strain);
|
||||
inline constexpr auto Young_modulus = modulus_of_elasticity;
|
||||
QUANTITY_SPEC(modulus_of_rigidity, shear_stress / shear_strain);
|
||||
inline constexpr auto shear_modulus = modulus_of_rigidity;
|
||||
QUANTITY_SPEC(modulus_of_compression, pressure / relative_volume_strain);
|
||||
// QUANTITY_SPEC(modulus_of_compression, -pressure / relative_volume_strain); // TODO how to handle "negative" part
|
||||
inline constexpr auto bulk_modulus = modulus_of_compression;
|
||||
QUANTITY_SPEC(compressibility, 1 / volume * (volume / pressure));
|
||||
// QUANTITY_SPEC(compressibility, -1 / volume * (volume / pressure)); // TODO how to handle "negative" part
|
||||
QUANTITY_SPEC(second_axial_moment_of_area, pow<2>(radial_distance) * area);
|
||||
QUANTITY_SPEC(second_polar_moment_of_area, pow<2>(radial_distance) * area);
|
||||
QUANTITY_SPEC(section_modulus, second_axial_moment_of_area / radial_distance);
|
||||
QUANTITY_SPEC(static_friction_coefficient, static_friction_force / force, quantity_character::scalar);
|
||||
inline constexpr auto static_friction_factor = static_friction_coefficient;
|
||||
inline constexpr auto coefficient_of_static_friction = static_friction_coefficient;
|
||||
QUANTITY_SPEC(kinetic_friction_factor, kinetic_friction_force / force, quantity_character::scalar);
|
||||
inline constexpr auto dynamic_friction_factor = kinetic_friction_factor;
|
||||
QUANTITY_SPEC(rolling_resistance_factor, force / force, quantity_character::scalar);
|
||||
// QUANTITY_SPEC(drag_coefficient, mag<2>* drag_force / (mass_density * pow<2>(speed) * area));
|
||||
// inline constexpr auto drag_factor = drag_coefficient;
|
||||
QUANTITY_SPEC(dynamic_viscosity, shear_stress* length / velocity);
|
||||
QUANTITY_SPEC(kinematic_viscosity, dynamic_viscosity / mass_density);
|
||||
QUANTITY_SPEC(surface_tension, force / length, quantity_character::scalar); // TODO what is a correct equation here?
|
||||
QUANTITY_SPEC(power, force* velocity, quantity_character::scalar);
|
||||
// QUANTITY_SPEC(energy, force* length);
|
||||
QUANTITY_SPEC(potential_energy, mass* acceleration* height); // TODO what is a correct equation here?
|
||||
QUANTITY_SPEC(kinetic_energy, mass* pow<2>(speed));
|
||||
// QUANTITY_SPEC(kinetic_energy, mag<1, 2>* mass* pow<2>(speed));
|
||||
// TODO how to implement that?
|
||||
// QUANTITY_SPEC(mechanical_energy, potential_energy + kinetic_energy);
|
||||
QUANTITY_SPEC(mechanical_energy, potential_energy);
|
||||
QUANTITY_SPEC(mechanical_work, force* displacement, quantity_character::scalar);
|
||||
inline constexpr auto work = mechanical_work;
|
||||
QUANTITY_SPEC(efficiency, power / power);
|
||||
QUANTITY_SPEC(mass_flow, mass_density* velocity); // vector
|
||||
QUANTITY_SPEC(mass_flow_rate, mass_flow* area, quantity_character::scalar);
|
||||
QUANTITY_SPEC(mass_change_rate, mass / time);
|
||||
QUANTITY_SPEC(volume_flow_rate, velocity* area, quantity_character::scalar);
|
||||
QUANTITY_SPEC(action, mechanical_energy* time);
|
||||
|
||||
} // namespace units::isq
|
||||
|
@@ -23,44 +23,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <units/dimension.h>
|
||||
#include <units/isq/base_dimensions.h>
|
||||
#include <units/isq/base_quantities.h>
|
||||
|
||||
namespace units::isq {
|
||||
|
||||
// inline constexpr struct length : base_dimension<"L"> {} length;
|
||||
DERIVED_DIMENSION(curvature, decltype(1 / length));
|
||||
DERIVED_DIMENSION(area, decltype(length * length));
|
||||
DERIVED_DIMENSION(volume, decltype(length * length * length));
|
||||
DERIVED_DIMENSION(angular_measure, decltype(length / length));
|
||||
DERIVED_DIMENSION(angular_displacement, decltype(length / length));
|
||||
DERIVED_DIMENSION(phase_angle, decltype(length / length));
|
||||
inline constexpr struct solid_angular_measure : decltype(area / (length * length)) {
|
||||
} solid_angular_measure;
|
||||
// inline constexpr struct time : base_dimension<"T"> {} time; // TODO called duration in ISO 80000
|
||||
// TODO there is also a velocity in ISO 80000
|
||||
DERIVED_DIMENSION(speed, decltype(length / time));
|
||||
DERIVED_DIMENSION(acceleration, decltype(speed / time));
|
||||
DERIVED_DIMENSION(angular_velocity, decltype(angular_displacement / time));
|
||||
DERIVED_DIMENSION(angular_acceleration, decltype(angular_velocity / time));
|
||||
inline constexpr struct period_duration : time {
|
||||
} period_duration;
|
||||
inline constexpr struct time_constant : time {
|
||||
} time_constant;
|
||||
inline constexpr struct rotation : angular_displacement {
|
||||
} rotation;
|
||||
DERIVED_DIMENSION(frequency, decltype(1 / time));
|
||||
DERIVED_DIMENSION(rotational_frequency, decltype(rotation / time));
|
||||
DERIVED_DIMENSION(angular_frequency, decltype(angular_measure / time));
|
||||
inline constexpr struct wavelength : length {
|
||||
} wavelength;
|
||||
DERIVED_DIMENSION(repetency, decltype(1 / wavelength));
|
||||
DERIVED_DIMENSION(wave_vector, decltype(1 / length));
|
||||
DERIVED_DIMENSION(angular_repetency, decltype(1 / wavelength));
|
||||
DERIVED_DIMENSION(phase_velocity, decltype(angular_frequency / angular_repetency));
|
||||
DERIVED_DIMENSION(damping_coefficient, decltype(1 / time_constant));
|
||||
DERIVED_DIMENSION(logarithmic_decrement, decltype(damping_coefficient * period_duration));
|
||||
DERIVED_DIMENSION(attenuation, decltype(1 / length));
|
||||
DERIVED_DIMENSION(phase_coefficient, decltype(phase_angle / length));
|
||||
DERIVED_DIMENSION(propagation_coefficient, decltype(1 / length));
|
||||
// clang-format off
|
||||
QUANTITY_SPEC(width, length);
|
||||
inline constexpr auto breadth = width;
|
||||
QUANTITY_SPEC(height, length);
|
||||
inline constexpr auto depth = height;
|
||||
inline constexpr auto altitude = height;
|
||||
QUANTITY_SPEC(thickness, width);
|
||||
QUANTITY_SPEC(diameter, width);
|
||||
// QUANTITY_SPEC(radius, mag<ratio{1, 2}> * diameter);
|
||||
QUANTITY_SPEC(radius, diameter);
|
||||
QUANTITY_SPEC(path_length, length);
|
||||
inline constexpr auto arc_length = path_length;
|
||||
QUANTITY_SPEC(distance, path_length);
|
||||
QUANTITY_SPEC(radial_distance, distance);
|
||||
QUANTITY_SPEC(position_vector, length, quantity_character::vector);
|
||||
QUANTITY_SPEC(displacement, length, quantity_character::vector);
|
||||
QUANTITY_SPEC(radius_of_curvature, radius);
|
||||
QUANTITY_SPEC(curvature, 1 / radius_of_curvature);
|
||||
QUANTITY_SPEC(area, pow<2>(length));
|
||||
QUANTITY_SPEC(volume, pow<3>(length));
|
||||
QUANTITY_SPEC(angular_measure, arc_length / radius);
|
||||
QUANTITY_SPEC(rotational_displacement, path_length / radius);
|
||||
inline constexpr auto angular_displacement = rotational_displacement;
|
||||
QUANTITY_SPEC(phase_angle, angular_measure);
|
||||
QUANTITY_SPEC(solid_angular_measure, angular_measure * angular_measure);
|
||||
inline constexpr auto duration = time;
|
||||
QUANTITY_SPEC(velocity, position_vector / duration); // vector
|
||||
QUANTITY_SPEC(speed, distance / duration); // TODO length, path_length?
|
||||
QUANTITY_SPEC(acceleration, velocity / duration); // vector
|
||||
QUANTITY_SPEC(angular_velocity, angular_displacement / duration, quantity_character::vector);
|
||||
QUANTITY_SPEC(angular_acceleration, angular_velocity / duration);
|
||||
QUANTITY_SPEC(period_duration, duration);
|
||||
inline constexpr auto period = period_duration;
|
||||
QUANTITY_SPEC(time_constant, duration);
|
||||
QUANTITY_SPEC(rotation, rotational_displacement);
|
||||
QUANTITY_SPEC(frequency, 1 / period_duration);
|
||||
QUANTITY_SPEC(rotational_frequency, rotation / duration);
|
||||
QUANTITY_SPEC(angular_frequency, phase_angle / duration);
|
||||
QUANTITY_SPEC(wavelength, length);
|
||||
QUANTITY_SPEC(repetency, 1 / wavelength);
|
||||
inline constexpr auto wavenumber = repetency;
|
||||
QUANTITY_SPEC(wave_vector, repetency, quantity_character::vector);
|
||||
QUANTITY_SPEC(angular_repetency, 1 / wavelength);
|
||||
inline constexpr auto angular_wavenumber = angular_repetency;
|
||||
QUANTITY_SPEC(phase_velocity, angular_frequency / angular_repetency);
|
||||
inline constexpr auto phase_speed = phase_velocity;
|
||||
QUANTITY_SPEC(group_velocity, angular_frequency / angular_repetency);
|
||||
inline constexpr auto group_speed = group_velocity;
|
||||
QUANTITY_SPEC(damping_coefficient, 1 / time_constant);
|
||||
QUANTITY_SPEC(logarithmic_decrement, damping_coefficient * period_duration);
|
||||
QUANTITY_SPEC(attenuation, 1 / distance);
|
||||
inline constexpr auto extinction = attenuation;
|
||||
QUANTITY_SPEC(phase_coefficient, phase_angle / path_length);
|
||||
QUANTITY_SPEC(propagation_coefficient, 1 / length); // γ = α + iβ where α denotes attenuation and β the phase coefficient of a plane wave
|
||||
// clang-format on
|
||||
|
||||
} // namespace units::isq
|
||||
|
@@ -23,7 +23,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <units/dimension.h>
|
||||
#include <units/isq/base_dimensions.h>
|
||||
#include <units/isq/base_quantities.h>
|
||||
#include <units/isq/space_and_time.h>
|
||||
|
||||
namespace units::isq {
|
||||
@@ -34,6 +34,6 @@ namespace units::isq {
|
||||
// DERIVED_DIMENSION(mass_density, decltype(mass / volume));
|
||||
|
||||
|
||||
DERIVED_DIMENSION(energy, decltype(force * length));
|
||||
// DERIVED_DIMENSION(energy, decltype(force * length)); // defined in a mechanics header
|
||||
|
||||
} // namespace units::isq
|
||||
|
@@ -641,5 +641,6 @@ inline constexpr auto d = day;
|
||||
inline constexpr auto m2 = square<metre>;
|
||||
inline constexpr auto m3 = cubic<metre>;
|
||||
inline constexpr auto s2 = square<second>;
|
||||
inline constexpr auto s3 = cubic<second>;
|
||||
|
||||
} // namespace units::si::unit_symbols
|
||||
|
@@ -22,7 +22,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <units/isq/base_dimensions.h>
|
||||
#include <units/isq/base_quantities.h>
|
||||
#include <units/si/prefixes.h>
|
||||
#include <units/unit.h>
|
||||
|
||||
|
@@ -26,9 +26,7 @@ find_package(Catch2 3 CONFIG REQUIRED)
|
||||
|
||||
add_executable(
|
||||
unit_tests_runtime
|
||||
distribution_test.cpp
|
||||
fmt_test.cpp
|
||||
|
||||
distribution_test.cpp fmt_test.cpp
|
||||
# fmt_units_test.cpp
|
||||
math_test.cpp
|
||||
)
|
||||
|
@@ -35,7 +35,6 @@ cmake_minimum_required(VERSION 3.2)
|
||||
add_library(
|
||||
unit_tests_static
|
||||
dimension_test.cpp
|
||||
|
||||
# angle_test.cpp
|
||||
# cgs_test.cpp
|
||||
# chrono_test.cpp
|
||||
@@ -43,27 +42,26 @@ add_library(
|
||||
# custom_rep_test_min_expl.cpp
|
||||
# custom_unit_test.cpp
|
||||
# dimension_op_test.cpp
|
||||
dimension_test.cpp
|
||||
# dimensions_concepts_test.cpp
|
||||
# fixed_string_test.cpp
|
||||
# fps_test.cpp
|
||||
# iec80000_test.cpp
|
||||
# kind_test.cpp
|
||||
magnitude_test.cpp
|
||||
|
||||
# math_test.cpp
|
||||
# point_origin_test.cpp
|
||||
# prime_test.cpp
|
||||
quantity_spec_test.cpp
|
||||
ratio_test.cpp
|
||||
reference_test.cpp
|
||||
|
||||
# si_test.cpp
|
||||
# si_cgs_test.cpp
|
||||
# si_fps_test.cpp
|
||||
# si_hep_test.cpp
|
||||
# si_test.cpp
|
||||
# symbol_text_test.cpp
|
||||
type_list_test.cpp
|
||||
unit_test.cpp
|
||||
|
||||
# us_test.cpp
|
||||
)
|
||||
|
||||
|
@@ -33,37 +33,37 @@ using namespace units;
|
||||
using dimension_one_ = struct dimension_one;
|
||||
|
||||
// clang-format off
|
||||
BASE_DIMENSION_(length, "L");
|
||||
BASE_DIMENSION_(time, "T");
|
||||
BASE_DIMENSION_(mass, "M");
|
||||
inline constexpr struct length_ : base_dimension<"L"> {} length;
|
||||
inline constexpr struct mass_ : base_dimension<"M"> {} mass;
|
||||
inline constexpr struct time_ : base_dimension<"T"> {} time;
|
||||
|
||||
inline constexpr struct second_ : named_unit<"s", time> {} second;
|
||||
QUANTITY_SPEC_(q_time, time);
|
||||
inline constexpr struct second_ : named_unit<"s", q_time> {} second;
|
||||
|
||||
DERIVED_DIMENSION_(frequency, decltype(1 / time));
|
||||
DERIVED_DIMENSION_(action, decltype(1 / time));
|
||||
DERIVED_DIMENSION_(area, decltype(length * length));
|
||||
DERIVED_DIMENSION_(volume, decltype(area * length));
|
||||
DERIVED_DIMENSION_(speed, decltype(length / time));
|
||||
inline constexpr struct velocity_ : speed_ {} velocity;
|
||||
DERIVED_DIMENSION_(acceleration, decltype(speed / time));
|
||||
DERIVED_DIMENSION_(force, decltype(mass * acceleration));
|
||||
DERIVED_DIMENSION_(moment_of_force, decltype(length * force));
|
||||
DERIVED_DIMENSION_(torque, decltype(moment_of_force));
|
||||
DERIVED_DIMENSION_(pressure, decltype(force / area));
|
||||
DERIVED_DIMENSION_(stress, decltype(pressure));
|
||||
DERIVED_DIMENSION_(strain, decltype(stress / stress));
|
||||
DERIVED_DIMENSION_(power, decltype(force * speed));
|
||||
DERIVED_DIMENSION_(efficiency, decltype(power / power));
|
||||
DERIVED_DIMENSION_(energy, decltype(force * length));
|
||||
inline constexpr auto frequency = 1 / time;
|
||||
inline constexpr auto action = 1 / time;
|
||||
inline constexpr auto area = length * length;
|
||||
inline constexpr auto volume = area * length;
|
||||
inline constexpr auto speed = length / time;
|
||||
inline constexpr auto acceleration = speed / time;
|
||||
inline constexpr auto force = mass * acceleration;
|
||||
inline constexpr auto moment_of_force = length * force;
|
||||
inline constexpr auto torque = moment_of_force;
|
||||
inline constexpr auto pressure = force / area;
|
||||
inline constexpr auto stress = pressure;
|
||||
inline constexpr auto strain = stress / stress;
|
||||
inline constexpr auto power = force * speed;
|
||||
inline constexpr auto efficiency = power / power;
|
||||
inline constexpr auto energy = force * length;
|
||||
// clang-format on
|
||||
|
||||
// concepts verification
|
||||
static_assert(BaseDimension<length_>);
|
||||
static_assert(!BaseDimension<frequency_>);
|
||||
static_assert(!BaseDimension<std::remove_const_t<decltype(frequency)>>);
|
||||
static_assert(!DerivedDimension<length_>);
|
||||
static_assert(DerivedDimension<frequency_>);
|
||||
static_assert(DerivedDimension<std::remove_const_t<decltype(frequency)>>);
|
||||
static_assert(Dimension<length_>);
|
||||
static_assert(Dimension<frequency_>);
|
||||
static_assert(Dimension<std::remove_const_t<decltype(frequency)>>);
|
||||
|
||||
static_assert(DerivedDimension<dimension_one_>);
|
||||
static_assert(DerivedDimension<decltype(length / length)>); // dimension_one
|
||||
@@ -137,29 +137,29 @@ concept invalid_operations = requires {
|
||||
requires !requires { 2 == t; };
|
||||
requires !requires { t < 2; };
|
||||
requires !requires { 2 < t; };
|
||||
requires !requires { t + time[second]; };
|
||||
requires !requires { t - time[second]; };
|
||||
requires !requires { t* time[second]; };
|
||||
requires !requires { t / time[second]; };
|
||||
requires !requires { t == time[second]; };
|
||||
requires !requires { t < time[second]; };
|
||||
requires !requires { time[second] + t; };
|
||||
requires !requires { time[second] - t; };
|
||||
requires !requires { time[second] * t; };
|
||||
requires !requires { time[second] / t; };
|
||||
requires !requires { time[second] == t; };
|
||||
requires !requires { time[second] < t; };
|
||||
requires !requires { t + 1 * time[second]; };
|
||||
requires !requires { t - 1 * time[second]; };
|
||||
requires !requires { t * 1 * time[second]; };
|
||||
requires !requires { t / 1 * time[second]; };
|
||||
requires !requires { t == 1 * time[second]; };
|
||||
requires !requires { t == 1 * time[second]; };
|
||||
requires !requires { 1 * time[second] + t; };
|
||||
requires !requires { 1 * time[second] - t; };
|
||||
requires !requires { 1 * time[second] * t; };
|
||||
requires !requires { 1 * time[second] == t; };
|
||||
requires !requires { 1 * time[second] < t; };
|
||||
requires !requires { t + q_time[second]; };
|
||||
requires !requires { t - q_time[second]; };
|
||||
requires !requires { t* q_time[second]; };
|
||||
requires !requires { t / q_time[second]; };
|
||||
requires !requires { t == q_time[second]; };
|
||||
requires !requires { t < q_time[second]; };
|
||||
requires !requires { q_time[second] + t; };
|
||||
requires !requires { q_time[second] - t; };
|
||||
requires !requires { q_time[second] * t; };
|
||||
requires !requires { q_time[second] / t; };
|
||||
requires !requires { q_time[second] == t; };
|
||||
requires !requires { q_time[second] < t; };
|
||||
requires !requires { t + 1 * q_time[second]; };
|
||||
requires !requires { t - 1 * q_time[second]; };
|
||||
requires !requires { t * 1 * q_time[second]; };
|
||||
requires !requires { t / 1 * q_time[second]; };
|
||||
requires !requires { t == 1 * q_time[second]; };
|
||||
requires !requires { t == 1 * q_time[second]; };
|
||||
requires !requires { 1 * q_time[second] + t; };
|
||||
requires !requires { 1 * q_time[second] - t; };
|
||||
requires !requires { 1 * q_time[second] * t; };
|
||||
requires !requires { 1 * q_time[second] == t; };
|
||||
requires !requires { 1 * q_time[second] < t; };
|
||||
};
|
||||
static_assert(invalid_operations<time>);
|
||||
|
||||
@@ -170,95 +170,62 @@ static_assert(speed == speed);
|
||||
// comparisons of equivalent dimensions (named vs unnamed/derived)
|
||||
static_assert(length / length == dimension_one);
|
||||
|
||||
static_assert(1 / time != frequency);
|
||||
static_assert(interconvertible(1 / time, frequency));
|
||||
static_assert(1 / time == frequency);
|
||||
static_assert(1 / frequency == time);
|
||||
static_assert(frequency * time == dimension_one);
|
||||
static_assert(is_of_type<common_dimension(1 / time, frequency), frequency_>);
|
||||
static_assert(is_of_type<common_dimension(frequency, 1 / time), frequency_>);
|
||||
|
||||
static_assert(length * length != area);
|
||||
static_assert(interconvertible(length * length, area));
|
||||
static_assert(length * length == area);
|
||||
static_assert(length * length != volume);
|
||||
static_assert(area / length == length);
|
||||
static_assert(is_of_type<common_dimension(length* length, area), area_>);
|
||||
static_assert(is_of_type<common_dimension(area, length* length), area_>);
|
||||
|
||||
static_assert(length * length * length != volume);
|
||||
static_assert(area * length != volume);
|
||||
static_assert(volume / length != area);
|
||||
static_assert(length * length * length == volume);
|
||||
static_assert(area * length == volume);
|
||||
static_assert(volume / length == area);
|
||||
static_assert(volume / length / length == length);
|
||||
static_assert(area * area / length != volume);
|
||||
static_assert(area * (area / length) != volume);
|
||||
static_assert(area * area / length == volume);
|
||||
static_assert(area * (area / length) == volume);
|
||||
static_assert(volume / (length * length) == length);
|
||||
|
||||
static_assert(length / time != speed);
|
||||
static_assert(length / time == speed);
|
||||
static_assert(length * time != speed);
|
||||
static_assert(length / time / time != speed);
|
||||
static_assert(length / speed == time);
|
||||
static_assert(speed * time == length);
|
||||
static_assert(is_of_type<common_dimension(length / time, speed), speed_>);
|
||||
static_assert(is_of_type<common_dimension(speed, length / time), speed_>);
|
||||
static_assert(is_of_type<common_dimension(length / time, length / time), decltype(length / time)>);
|
||||
|
||||
static_assert(length / time / time != acceleration);
|
||||
static_assert(length / (time * time) != acceleration);
|
||||
static_assert(speed / time != acceleration);
|
||||
static_assert(length / time / time == acceleration);
|
||||
static_assert(length / (time * time) == acceleration);
|
||||
static_assert(speed / time == acceleration);
|
||||
static_assert(speed / acceleration == time);
|
||||
static_assert(acceleration * time != speed);
|
||||
static_assert(acceleration * time == speed);
|
||||
static_assert(acceleration * (time * time) == length);
|
||||
static_assert(acceleration / speed != frequency);
|
||||
|
||||
// comparison of convertible named dimensions
|
||||
static_assert(velocity != speed);
|
||||
static_assert(interconvertible(speed, velocity));
|
||||
static_assert(is_of_type<common_dimension(velocity, speed), velocity_>);
|
||||
static_assert(is_of_type<common_dimension(speed, velocity), velocity_>);
|
||||
static_assert(acceleration / speed == frequency);
|
||||
|
||||
// comparison of convertible unnamed dimensions
|
||||
static_assert(is_of_type<mass * acceleration, derived_dimension<length_, mass_, per<units::power<time_, 2>>>>);
|
||||
static_assert(is_of_type<acceleration * mass, derived_dimension<length_, mass_, per<units::power<time_, 2>>>>);
|
||||
static_assert(mass * acceleration == acceleration * mass);
|
||||
static_assert(interconvertible(mass * acceleration, acceleration* mass));
|
||||
|
||||
// comparisons of equivalent but not convertible dimensions
|
||||
static_assert(energy != torque);
|
||||
static_assert(!interconvertible(energy, torque));
|
||||
static_assert(energy == torque);
|
||||
|
||||
static_assert(force * length != energy);
|
||||
static_assert(force * length != torque);
|
||||
static_assert(interconvertible(force * length, energy));
|
||||
static_assert(interconvertible(force * length, torque));
|
||||
template<auto T1, auto T2>
|
||||
concept no_common_type = requires {
|
||||
requires !requires { typename std::common_type_t<decltype(T1), decltype(T2)>; };
|
||||
requires !requires { typename std::common_type_t<decltype(T2), decltype(T1)>; };
|
||||
};
|
||||
static_assert(no_common_type<energy, torque>);
|
||||
static_assert(force * length == energy);
|
||||
static_assert(force * length == torque);
|
||||
|
||||
static_assert(frequency != action);
|
||||
static_assert(!interconvertible(frequency, action));
|
||||
static_assert(no_common_type<frequency, action>);
|
||||
static_assert(frequency == action);
|
||||
|
||||
// dimension_one
|
||||
static_assert(interconvertible(power / power, efficiency));
|
||||
static_assert(power / power != efficiency);
|
||||
static_assert(dimension_one != efficiency);
|
||||
static_assert(power / power == efficiency);
|
||||
static_assert(dimension_one == efficiency);
|
||||
|
||||
static_assert(!interconvertible(efficiency, strain));
|
||||
static_assert(efficiency != strain);
|
||||
static_assert(efficiency == strain);
|
||||
|
||||
static_assert(stress / stress != strain);
|
||||
static_assert(stress / stress != efficiency);
|
||||
static_assert(interconvertible(stress / stress, strain));
|
||||
static_assert(interconvertible(stress / stress, efficiency));
|
||||
static_assert(stress / stress == strain);
|
||||
static_assert(stress / stress == efficiency);
|
||||
|
||||
// comparison of not equivalent dimensions
|
||||
static_assert(length != time);
|
||||
static_assert(!interconvertible(length, time));
|
||||
|
||||
static_assert(acceleration != speed);
|
||||
static_assert(!interconvertible(acceleration, speed));
|
||||
|
||||
// power
|
||||
static_assert(is_of_type<pow<2>(length), derived_dimension<units::power<length_, 2>>>);
|
||||
|
355
test/unit_test/static/quantity_spec_test.cpp
Normal file
355
test/unit_test/static/quantity_spec_test.cpp
Normal file
@@ -0,0 +1,355 @@
|
||||
// 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.
|
||||
|
||||
#include "test_tools.h"
|
||||
#include <units/quantity.h>
|
||||
#include <units/quantity_spec.h>
|
||||
#include <units/reference.h>
|
||||
#include <units/unit.h>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace units;
|
||||
|
||||
using dimensionless_ = struct dimensionless;
|
||||
using dim_one_ = struct dimension_one;
|
||||
|
||||
// clang-format off
|
||||
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;
|
||||
|
||||
// quantities specification
|
||||
QUANTITY_SPEC_(length, dim_length);
|
||||
QUANTITY_SPEC_(mass, dim_mass);
|
||||
QUANTITY_SPEC_(time, dim_time);
|
||||
|
||||
inline constexpr struct second_ : named_unit<"s", time> {} second;
|
||||
|
||||
QUANTITY_SPEC_(height, length);
|
||||
QUANTITY_SPEC_(path_length, length);
|
||||
QUANTITY_SPEC_(distance, path_length);
|
||||
QUANTITY_SPEC_(position_vector, length, quantity_character::vector);
|
||||
QUANTITY_SPEC_(period_duration, time);
|
||||
|
||||
QUANTITY_SPEC_(frequency, 1 / period_duration);
|
||||
QUANTITY_SPEC_(action, 1 / time);
|
||||
QUANTITY_SPEC_(area, pow<2>(length));
|
||||
QUANTITY_SPEC_(volume, pow<3>(length));
|
||||
QUANTITY_SPEC_(velocity, position_vector / time);
|
||||
QUANTITY_SPEC_(speed, distance / time);
|
||||
QUANTITY_SPEC_(acceleration, velocity / time);
|
||||
QUANTITY_SPEC_(force, mass * acceleration);
|
||||
QUANTITY_SPEC_(moment_of_force, position_vector* force);
|
||||
QUANTITY_SPEC_(torque, moment_of_force, quantity_character::scalar);
|
||||
QUANTITY_SPEC_(pressure, force / area, quantity_character::scalar);
|
||||
QUANTITY_SPEC_(stress, pressure, quantity_character::tensor);
|
||||
QUANTITY_SPEC_(strain, dimensionless, quantity_character::tensor);
|
||||
QUANTITY_SPEC_(power, force* velocity, quantity_character::scalar);
|
||||
QUANTITY_SPEC_(efficiency, power / power);
|
||||
QUANTITY_SPEC_(potential_energy, mass* acceleration* height);
|
||||
QUANTITY_SPEC_(energy, force * length);
|
||||
// clang-format on
|
||||
|
||||
// concepts verification
|
||||
static_assert(QuantitySpec<length_>);
|
||||
static_assert(BaseQuantitySpec<length_>);
|
||||
static_assert(NamedQuantitySpec<length_>);
|
||||
static_assert(!DerivedQuantitySpec<length_>);
|
||||
|
||||
static_assert(QuantitySpec<frequency_>);
|
||||
static_assert(!BaseQuantitySpec<frequency_>);
|
||||
static_assert(NamedQuantitySpec<frequency_>);
|
||||
static_assert(!DerivedQuantitySpec<frequency_>);
|
||||
|
||||
static_assert(QuantitySpec<decltype(1 / time)>);
|
||||
static_assert(!BaseQuantitySpec<decltype(1 / time)>);
|
||||
static_assert(!NamedQuantitySpec<decltype(1 / time)>);
|
||||
static_assert(DerivedQuantitySpec<decltype(1 / time)>);
|
||||
|
||||
static_assert(QuantitySpec<dimensionless_>);
|
||||
static_assert(!BaseQuantitySpec<dimensionless_>);
|
||||
static_assert(NamedQuantitySpec<dimensionless_>);
|
||||
static_assert(!DerivedQuantitySpec<dimensionless_>);
|
||||
|
||||
// dimensionless
|
||||
static_assert(QuantitySpec<decltype(length / length)>);
|
||||
static_assert(!BaseQuantitySpec<decltype(length / length)>);
|
||||
static_assert(NamedQuantitySpec<decltype(length / length)>);
|
||||
static_assert(!DerivedQuantitySpec<decltype(length / length)>);
|
||||
|
||||
// length
|
||||
static_assert(QuantitySpec<decltype(speed * time)>);
|
||||
static_assert(!BaseQuantitySpec<decltype(speed * time)>);
|
||||
static_assert(!NamedQuantitySpec<decltype(speed * time)>);
|
||||
static_assert(DerivedQuantitySpec<decltype(speed * time)>);
|
||||
|
||||
// derived QuantitySpec expression template syntax verification
|
||||
static_assert(is_of_type<1 / time, derived_quantity_spec<dimensionless_, per<time_>>>);
|
||||
static_assert(is_of_type<1 / (1 / time), time_>);
|
||||
|
||||
static_assert(is_of_type<dimensionless * time, time_>);
|
||||
static_assert(is_of_type<time * dimensionless, time_>);
|
||||
static_assert(is_of_type<dimensionless * (1 / time), derived_quantity_spec<dimensionless_, per<time_>>>);
|
||||
static_assert(is_of_type<1 / time * dimensionless, derived_quantity_spec<dimensionless_, per<time_>>>);
|
||||
|
||||
static_assert(is_of_type<length * time, derived_quantity_spec<length_, time_>>);
|
||||
static_assert(is_of_type<length * length, derived_quantity_spec<units::power<length_, 2>>>);
|
||||
|
||||
static_assert(is_of_type<length * length * time, derived_quantity_spec<units::power<length_, 2>, time_>>);
|
||||
static_assert(is_of_type<length * time * length, derived_quantity_spec<units::power<length_, 2>, time_>>);
|
||||
|
||||
static_assert(is_of_type<length*(time* length), derived_quantity_spec<units::power<length_, 2>, time_>>);
|
||||
static_assert(is_of_type<time*(length* length), derived_quantity_spec<units::power<length_, 2>, time_>>);
|
||||
|
||||
static_assert(is_of_type<1 / time * length, derived_quantity_spec<length_, per<time_>>>);
|
||||
static_assert(is_of_type<1 / time * time, dimensionless_>);
|
||||
|
||||
static_assert(is_of_type<time / dimensionless, time_>);
|
||||
static_assert(is_of_type<1 / time / dimensionless, derived_quantity_spec<dimensionless_, per<time_>>>);
|
||||
|
||||
static_assert(is_of_type<length / time * time, length_>);
|
||||
static_assert(is_of_type<1 / time * (1 / time), derived_quantity_spec<dimensionless_, per<units::power<time_, 2>>>>);
|
||||
static_assert(is_of_type<1 / (time * time), derived_quantity_spec<dimensionless_, per<units::power<time_, 2>>>>);
|
||||
static_assert(is_of_type<1 / (1 / (time * time)), derived_quantity_spec<units::power<time_, 2>>>);
|
||||
|
||||
static_assert(is_of_type<length / time * (1 / time), derived_quantity_spec<length_, per<units::power<time_, 2>>>>);
|
||||
static_assert(is_of_type<length / time*(length / time),
|
||||
derived_quantity_spec<units::power<length_, 2>, per<units::power<time_, 2>>>>);
|
||||
static_assert(is_of_type<length / time*(time / length), dimensionless_>);
|
||||
|
||||
static_assert(is_of_type<speed / acceleration, derived_quantity_spec<speed_, per<acceleration_>>>);
|
||||
static_assert(is_of_type<(speed / acceleration).dimension, dim_time_>);
|
||||
static_assert(is_of_type<acceleration / speed, derived_quantity_spec<acceleration_, per<speed_>>>);
|
||||
static_assert(is_of_type<(acceleration / speed).dimension, derived_dimension<dim_one_, per<dim_time_>>>);
|
||||
static_assert(is_of_type<speed * speed / length, derived_quantity_spec<units::power<speed_, 2>, per<length_>>>);
|
||||
static_assert(
|
||||
is_of_type<(speed * speed / length).dimension, derived_dimension<dim_length_, per<units::power<dim_time_, 2>>>>);
|
||||
static_assert(is_of_type<1 / (speed * speed) * length, derived_quantity_spec<length_, per<units::power<speed_, 2>>>>);
|
||||
static_assert(is_of_type<(1 / (speed * speed) * length).dimension,
|
||||
derived_dimension<units::power<dim_time_, 2>, per<dim_length_>>>);
|
||||
|
||||
static_assert(is_of_type<(length * length) * (time * time),
|
||||
derived_quantity_spec<units::power<length_, 2>, units::power<time_, 2>>>);
|
||||
static_assert(is_of_type<(time * time) * (length * length),
|
||||
derived_quantity_spec<units::power<length_, 2>, units::power<time_, 2>>>);
|
||||
|
||||
static_assert(is_of_type<length * time * time, derived_quantity_spec<length_, units::power<time_, 2>>>);
|
||||
static_assert(
|
||||
is_of_type<mass / length / time / time, derived_quantity_spec<mass_, per<length_, units::power<time_, 2>>>>);
|
||||
static_assert(
|
||||
is_of_type<mass / (length * time * time), derived_quantity_spec<mass_, per<length_, units::power<time_, 2>>>>);
|
||||
static_assert(
|
||||
is_of_type<mass / length / (time * time), derived_quantity_spec<mass_, per<length_, units::power<time_, 2>>>>);
|
||||
|
||||
static_assert(is_of_type<force / area, derived_quantity_spec<force_, per<area_>>>);
|
||||
static_assert(
|
||||
is_of_type<(force / area).dimension, derived_dimension<dim_mass_, per<dim_length_, units::power<dim_time_, 2>>>>);
|
||||
|
||||
template<auto& t>
|
||||
concept invalid_operations = requires {
|
||||
requires !requires { t < t; };
|
||||
requires !requires { t / 2; };
|
||||
requires !requires { 2 * t; };
|
||||
requires !requires { t * 2; };
|
||||
requires !requires { t + 2; };
|
||||
requires !requires { 2 + t; };
|
||||
requires !requires { t + t; };
|
||||
requires !requires { t - 2; };
|
||||
requires !requires { 2 - t; };
|
||||
requires !requires { t - t; };
|
||||
requires !requires { t == 2; };
|
||||
requires !requires { 2 == t; };
|
||||
requires !requires { t < 2; };
|
||||
requires !requires { 2 < t; };
|
||||
requires !requires { t + time[second]; };
|
||||
requires !requires { t - time[second]; };
|
||||
requires !requires { t* time[second]; };
|
||||
requires !requires { t / time[second]; };
|
||||
requires !requires { t == time[second]; };
|
||||
requires !requires { t < time[second]; };
|
||||
requires !requires { time[second] + t; };
|
||||
requires !requires { time[second] - t; };
|
||||
requires !requires { time[second] * t; };
|
||||
requires !requires { time[second] / t; };
|
||||
requires !requires { time[second] == t; };
|
||||
requires !requires { time[second] < t; };
|
||||
requires !requires { t + 1 * time[second]; };
|
||||
requires !requires { t - 1 * time[second]; };
|
||||
requires !requires { t * 1 * time[second]; };
|
||||
requires !requires { t / 1 * time[second]; };
|
||||
requires !requires { t == 1 * time[second]; };
|
||||
requires !requires { t == 1 * time[second]; };
|
||||
requires !requires { 1 * time[second] + t; };
|
||||
requires !requires { 1 * time[second] - t; };
|
||||
requires !requires { 1 * time[second] * t; };
|
||||
requires !requires { 1 * time[second] == t; };
|
||||
requires !requires { 1 * time[second] < t; };
|
||||
};
|
||||
static_assert(invalid_operations<time>);
|
||||
|
||||
// comparisons of the same dimensions
|
||||
static_assert(length == length);
|
||||
static_assert(speed == speed);
|
||||
|
||||
// comparisons of equivalent dimensions (named vs unnamed/derived)
|
||||
static_assert(length / length == dimensionless);
|
||||
|
||||
static_assert(1 / time != frequency);
|
||||
static_assert(interconvertible(1 / time, frequency));
|
||||
static_assert(1 / frequency != time);
|
||||
static_assert(interconvertible(1 / frequency, time));
|
||||
static_assert(frequency * time != dimensionless);
|
||||
static_assert(interconvertible(frequency * time, dimensionless));
|
||||
static_assert(is_of_type<common_quantity_spec(1 / time, frequency), frequency_>);
|
||||
static_assert(is_of_type<common_quantity_spec(frequency, 1 / time), frequency_>);
|
||||
|
||||
static_assert(length * length != area);
|
||||
static_assert(interconvertible(length * length, area));
|
||||
static_assert(length * length != volume);
|
||||
static_assert(!interconvertible(length * length, volume));
|
||||
static_assert(area / length != length);
|
||||
static_assert(interconvertible(area / length, length));
|
||||
static_assert(is_of_type<common_quantity_spec(length* length, area), area_>);
|
||||
static_assert(is_of_type<common_quantity_spec(area, length* length), area_>);
|
||||
|
||||
static_assert(length * length * length != volume);
|
||||
static_assert(interconvertible(length * length * length, volume));
|
||||
static_assert(area * length != volume);
|
||||
static_assert(interconvertible(area * length, volume));
|
||||
static_assert(volume / length != area);
|
||||
static_assert(interconvertible(volume / length, area));
|
||||
static_assert(volume / length / length != length);
|
||||
static_assert(interconvertible(volume / length / length, length));
|
||||
static_assert(area * area / length != volume);
|
||||
static_assert(interconvertible(area * area / length, volume));
|
||||
static_assert(area * (area / length) != volume);
|
||||
static_assert(interconvertible(area * (area / length), volume));
|
||||
static_assert(volume / (length * length) != length);
|
||||
static_assert(interconvertible(volume / (length * length), length));
|
||||
|
||||
// TODO Can we improve the below so the `position_vector / time` is convertible only to `velocity` but not `speed`?
|
||||
static_assert(length / time != speed);
|
||||
static_assert(interconvertible(length / time, speed));
|
||||
static_assert(position_vector / time != speed);
|
||||
static_assert(interconvertible(position_vector / time, speed));
|
||||
static_assert(length / time != velocity);
|
||||
static_assert(interconvertible(length / time, velocity));
|
||||
static_assert(position_vector / time != velocity);
|
||||
static_assert(interconvertible(position_vector / time, velocity));
|
||||
|
||||
static_assert(length * time != speed);
|
||||
static_assert(!interconvertible(length * time, speed));
|
||||
static_assert(length / time / time != speed);
|
||||
static_assert(!interconvertible(length / time / time, speed));
|
||||
static_assert(length / speed != time);
|
||||
static_assert(interconvertible(length / speed, time));
|
||||
static_assert(speed * time != length);
|
||||
static_assert(interconvertible(speed * time, length));
|
||||
static_assert(is_of_type<common_quantity_spec(length / time, speed), speed_>);
|
||||
static_assert(is_of_type<common_quantity_spec(speed, length / time), speed_>);
|
||||
static_assert(is_of_type<common_quantity_spec(length / time, length / time), decltype(length / time)>);
|
||||
static_assert(is_of_type<common_quantity_spec(length / time, 1 / (time / length)), decltype(length / time)>);
|
||||
|
||||
static_assert(length / time / time != acceleration);
|
||||
static_assert(interconvertible(length / time / time, acceleration));
|
||||
static_assert(length / (time * time) != acceleration);
|
||||
static_assert(interconvertible(length / (time * time), acceleration));
|
||||
static_assert(speed / time != acceleration);
|
||||
static_assert(interconvertible(speed / time, acceleration));
|
||||
static_assert(speed / acceleration != time);
|
||||
static_assert(interconvertible(speed / acceleration, time));
|
||||
static_assert(acceleration * time != speed);
|
||||
static_assert(interconvertible(acceleration * time, speed));
|
||||
static_assert(acceleration * (time * time) != length);
|
||||
static_assert(interconvertible(acceleration * (time * time), length));
|
||||
static_assert(acceleration / speed != frequency);
|
||||
static_assert(interconvertible(acceleration / speed, frequency));
|
||||
|
||||
// comparison of convertible named dimensions
|
||||
static_assert(velocity != speed);
|
||||
static_assert(!interconvertible(speed, velocity));
|
||||
|
||||
// comparison of convertible unnamed dimensions
|
||||
static_assert(is_of_type<mass * acceleration, derived_quantity_spec<acceleration_, mass_>>);
|
||||
static_assert(is_of_type<(mass * acceleration).dimension,
|
||||
derived_dimension<dim_length_, dim_mass_, per<units::power<dim_time_, 2>>>>);
|
||||
static_assert(is_of_type<acceleration * mass, derived_quantity_spec<acceleration_, mass_>>);
|
||||
static_assert(is_of_type<(acceleration * mass).dimension,
|
||||
derived_dimension<dim_length_, dim_mass_, per<units::power<dim_time_, 2>>>>);
|
||||
static_assert(mass * acceleration == acceleration * mass);
|
||||
static_assert(interconvertible(mass * acceleration, acceleration* mass));
|
||||
|
||||
// comparisons of equivalent but not convertible dimensions
|
||||
static_assert(energy != torque);
|
||||
static_assert(!interconvertible(energy, torque));
|
||||
|
||||
static_assert(force * length != energy);
|
||||
static_assert(force * length != torque);
|
||||
static_assert(interconvertible(force * length, energy));
|
||||
static_assert(interconvertible(force * length, torque));
|
||||
template<auto T1, auto T2>
|
||||
concept no_common_type = requires {
|
||||
requires !requires { typename std::common_type_t<decltype(T1), decltype(T2)>; };
|
||||
requires !requires { typename std::common_type_t<decltype(T2), decltype(T1)>; };
|
||||
};
|
||||
static_assert(no_common_type<energy, torque>);
|
||||
|
||||
static_assert(frequency != action);
|
||||
static_assert(!interconvertible(frequency, action));
|
||||
static_assert(no_common_type<frequency, action>);
|
||||
|
||||
// dimensionless
|
||||
static_assert(power / power != efficiency);
|
||||
static_assert(interconvertible(power / power, efficiency));
|
||||
static_assert(dimensionless != efficiency);
|
||||
|
||||
static_assert(efficiency != strain);
|
||||
static_assert(!interconvertible(efficiency, strain));
|
||||
|
||||
static_assert(stress / stress != strain);
|
||||
static_assert(stress / stress != efficiency);
|
||||
static_assert(interconvertible(stress / stress, strain));
|
||||
static_assert(interconvertible(stress / stress, efficiency));
|
||||
|
||||
// comparison of not equivalent dimensions
|
||||
static_assert(length != time);
|
||||
static_assert(!interconvertible(length, time));
|
||||
|
||||
static_assert(acceleration != speed);
|
||||
static_assert(!interconvertible(acceleration, speed));
|
||||
|
||||
// power
|
||||
static_assert(is_of_type<pow<2>(length), derived_quantity_spec<units::power<length_, 2>>>);
|
||||
static_assert(is_of_type<pow<1, 2>(length), derived_quantity_spec<units::power<length_, 1, 2>>>);
|
||||
static_assert(is_of_type<pow<1, 2>(length* length), length_>);
|
||||
static_assert(is_of_type<pow<1, 3>(length* length* length), length_>);
|
||||
static_assert(is_of_type<pow<1, 3>(length* length), derived_quantity_spec<units::power<length_, 2, 3>>>);
|
||||
static_assert(is_of_type<pow<1, 2>(length / time),
|
||||
derived_quantity_spec<units::power<length_, 1, 2>, per<units::power<time_, 1, 2>>>>);
|
||||
static_assert(
|
||||
is_of_type<pow<1, 2>(length / (time * time)), derived_quantity_spec<units::power<length_, 1, 2>, per<time_>>>);
|
||||
|
||||
static_assert(is_same_v<decltype(pow<2>(length)), decltype(length * length)>);
|
||||
static_assert(is_same_v<decltype(pow<2>(length / time)), decltype(length * length / time / time)>);
|
||||
|
||||
} // namespace
|
@@ -23,6 +23,7 @@
|
||||
#include "test_tools.h"
|
||||
#include <units/dimension.h>
|
||||
#include <units/quantity.h>
|
||||
#include <units/quantity_spec.h>
|
||||
#include <units/reference.h>
|
||||
#include <units/si/prefixes.h>
|
||||
#include <units/system_reference.h>
|
||||
@@ -31,30 +32,34 @@
|
||||
namespace {
|
||||
|
||||
using namespace units;
|
||||
using namespace units::detail;
|
||||
|
||||
using dimension_one_ = struct dimension_one;
|
||||
using dimensionless_ = struct dimensionless;
|
||||
using one_ = struct one;
|
||||
|
||||
// base dimensions
|
||||
BASE_DIMENSION_(length, "L");
|
||||
BASE_DIMENSION_(time, "T");
|
||||
BASE_DIMENSION_(mass, "M");
|
||||
|
||||
DERIVED_DIMENSION_(frequency, decltype(1 / time));
|
||||
DERIVED_DIMENSION_(action, decltype(1 / time));
|
||||
DERIVED_DIMENSION_(area, decltype(length * length));
|
||||
DERIVED_DIMENSION_(volume, decltype(area * length));
|
||||
DERIVED_DIMENSION_(speed, decltype(length / time));
|
||||
DERIVED_DIMENSION_(acceleration, decltype(speed / time));
|
||||
DERIVED_DIMENSION_(force, decltype(mass * acceleration));
|
||||
DERIVED_DIMENSION_(moment_of_force, decltype(length * force));
|
||||
DERIVED_DIMENSION_(torque, decltype(moment_of_force));
|
||||
DERIVED_DIMENSION_(power, decltype(force * speed));
|
||||
DERIVED_DIMENSION_(efficiency, decltype(power / power));
|
||||
DERIVED_DIMENSION_(energy, decltype(force * length));
|
||||
|
||||
// clang-format off
|
||||
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;
|
||||
|
||||
// quantities specification
|
||||
QUANTITY_SPEC_(length, dim_length);
|
||||
QUANTITY_SPEC_(mass, dim_mass);
|
||||
QUANTITY_SPEC_(time, dim_time);
|
||||
|
||||
QUANTITY_SPEC_(frequency, 1 / time);
|
||||
QUANTITY_SPEC_(action, 1 / time);
|
||||
QUANTITY_SPEC_(area, length* length);
|
||||
QUANTITY_SPEC_(volume, area* length);
|
||||
QUANTITY_SPEC_(speed, length / time);
|
||||
QUANTITY_SPEC_(acceleration, speed / time);
|
||||
QUANTITY_SPEC_(force, mass* acceleration);
|
||||
QUANTITY_SPEC_(moment_of_force, length* force);
|
||||
QUANTITY_SPEC_(torque, moment_of_force);
|
||||
QUANTITY_SPEC_(power, force* speed);
|
||||
QUANTITY_SPEC_(efficiency, power / power);
|
||||
QUANTITY_SPEC_(energy, force* length);
|
||||
|
||||
// base units
|
||||
inline constexpr struct second_ : named_unit<"s", time> {} second;
|
||||
inline constexpr struct metre_ : named_unit<"m", length> {} metre;
|
||||
@@ -102,15 +107,15 @@ static_assert(is_same_v<decltype(5 * speed[metre / second]),
|
||||
static_assert(
|
||||
is_same_v<
|
||||
decltype(10 * length[metre] / (2 * time[second])),
|
||||
quantity<reference<derived_dimension<length_, per<time_>>{}, derived_unit<metre_, per<second_>>{}>{}, int>>);
|
||||
quantity<reference<derived_quantity_spec<length_, per<time_>>{}, derived_unit<metre_, per<second_>>{}>{}, int>>);
|
||||
|
||||
// Base quantity as a result of dimensional transformation
|
||||
static_assert(
|
||||
is_same_v<decltype(5 * speed[metre / second] * (5 * time[second])), quantity<reference<length, metre>{}, int>>);
|
||||
static_assert(is_same_v<decltype(5 * speed[metre / second] * (5 * time[second])),
|
||||
quantity<reference<derived_quantity_spec<speed_, time_>{}, metre>{}, int>>);
|
||||
|
||||
// dimension_one
|
||||
// dimensionless
|
||||
static_assert(is_same_v<decltype(20 * speed[metre / second] / (10 * length[metre]) * (5 * time[second])),
|
||||
quantity<reference<dimension_one, one>{}, int>>);
|
||||
quantity<reference<derived_quantity_spec<speed_, time_, per<length_>>{}, one>{}, int>>);
|
||||
|
||||
template<auto s>
|
||||
concept invalid_operations = requires {
|
||||
@@ -142,11 +147,11 @@ static_assert(invalid_operations<time[second]>);
|
||||
static_assert(
|
||||
is_same_v<
|
||||
decltype(2 * length[metre] / (1 * time[second])),
|
||||
quantity<reference<derived_dimension<length_, per<time_>>{}, derived_unit<metre_, per<second_>>{}>{}, int>>);
|
||||
quantity<reference<derived_quantity_spec<length_, per<time_>>{}, derived_unit<metre_, per<second_>>{}>{}, int>>);
|
||||
static_assert(
|
||||
is_same_v<
|
||||
decltype(2 * (length[metre] / time[second])),
|
||||
quantity<reference<derived_dimension<length_, per<time_>>{}, derived_unit<metre_, per<second_>>{}>{}, int>>);
|
||||
quantity<reference<derived_quantity_spec<length_, per<time_>>{}, derived_unit<metre_, per<second_>>{}>{}, int>>);
|
||||
static_assert(is_same_v<decltype(2 * (speed[metre / second])),
|
||||
quantity<reference<speed, derived_unit<metre_, per<second_>>{}>{}, int>>);
|
||||
|
||||
@@ -157,7 +162,7 @@ static_assert(
|
||||
static_assert(
|
||||
is_same_v<
|
||||
decltype(120 * length[kilometre] / (2 * time[hour])),
|
||||
quantity<reference<derived_dimension<length_, per<time_>>{}, derived_unit<kilometre_, per<hour_>>{}>{}, int>>);
|
||||
quantity<reference<derived_quantity_spec<length_, per<time_>>{}, derived_unit<kilometre_, per<hour_>>{}>{}, int>>);
|
||||
static_assert(120 * length[kilometre] / (2 * time[hour]) == 60 * speed[kilometre / hour]);
|
||||
static_assert(
|
||||
is_same_v<
|
||||
@@ -166,14 +171,14 @@ static_assert(
|
||||
const auto duration = 2;
|
||||
return distance * length[kilometre] / (duration * time[hour]);
|
||||
}()),
|
||||
quantity<reference<derived_dimension<length_, per<time_>>{}, derived_unit<kilometre_, per<hour_>>{}>{}, int>>);
|
||||
quantity<reference<derived_quantity_spec<length_, per<time_>>{}, derived_unit<kilometre_, per<hour_>>{}>{}, int>>);
|
||||
static_assert(
|
||||
is_same_v<decltype(std::int64_t{120} * length[kilometre] / (2 * time[hour])),
|
||||
quantity<reference<derived_dimension<length_, per<time_>>{}, derived_unit<kilometre_, per<hour_>>{}>{},
|
||||
quantity<reference<derived_quantity_spec<length_, per<time_>>{}, derived_unit<kilometre_, per<hour_>>{}>{},
|
||||
std::int64_t>>);
|
||||
static_assert(
|
||||
is_same_v<decltype(120.L * length[kilometre] / (2 * time[hour])),
|
||||
quantity<reference<derived_dimension<length_, per<time_>>{}, derived_unit<kilometre_, per<hour_>>{}>{},
|
||||
quantity<reference<derived_quantity_spec<length_, per<time_>>{}, derived_unit<kilometre_, per<hour_>>{}>{},
|
||||
long double>>);
|
||||
|
||||
static_assert(is_same_v<decltype(1. / 4 * area[square<metre>]), decltype(1. * area[square<metre>] / 4)>);
|
||||
@@ -185,13 +190,13 @@ static_assert(is_same_v<decltype(42 * nu::time[nu::minute]), quantity<reference<
|
||||
static_assert(is_same_v<decltype(42 * nu::length[nu::second]), quantity<reference<length, nu::second>{}, int>>);
|
||||
static_assert(is_same_v<decltype(42 * nu::length[nu::minute]), quantity<reference<length, nu::minute>{}, int>>);
|
||||
static_assert(is_same_v<decltype(42 * (nu::length[nu::second] / nu::time[nu::second])),
|
||||
quantity<reference<derived_dimension<length_, per<time_>>{}, one>{}, int>>);
|
||||
quantity<reference<derived_quantity_spec<length_, per<time_>>{}, one>{}, int>>);
|
||||
static_assert(is_same_v<decltype(42 * nu::length[nu::second] / (42 * nu::time[nu::second])),
|
||||
quantity<reference<derived_dimension<length_, per<time_>>{}, one>{}, int>>);
|
||||
quantity<reference<derived_quantity_spec<length_, per<time_>>{}, one>{}, int>>);
|
||||
static_assert(is_same_v<decltype(42 * nu::speed[nu::second / nu::second]), quantity<reference<speed, one>{}, int>>);
|
||||
static_assert(is_same_v<decltype(42 * nu::speed[one]), quantity<reference<speed, one>{}, int>>);
|
||||
static_assert(is_same_v<decltype(42 * mass[kilogram] * (1 * nu::length[nu::second]) / (1 * nu::time[nu::second])),
|
||||
quantity<reference<derived_dimension<length_, mass_, per<time_>>{}, kilogram>{}, int>>);
|
||||
quantity<reference<derived_quantity_spec<length_, mass_, per<time_>>{}, kilogram>{}, int>>);
|
||||
|
||||
template<auto dim, auto unit>
|
||||
concept invalid_nu_unit = !requires { dim[unit]; };
|
||||
|
@@ -139,22 +139,14 @@ inline constexpr bool is_of_type = std::is_same_v<std::remove_cvref_t<decltype(V
|
||||
|
||||
#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 { \
|
||||
#define QUANTITY_SPEC_(name, ...) \
|
||||
inline constexpr struct name##_ : quantity_spec<##__VA_ARGS__> { \
|
||||
} 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> { \
|
||||
#define QUANTITY_SPEC_(name, ...) \
|
||||
inline constexpr struct name##_ : quantity_spec<name##_, ##__VA_ARGS__> { \
|
||||
} name
|
||||
|
||||
#endif
|
||||
|
@@ -35,12 +35,18 @@ using namespace units::detail;
|
||||
using one_ = struct one;
|
||||
|
||||
// base dimensions
|
||||
BASE_DIMENSION_(length, "L");
|
||||
BASE_DIMENSION_(time, "T");
|
||||
BASE_DIMENSION_(mass, "M");
|
||||
BASE_DIMENSION_(thermodynamic_temperature, "Θ");
|
||||
|
||||
// clang-format off
|
||||
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 dim_thermodynamic_temperature_ : base_dimension<basic_symbol_text{"Θ", "O"}> {} dim_thermodynamic_temperature;
|
||||
|
||||
// quantities specification
|
||||
QUANTITY_SPEC_(length, dim_length);
|
||||
QUANTITY_SPEC_(mass, dim_mass);
|
||||
QUANTITY_SPEC_(time, dim_time);
|
||||
QUANTITY_SPEC_(thermodynamic_temperature, dim_thermodynamic_temperature);
|
||||
|
||||
// base units
|
||||
inline constexpr struct second_ : named_unit<"s", time> {} second;
|
||||
inline constexpr struct metre_ : named_unit<"m", length> {} metre;
|
||||
|
Reference in New Issue
Block a user