feat: quantity_spec support added

This commit is contained in:
Mateusz Pusz
2022-12-16 18:15:48 +01:00
parent e0101f14d9
commit 92c70f1a4e
32 changed files with 1524 additions and 827 deletions

View File

@ -34,15 +34,14 @@
namespace { namespace {
using namespace units; using namespace units;
using namespace units::si;
using namespace units::si::unit_symbols; 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]; inline constexpr auto air_density = 1.225 * isq::mass_density[kg / m3];
class Box { class Box {
quantity<isq::area[m2]> base_; quantity<isq::area[m2]> base_;
quantity<isq::length[m]> height_; quantity<isq::height[m]> height_;
quantity<isq::mass_density[kg / m3]> density_ = air_density; quantity<isq::mass_density[kg / m3]> density_ = air_density;
public: public:
constexpr Box(const quantity<isq::length[m]>& length, const quantity<isq::length[m]>& width, 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 weak_quantity_of<isq::volume> auto volume = base_ * height_;
const quantity_of<isq::mass> auto mass = density_ * volume; const weak_quantity_of<isq::mass> auto mass = density_ * volume;
return mass * g; return quantity_cast<isq::weight>(mass * g);
} }
[[nodiscard]] constexpr quantity<isq::length[m]> fill_level(const quantity<isq::mass[kg]>& measured_mass) const [[nodiscard]] constexpr quantity<isq::length[m]> fill_level(const quantity<isq::mass[kg]>& measured_mass) const
@ -80,7 +79,9 @@ public:
int main() 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]; const auto height = (200.0 * mm)[metre];
auto box = Box(1000.0 * mm, 500.0 * mm, height); auto box = Box(1000.0 * mm, 500.0 * mm, height);

View File

@ -152,7 +152,7 @@ public:
leg(const waypoint& b, const waypoint& e) noexcept : begin_(&b), end_(&e) {} leg(const waypoint& b, const waypoint& e) noexcept : begin_(&b), end_(&e) {}
constexpr const waypoint& begin() const { return *begin_; }; constexpr const waypoint& begin() const { return *begin_; };
constexpr const waypoint& end() const { return *end_; } 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>; using legs = std::vector<leg>;

View File

@ -30,7 +30,7 @@
using namespace units; 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); return quantity_cast<isq::speed>(d / t);
} }
@ -42,8 +42,8 @@ int main()
constexpr auto v1 = 110 * isq::speed[km / h]; constexpr auto v1 = 110 * isq::speed[km / h];
constexpr auto v2 = 70. * isq::speed[mph]; constexpr auto v2 = 70. * isq::speed[mph];
constexpr auto v3 = avg_speed(220 * isq::length[km], 2 * isq::time[h]); constexpr auto v3 = avg_speed(220 * isq::distance[km], 2 * isq::duration[h]);
constexpr auto v4 = avg_speed(quantity<isq::length[mi]>{140}, quantity<isq::time[h]>{2}); 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 v5 = quantity_cast<quantity<isq::speed[m / s]>>(v3);
constexpr auto v6 = quantity_cast<m / s>(v4); constexpr auto v6 = quantity_cast<m / s>(v4);
constexpr auto v7 = quantity_cast<int>(v6); constexpr auto v7 = quantity_cast<int>(v6);

View File

@ -42,18 +42,19 @@ add_library(
include/units/customization_points.h include/units/customization_points.h
include/units/dimension.h include/units/dimension.h
# include/units/generic/angle.h # include/units/generic/angle.h
# include/units/generic/dimensionless.h include/units/generic/dimensionless.h
# include/units/generic/solid_angle.h # include/units/generic/solid_angle.h
# include/units/kind.h # include/units/kind.h
include/units/magnitude.h include/units/magnitude.h
# include/units/math.h include/units/math.h
# include/units/point_origin.h # include/units/point_origin.h
include/units/quantity.h include/units/quantity.h
# include/units/quantity_cast.h include/units/quantity_cast.h
# include/units/quantity_kind.h # include/units/quantity_kind.h
# include/units/quantity_point.h # include/units/quantity_point.h
# include/units/quantity_point_kind.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/ratio.h
include/units/reference.h include/units/reference.h
include/units/symbol_text.h include/units/symbol_text.h

View File

@ -24,6 +24,7 @@
#include <units/bits/external/hacks.h> // IWYU pragma: keep #include <units/bits/external/hacks.h> // IWYU pragma: keep
#include <compare> #include <compare>
#include <initializer_list>
#include <iterator> #include <iterator>
#include <ranges> #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()); 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> template<class I, class O>
struct in_out_result { struct in_out_result {
[[no_unique_address]] I in; [[no_unique_address]] I in;

View File

@ -104,6 +104,19 @@ struct power {
namespace detail { 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> template<typename T>
inline constexpr bool is_specialization_of_power = false; 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 // expr_less
template<typename Lhs, typename Rhs, template<typename, typename> typename Pred> template<typename Lhs, typename Rhs, template<typename, typename> typename Pred>
struct expr_less_impl : Pred<Lhs, Rhs> {}; struct expr_less_impl : Pred<expr_type<Lhs>, expr_type<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> {};
template<typename T, int... Ints, template<typename, typename> typename Pred> template<typename T, int... Ints, template<typename, typename> typename Pred>
struct expr_less_impl<T, power<T, Ints...>, Pred> : std::true_type {}; 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 { concept expr_projectable = requires {
typename T::_num_; typename T::_num_;
typename T::_den_; 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::_num_, Proj>;
requires expr_projectable_impl<typename T::_den_, 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<template<typename> typename Proj, template<typename...> typename To, typename OneType,
template<typename, typename> typename Pred, expr_type_projectable<Proj>... Nums, template<typename, typename> typename Pred, expr_type_projectable<Proj>... Nums,
expr_type_projectable<Proj>... Dens> expr_type_projectable<Proj>... Dens>
[[nodiscard]] consteval auto expr_map_impl(type_list<Nums...>, type_list<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>; return (OneType{} * ... * map_power(typename expr_type_map<std::remove_const_t<Nums>, Proj>::type{})) /
using dens = type_list_sort<type_list<typename expr_type_map<std::remove_const_t<Dens>, Proj>::type...>, Pred>; (OneType{} * ... * map_power(typename expr_type_map<std::remove_const_t<Dens>, Proj>::type{}));
return detail::get_optimized_expression<nums, dens, OneType, Pred, To>();
} }
/** /**
@ -532,7 +546,10 @@ template<template<typename> typename Proj, template<typename...> typename To, ty
template<typename, typename> typename Pred, expr_projectable<Proj> T> template<typename, typename> typename Pred, expr_projectable<Proj> T>
[[nodiscard]] consteval auto expr_map(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 } // namespace detail

View File

@ -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); }; concept is_derived_from_specialization_of = requires(T* t) { detail::to_base_specialization_of<Type>(t); };
template<typename T, typename... Ts> template<typename T, typename... Ts>
concept one_of = (... || std::same_as<T, Ts>); concept one_of = (false || ... || std::same_as<T, Ts>);
} // namespace units } // namespace units

View File

@ -22,17 +22,14 @@
#pragma once #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/bits/external/type_traits.h>
#include <units/dimension.h> #include <units/dimension.h>
#include <units/quantity_spec.h>
#include <units/unit.h> #include <units/unit.h>
namespace units { namespace units {
template<Dimension auto D, Unit auto U> template<QuantitySpec auto Q, Unit auto U>
struct reference; struct reference;
namespace detail { namespace detail {
@ -40,8 +37,8 @@ namespace detail {
template<typename T> template<typename T>
inline constexpr bool is_specialization_of_reference = false; inline constexpr bool is_specialization_of_reference = false;
template<Dimension auto D, Unit auto U> template<auto Q, auto U>
inline constexpr bool is_specialization_of_reference<reference<D, U>> = true; inline constexpr bool is_specialization_of_reference<reference<Q, U>> = true;
} // namespace detail } // namespace detail
@ -92,11 +89,15 @@ class quantity;
namespace detail { namespace detail {
// TODO make the below code from the comment to compile and replace it
template<auto R, typename Rep> template<auto R, typename Rep>
inline constexpr bool is_quantity<quantity<R, Rep>> = true; 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> // 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; // inline constexpr bool is_quantity<T> = true;
} // namespace detail } // namespace detail
@ -109,6 +110,7 @@ inline constexpr bool is_quantity<quantity<R, Rep>> = true;
*/ */
template<typename Q, auto V> template<typename Q, auto V>
concept quantity_of = Quantity<Q> && ((Dimension<std::remove_const_t<decltype(V)>> && Q::dimension == 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)); (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> template<typename Q, auto V>
concept weak_quantity_of = Quantity<Q> && concept weak_quantity_of = Quantity<Q> &&
((Dimension<std::remove_const_t<decltype(V)>> && interconvertible(Q::dimension, V)) || ((Dimension<std::remove_const_t<decltype(V)>> && Q::dimension == V) ||
(Reference<std::remove_const_t<decltype(V)>> && (QuantitySpec<std::remove_const_t<decltype(V)>> && interconvertible(Q::quantity_spec, V)) ||
interconvertible(Q::dimension, V.dimension) && Q::unit == V.unit)); (Reference<std::remove_const_t<decltype(V)>> && Q::dimension == V.dimension &&
Q::unit == V.unit));
} // namespace units } // namespace units

View File

@ -23,49 +23,11 @@
#pragma once #pragma once
#include <units/bits/expression_template.h> #include <units/bits/expression_template.h>
#include <units/bits/external/fixed_string.h>
#include <units/bits/external/type_traits.h> #include <units/bits/external/type_traits.h>
#include <units/unit.h> #include <units/symbol_text.h>
namespace units { 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 * @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 * being mutually independent since a base quantity cannot be expressed as a product of powers of the other base
* quantities. * quantities.
* *
* Symbol template parameters is an unique identifier of the base dimension. The same identifiers can be multiplied * `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 * 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). * (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. * User should derive a strong type from this class template rather than use it directly in the source code.
* For example: * For example:
* *
* @code{.cpp} * @code{.cpp}
* inline constexpr struct length : base_dimension<"L"> {} length; * inline constexpr struct dim_length : base_dimension<"L"> {} dim_length;
* inline constexpr struct time : base_dimension<"T"> {} time; * inline constexpr struct dim_time : base_dimension<"T"> {} dim_time;
* inline constexpr struct mass : base_dimension<"M"> {} mass; * inline constexpr struct dim_mass : base_dimension<"M"> {} dim_mass;
* @endcode * @endcode
* *
* @note A common convention in this library is to assign the same name for a type and an object of this type. * @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 * @tparam Symbol an unique identifier of the base dimension used to provide dimensional analysis support
*/ */
#ifdef __cpp_explicit_this_parameter template<basic_symbol_text Symbol>
template<basic_fixed_string Symbol>
#else
template<typename Self, basic_fixed_string Symbol>
#endif
struct base_dimension { struct base_dimension {
static constexpr auto symbol = Symbol; ///< Unique base dimension identifier 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 { 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> template<BaseDimension Lhs, BaseDimension Rhs>
struct base_dimension_less : std::bool_constant<(Lhs::symbol < Rhs::symbol)> {}; 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 } // namespace detail
template<typename T> 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>; 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 * @brief A dimension of a derived quantity
* *
@ -172,99 +128,41 @@ struct derived_dimension_impl : detail::expr_fractions<derived_dimension<>, Ds..
* For example: * For example:
* *
* @code{.cpp} * @code{.cpp}
* inline constexpr struct frequency : decltype(1 / time) {} frequency; * using frequency = decltype(1 / dim_time);
* inline constexpr struct speed : decltype(length / time) {} speed; * using speed = decltype(dim_length / dim_time);
* inline constexpr struct acceleration : decltype(speed / time) {} acceleration; * using acceleration = decltype(dim_speed / dim_time);
* inline constexpr struct force : decltype(mass * acceleration) {} force; * using force = decltype(dim_mass * dim_acceleration);
* inline constexpr struct energy : decltype(force * length) {} energy; * using energy = decltype(dim_force * dim_length);
* inline constexpr struct moment_of_force : decltype(length * force) {} moment_of_force; * using moment_of_force = decltype(dim_length * dim_force);
* inline constexpr struct torque : decltype(moment_of_force) {} torque; * using torque = decltype(dim_moment_of_force);
* @endcode * @endcode
* *
* - `frequency` will be derived from type `derived_dimension<dimension_one, per<time>>` * - `frequency` will be derived from type `derived_dimension<dimension_one, per<dim_time>>`
* - `speed` will be derived from type `derived_dimension<length, per<time>>` * - `speed` will be derived from type `derived_dimension<dim_length, per<dim_time>>`
* - `acceleration` will be derived from type `derived_dimension<length, per<power<time, 2>>>` * - `acceleration` will be derived from type `derived_dimension<dim_length, per<power<dim_time, 2>>>`
* - `force` will be derived from type `derived_dimension<length, mass, per<power<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<length, 2>, mass, per<power<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. * @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 * 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 * 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. * 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 * @tparam Ds a parameter pack consisting tokens allowed in the dimension specification
* (base dimensions, `dimension_one`, `power<Dim, Num, Den>`, `per<...>`) * (base dimensions, `dimension_one`, `power<Dim, Num, Den>`, `per<...>`)
* *
* @note User should not instantiate this type! It is not exported from the C++ module. The library will * @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. * instantiate this type automatically based on the dimensional arithmetic equation provided by the user.
*/ */
#ifdef __cpp_explicit_this_parameter template<DerivedDimensionExpr... Ds>
struct derived_dimension : detail::expr_fractions<derived_dimension<>, Ds...> {};
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
/** /**
* @brief Dimension one * @brief Dimension one
* *
* Dimension for which all the exponents of the factors corresponding to the base * 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<> { inline constexpr struct dimension_one : derived_dimension<> {
} dimension_one; } dimension_one;
@ -274,21 +172,32 @@ namespace detail {
template<> template<>
inline constexpr bool is_dimension_one<struct dimension_one> = true; inline constexpr bool is_dimension_one<struct dimension_one> = true;
template<Dimension T> template<typename... Ds>
struct dim_type_impl { void to_base_specialization_of_derived_dimension(const volatile derived_dimension<Ds...>*);
using type = T;
};
template<DerivedDimension T> template<typename T>
struct dim_type_impl<T> { inline constexpr bool is_derived_from_specialization_of_derived_dimension =
using type = TYPENAME T::_type_; requires(T * t) { to_base_specialization_of_derived_dimension(t); };
};
template<Dimension T>
using dim_type = TYPENAME dim_type_impl<T>::type;
} // namespace detail } // 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 // Operators
@ -296,21 +205,21 @@ template<Dimension Lhs, Dimension Rhs>
[[nodiscard]] consteval Dimension auto operator*(Lhs, Rhs) [[nodiscard]] consteval Dimension auto operator*(Lhs, Rhs)
{ {
return detail::expr_multiply<derived_dimension, struct dimension_one, detail::type_list_of_base_dimension_less>( return detail::expr_multiply<derived_dimension, struct dimension_one, detail::type_list_of_base_dimension_less>(
detail::dim_type<Lhs>{}, detail::dim_type<Rhs>{}); Lhs{}, Rhs{});
} }
template<Dimension Lhs, Dimension Rhs> template<Dimension Lhs, Dimension Rhs>
[[nodiscard]] consteval Dimension auto operator/(Lhs, Rhs) [[nodiscard]] consteval Dimension auto operator/(Lhs, Rhs)
{ {
return detail::expr_divide<derived_dimension, struct dimension_one, detail::type_list_of_base_dimension_less>( return detail::expr_divide<derived_dimension, struct dimension_one, detail::type_list_of_base_dimension_less>(Lhs{},
detail::dim_type<Lhs>{}, detail::dim_type<Rhs>{}); Rhs{});
} }
template<Dimension D> template<Dimension D>
[[nodiscard]] consteval Dimension auto operator/(int value, D) [[nodiscard]] consteval Dimension auto operator/(int value, D)
{ {
gsl_Expects(value == 1); 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> template<Dimension D>
@ -322,31 +231,6 @@ template<Dimension Lhs, Dimension Rhs>
return is_same_v<Lhs, 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 * @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); 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 // TODO consider adding the support for text output of the dimensional equation
} // namespace units } // 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

View File

@ -27,7 +27,7 @@
namespace units { 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> struct one; // defined in <units/unit.h>
// clang-format off // clang-format off

View File

@ -22,10 +22,7 @@
#pragma once #pragma once
#include <units/bits/dimension_op.h>
#include <units/bits/external/hacks.h> #include <units/bits/external/hacks.h>
#include <units/generic/angle.h>
#include <units/generic/dimensionless.h>
#include <units/quantity.h> #include <units/quantity.h>
#include <units/unit.h> #include <units/unit.h>
@ -39,9 +36,9 @@
namespace units { 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 Num Exponent numerator
* @tparam Den Exponent denominator * @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; using rep = TYPENAME Q::rep;
if constexpr (Num == 0) { if constexpr (Num == 0) {
return rep(1); return rep(1);
} else if constexpr (ratio{Num, Den} == 1) {
return q;
} else { } 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; 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)))); 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 * @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 * @param q Quantity being the base of the operation
* @return Quantity The result of computation * @return Quantity The result of computation
@ -77,17 +74,16 @@ template<Quantity Q>
[[nodiscard]] inline Quantity auto sqrt(const Q& q) noexcept [[nodiscard]] inline Quantity auto sqrt(const Q& q) noexcept
requires requires { sqrt(q.number()); } || requires { std::sqrt(q.number()); } 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 rep = TYPENAME Q::rep;
using std::sqrt; 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 * @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 * @param q Quantity being the base of the operation
* @return Quantity The result of computation * @return Quantity The result of computation
@ -96,11 +92,10 @@ template<Quantity Q>
[[nodiscard]] inline Quantity auto cbrt(const Q& q) noexcept [[nodiscard]] inline Quantity auto cbrt(const Q& q) noexcept
requires requires { cbrt(q.number()); } || requires { std::cbrt(q.number()); } 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 rep = TYPENAME Q::rep;
using std::cbrt; 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 * @param q Quantity being the base of the operation
* @return Quantity The value of the same quantity type * @return Quantity The value of the same quantity type
*/ */
template<typename U, typename Rep> template<quantity_of<dimensionless> Q, typename Rep>
[[nodiscard]] inline dimensionless<U, Rep> exp(const dimensionless<U, Rep>& q) [[nodiscard]] inline Q exp(const Q& q)
requires requires { exp(q.number()); } || requires { std::exp(q.number()); } requires requires { exp(q.number()); } || requires { std::exp(q.number()); }
{ {
using std::exp; 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 * @param q Quantity being the base of the operation
* @return Quantity The absolute value of a provided quantity * @return Quantity The absolute value of a provided quantity
*/ */
template<typename D, typename U, typename Rep> template<Quantity Q>
[[nodiscard]] inline quantity<D, U, Rep> abs(const quantity<D, U, Rep>& q) noexcept [[nodiscard]] inline Q abs(const Q& q) noexcept
requires requires { abs(q.number()); } || requires { std::abs(q.number()); } requires requires { abs(q.number()); } || requires { std::abs(q.number()); }
{ {
using std::abs; 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 * @tparam Q Quantity type being the base of the operation
* @return Quantity The epsilon value for quantity's representation type * @return Quantity The epsilon value for quantity's representation type
*/ */
template<Quantity Q> template<Representation Rep, Reference R>
requires requires { std::numeric_limits<typename Q::rep>::epsilon(); } requires requires { std::numeric_limits<Rep>::epsilon(); }
[[nodiscard]] constexpr Quantity auto epsilon() noexcept [[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 * @tparam q Quantity being the base of the operation
* @return Quantity The rounded quantity with unit type To * @return Quantity The rounded quantity with unit type To
*/ */
template<Unit To, typename D, typename U, typename Rep> template<Unit auto To, auto R, typename Rep>
[[nodiscard]] constexpr quantity<D, To, Rep> floor(const quantity<D, U, Rep>& q) noexcept [[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((!treat_as_floating_point<Rep>) || requires { floor(q.number()); } ||
requires { std::floor(q.number()); }) && requires { std::floor(q.number()); }) &&
(std::same_as<To, U> || requires { (To == R.unit || requires {
::units::quantity_cast<To>(q); ::units::quantity_cast<To>(q);
quantity<D, To, Rep>::one(); quantity<reference<R.quantity_spec, To>{}, Rep>::one();
}) })
{ {
const auto handle_signed_results = [&]<typename T>(const T& res) { const auto handle_signed_results = [&]<typename T>(const T& res) {
if (res > q) { if (res > q) {
@ -171,46 +166,34 @@ template<Unit To, typename D, typename U, typename Rep>
}; };
if constexpr (treat_as_floating_point<Rep>) { if constexpr (treat_as_floating_point<Rep>) {
using std::floor; using std::floor;
if constexpr (std::is_same_v<To, U>) { if constexpr (To == R.unit) {
return quantity<D, To, Rep>(floor(q.number())); return quantity<reference<R.quantity_spec, To>{}, Rep>(floor(q.number()));
} else { } 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 { } else {
if constexpr (std::is_same_v<To, U>) { if constexpr (To == R.unit) {
return q; return quantity_cast<To>(q);
} else { } else {
return handle_signed_results(quantity_cast<To>(q)); 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 * @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 * @tparam q Quantity being the base of the operation
* @return Quantity The rounded quantity with unit type To * @return Quantity The rounded quantity with unit type To
*/ */
template<Unit To, typename D, typename U, typename Rep> template<Unit auto To, auto R, typename Rep>
[[nodiscard]] constexpr quantity<D, To, Rep> ceil(const quantity<D, U, Rep>& q) noexcept [[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()); }) && requires((!treat_as_floating_point<Rep>) || requires { ceil(q.number()); } || requires { std::ceil(q.number()); }) &&
(std::same_as<To, U> || requires { (To == R.unit || requires {
::units::quantity_cast<To>(q); ::units::quantity_cast<To>(q);
quantity<D, To, Rep>::one(); quantity<reference<R.quantity_spec, To>{}, Rep>::one();
}) })
{ {
const auto handle_signed_results = [&]<typename T>(const T& res) { const auto handle_signed_results = [&]<typename T>(const T& res) {
if (res < q) { if (res < q) {
@ -220,33 +203,21 @@ template<Unit To, typename D, typename U, typename Rep>
}; };
if constexpr (treat_as_floating_point<Rep>) { if constexpr (treat_as_floating_point<Rep>) {
using std::ceil; using std::ceil;
if constexpr (std::is_same_v<To, U>) { if constexpr (To == R.unit) {
return quantity<D, To, Rep>(ceil(q.number())); return quantity<reference<R.quantity_spec, To>{}, Rep>(ceil(q.number()));
} else { } 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 { } else {
if constexpr (std::is_same_v<To, U>) { if constexpr (To == R.unit) {
return q; return quantity_cast<To>(q);
} else { } else {
return handle_signed_results(quantity_cast<To>(q)); 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 * @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 * @tparam q Quantity being the base of the operation
* @return Quantity The rounded quantity with unit type To * @return Quantity The rounded quantity with unit type To
*/ */
template<Unit To, typename D, typename U, typename Rep> template<Unit auto To, auto R, typename Rep>
[[nodiscard]] constexpr quantity<D, To, Rep> round(const quantity<D, U, Rep>& q) noexcept [[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((!treat_as_floating_point<Rep>) || requires { round(q.number()); } ||
requires { std::round(q.number()); }) && requires { std::round(q.number()); }) &&
(std::same_as<To, U> || requires { (To == R.unit || requires {
::units::floor<To>(q); ::units::floor<To>(q);
quantity<D, To, Rep>::one(); 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>) { if constexpr (treat_as_floating_point<Rep>) {
using std::round; using std::round;
return quantity<D, To, Rep>(round(q.number())); return quantity<reference<R.quantity_spec, To>{}, Rep>(round(q.number()));
} else { } else {
return q; return quantity_cast<To>(q);
} }
} else { } else {
const auto res_low = units::floor<To>(q); 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 diff0 = q - res_low;
const auto diff1 = res_high - q; const auto diff1 = res_high - q;
if (diff0 == diff1) { 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, * @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 * without undue overflow or underflow at intermediate stages of the computation
*/ */
template<Quantity Q1, Quantity Q2> template<Quantity Q1, Quantity Q2>
[[nodiscard]] inline std::common_type_t<Q1, Q2> hypot(const Q1& x, const Q2& y) noexcept [[nodiscard]] inline quantity_of<common_reference(Q1::reference, Q2::reference)> auto hypot(const Q1& x,
requires requires { typename std::common_type_t<Q1, Q2>; } && const Q2& y) noexcept
requires(std::common_type_t<Q1, Q2> q) { requires requires { common_reference(Q1::reference, Q2::reference); } &&
requires requires { hypot(q.number(), q.number()); } || requires { std::hypot(q.number(), q.number()); }; (
} 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; 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 * without undue overflow or underflow at intermediate stages of the computation
*/ */
template<Quantity Q1, Quantity Q2, Quantity Q3> 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 [[nodiscard]] inline quantity_of<common_reference(Q1::reference, Q2::reference, Q3::reference)> auto hypot(
requires requires { typename std::common_type_t<Q1, Q2, Q3>; } && const Q1& x, const Q2& y, const Q3& z) noexcept
requires(std::common_type_t<Q1, Q2, Q3> q) { requires requires { common_reference(Q1::reference, Q2::reference, Q3::reference); } &&
requires requires { hypot(q.number(), q.number(), q.number()); } || (
requires { std::hypot(q.number(), q.number(), q.number()); }; 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; using std::hypot;
return type(hypot(xx.number(), yy.number(), zz.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())};
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()));
} }
} // namespace units } // namespace units

View File

@ -29,6 +29,7 @@
#include <units/concepts.h> #include <units/concepts.h>
#include <units/customization_points.h> #include <units/customization_points.h>
#include <units/dimension.h> #include <units/dimension.h>
#include <units/quantity_spec.h>
#include <units/reference.h> #include <units/reference.h>
#include <units/unit.h> #include <units/unit.h>
#include <compare> #include <compare>
@ -115,9 +116,10 @@ class quantity {
public: public:
// member types and values // member types and values
using rep = Rep; using rep = Rep;
static constexpr auto reference = R; static constexpr Reference auto reference = R;
static constexpr auto dimension = R.dimension; static constexpr QuantitySpec auto quantity_spec = R.quantity_spec;
static constexpr auto unit = R.unit; static constexpr Dimension auto dimension = R.dimension;
static constexpr Unit auto unit = R.unit;
// static member functions // static member functions
[[nodiscard]] static constexpr quantity zero() noexcept [[nodiscard]] static constexpr quantity zero() noexcept
@ -176,10 +178,10 @@ public:
[[nodiscard]] constexpr const rep&& number() const&& noexcept { return std::move(number_); } [[nodiscard]] constexpr const rep&& number() const&& noexcept { return std::move(number_); }
template<Unit U> template<Unit U>
requires quantity_convertible_to_<quantity, quantity<::units::reference<dimension, U{}>{}, Rep>> requires quantity_convertible_to_<quantity, quantity<::units::reference<quantity_spec, U{}>{}, Rep>>
[[nodiscard]] constexpr quantity<::units::reference<dimension, U{}>{}, Rep> operator[](U) const [[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 // member unary operators
@ -424,7 +426,7 @@ public:
requires(!Quantity<Value>) && invoke_result_convertible_to_<rep, std::divides<>, const Value&, rep> 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) [[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> template<typename Value>
@ -459,16 +461,11 @@ public:
}; };
// CTAD // 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> template<auto R, typename Rep>
explicit(false) quantity(quantity<R, Rep>) -> quantity<R, Rep>; explicit(false) quantity(quantity<R, Rep>) -> quantity<R, Rep>;
template<Representation Rep> template<Representation Rep>
explicit(false) quantity(Rep)->quantity<dimension_one[one], Rep>; explicit(false) quantity(Rep)->quantity<dimensionless[one], Rep>;
template<QuantityLike Q> template<QuantityLike Q>
explicit quantity(Q) -> quantity<reference<quantity_like_traits<Q>::dimension, quantity_like_traits<Q>::unit>{}, explicit quantity(Q) -> quantity<reference<quantity_like_traits<Q>::dimension, quantity_like_traits<Q>::unit>{},

View File

@ -52,11 +52,12 @@ class quantity;
* @brief Explicit cast of a quantity * @brief Explicit cast of a quantity
* *
* Implicit conversions between quantities of different types are allowed only for "safe" * 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: * 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 * @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 * @brief Explicit cast of a quantity
* *
* Implicit conversions between quantities of different types are allowed only for "safe" * 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 * @tparam ToR a reference to use for a target quantity
* (@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
*/ */
template<Reference auto ToR, auto R, typename Rep> template<Reference auto ToR, auto R, typename Rep>
requires(interconvertible(ToR, R)) requires(interconvertible(ToR, R))
@ -134,19 +131,19 @@ template<Reference auto ToR, auto R, typename Rep>
* @brief Explicit cast of a quantity * @brief Explicit cast of a quantity
* *
* Implicit conversions between quantities of different types are allowed only for "safe" * 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> template<QuantitySpec auto ToQS, auto R, typename Rep>
requires(interconvertible(ToD, R.dimension)) requires(interconvertible(ToQS, R.quantity_spec))
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q) [[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); 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 * @brief Explicit cast of a quantity
* *
* Implicit conversions between quantities of different types are allowed only for "safe" * 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: * 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> template<Unit auto ToU, auto R, typename Rep>
requires(interconvertible(ToU, R.unit)) requires(interconvertible(ToU, R.unit))
[[nodiscard]] constexpr auto quantity_cast(const quantity<R, Rep>& q) [[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); 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 * @brief Explicit cast of a quantity
* *
* Implicit conversions between quantities of different types are allowed only for "safe" * 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: * 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 * @tparam ToRep a representation type to use for a target quantity
*/ */

View 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

View File

@ -23,72 +23,52 @@
#pragma once #pragma once
#include <units/concepts.h> #include <units/concepts.h>
#include <units/dimension.h> #include <units/quantity_spec.h>
#include <units/unit.h> #include <units/unit.h>
namespace units { 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} * @code{.cpp}
* namespace length_references { * Reference auto kmph = isq::speed[km / h];
* * quantity_of<isq::speed[km / h]> auto speed = 90 * kmph;
* 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;
* @endcode * @endcode
* *
* The following syntaxes are not allowed: * 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 { struct reference {
static constexpr auto dimension = D; static constexpr QuantitySpec auto quantity_spec = Q;
static constexpr auto unit = U; static constexpr Dimension auto dimension = Q.dimension;
static constexpr Unit auto unit = U;
}; };
// Reference // Reference
template<Magnitude M, Reference R> 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 {}; return {};
} }
template<Reference R1, Reference R2> 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 {}; return {};
} }
template<Reference R1, Reference R2> 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 {}; return {};
} }
@ -104,26 +84,26 @@ void /*Use `q * (1 * r)` rather than `q * r`.*/ operator*(Quantity auto, Referen
template<Reference R1, Reference R2> template<Reference R1, Reference R2>
[[nodiscard]] consteval bool operator==(R1, 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> template<Reference R1, Reference R2>
[[nodiscard]] consteval bool interconvertible(R1, 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) [[nodiscard]] consteval auto common_reference(Reference auto r1, Reference auto r2, Reference auto... rest)
requires requires { requires requires {
{ {
common_dimension(r1.dimension, r2.dimension, rest.dimension...) common_quantity_spec(r1.quantity_spec, r2.quantity_spec, rest.quantity_spec...)
} -> Dimension; } -> QuantitySpec;
{ {
common_unit(r1.unit, r2.unit, rest.unit...) common_unit(r1.unit, r2.unit, rest.unit...)
} -> 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...)>{}; common_unit(r1.unit, r2.unit, rest.unit...)>{};
} }

View File

@ -23,20 +23,48 @@
#pragma once #pragma once
#include <units/concepts.h> #include <units/concepts.h>
#include <units/quantity_spec.h>
#include <units/reference.h> #include <units/reference.h>
#include <units/unit.h> #include <units/unit.h>
namespace units { 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 { struct system_reference {
static constexpr auto dimension = Dim; static constexpr auto quantity_spec = Q;
static constexpr auto coherent_unit = CoU; static constexpr auto coherent_unit = CoU;
template<Unit U> template<Unit U>
requires(interconvertible(coherent_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 {}; return {};
} }

View File

@ -28,6 +28,7 @@
#include <units/bits/external/text_tools.h> #include <units/bits/external/text_tools.h>
#include <units/bits/external/type_name.h> #include <units/bits/external/type_name.h>
#include <units/bits/external/type_traits.h> #include <units/bits/external/type_traits.h>
#include <units/dimension.h>
#include <units/magnitude.h> #include <units/magnitude.h>
#include <units/ratio.h> #include <units/ratio.h>
#include <units/symbol_text.h> #include <units/symbol_text.h>
@ -37,43 +38,57 @@
namespace units { namespace units {
#ifdef __cpp_explicit_this_parameter #ifdef __cpp_explicit_this_parameter
template<basic_fixed_string Symbol> template<auto...>
#else #else
template<typename Self, basic_fixed_string Symbol> template<typename, auto...>
#endif #endif
struct base_dimension; struct quantity_spec;
namespace detail { namespace detail {
#ifdef __cpp_explicit_this_parameter #ifdef __cpp_explicit_this_parameter
template<basic_fixed_string Symbol> template<auto... Args>
void to_base_base_dimension(const volatile base_dimension<Symbol>*); void to_base_specialization_of_quantity_spec(const volatile quantity_spec<Args...>*);
#else #else
template<typename Self, basic_fixed_string Symbol> template<typename T, auto... Args>
void to_base_base_dimension(const volatile base_dimension<Self, Symbol>*); 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 #endif
template<typename T> 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 #ifdef __cpp_explicit_this_parameter
template<basic_fixed_string Symbol> template<auto... Args>
inline constexpr bool is_specialization_of_base_dimension<base_dimension<Symbol>> = true; inline constexpr bool is_specialization_of_quantity_spec<quantity_spec<Args...>> = true;
#else #else
template<typename Self, basic_fixed_string Symbol> template<typename T, auto... Args>
inline constexpr bool is_specialization_of_base_dimension<base_dimension<Self, Symbol>> = true; inline constexpr bool is_specialization_of_quantity_spec<quantity_spec<T, Args...>> = true;
#endif #endif
} // namespace detail } // 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> template<typename T>
concept BaseDimension = requires(T* t) { detail::to_base_base_dimension(t); } && concept NamedQuantitySpec = requires(T* t) { detail::to_base_specialization_of_quantity_spec(t); } &&
(!detail::is_specialization_of_base_dimension<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 { namespace detail {
@ -146,12 +161,12 @@ template<basic_symbol_text Symbol, auto...>
struct named_unit; 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. * 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 * @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` * 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. * 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 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()) 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 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 * 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. * 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. * 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 } // namespace detail
template<typename T> 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 * @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 * @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. * 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...> {}; struct derived_unit : detail::expr_fractions<derived_unit<>, Us...> {};
/** /**
@ -424,8 +439,8 @@ struct canonical_unit {
U reference_unit; U reference_unit;
}; };
template<Unit T, basic_symbol_text Symbol, BaseDimension auto D> template<Unit T, basic_symbol_text Symbol, BaseQuantitySpec auto Q>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, D>&); [[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q>&);
template<Unit T, basic_symbol_text Symbol> template<Unit T, basic_symbol_text Symbol>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<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}; return canonical_unit{M * base.mag, base.reference_unit};
} }
template<Unit T, basic_symbol_text Symbol, BaseDimension auto D> template<Unit T, basic_symbol_text Symbol, BaseQuantitySpec auto Q>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, D>&) [[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q>&)
{ {
return canonical_unit{mag<1>, t}; 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>)) (!is_derived_from_specialization_of_constant_unit<Lhs> && !is_derived_from_specialization_of_constant_unit<Rhs>))
return type_name<Lhs>() < type_name<Rhs>(); return type_name<Lhs>() < type_name<Rhs>();
else else
// put constants at the front of units list in the expression
return is_derived_from_specialization_of_constant_unit<Lhs>; 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) 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); if (Idx > 0) out = print_separator<CharT>(out, fmt);
return unit_symbol_impl<CharT>(out, m, fmt, negative_power); 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...>, constexpr Out unit_symbol_impl(Out out, const type_list<Ms...>&, std::index_sequence<Idxs...>,
unit_symbol_formatting fmt, bool negative_power) unit_symbol_formatting fmt, bool negative_power)
{ {
return (..., (out = unit_symbol_impl<CharT>(out, Ms{}, Idxs, fmt, 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, constexpr Out unit_symbol_impl(Out out, const type_list<Nums...>& nums, const type_list<Dens...>& dens,
unit_symbol_formatting fmt) unit_symbol_formatting fmt)
{ {

View File

@ -23,6 +23,7 @@
cmake_minimum_required(VERSION 3.19) cmake_minimum_required(VERSION 3.19)
add_units_module( add_units_module(
isq DEPENDENCIES mp-units::core HEADERS include/units/isq/base_dimensions.h include/units/isq/isq.h isq DEPENDENCIES mp-units::core
include/units/isq/mechanics.h include/units/isq/space_and_time.h 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
) )

View File

@ -23,16 +23,28 @@
#pragma once #pragma once
#include <units/dimension.h> #include <units/dimension.h>
#include <units/quantity_spec.h>
namespace units::isq { namespace units::isq {
BASE_DIMENSION(length, "L"); // clang-format off
BASE_DIMENSION(mass, "M"); // dimensions of base quantities
BASE_DIMENSION(time, "T"); inline constexpr struct dim_length : base_dimension<"L"> {} dim_length;
BASE_DIMENSION(electric_current, "I"); inline constexpr struct dim_mass : base_dimension<"M"> {} dim_mass;
// TODO Should the below use basic_symbol_text? How to name it for ASCII? inline constexpr struct dim_time : base_dimension<"T"> {} dim_time;
BASE_DIMENSION(thermodynamic_temperature, "Θ"); inline constexpr struct dim_electric_current : base_dimension<"I"> {} dim_electric_current;
BASE_DIMENSION(amount_of_substance, "N"); inline constexpr struct dim_thermodynamic_temperature : base_dimension<basic_symbol_text{"Θ", "O"}> {} dim_thermodynamic_temperature;
BASE_DIMENSION(luminous_intensity, "J"); 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 } // namespace units::isq

View File

@ -23,6 +23,8 @@
#pragma once #pragma once
// IWYU pragma: begin_exports // 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/space_and_time.h>
#include <units/isq/thermodynamics.h>
// IWYU pragma: end_exports // IWYU pragma: end_exports

View File

@ -23,50 +23,86 @@
#pragma once #pragma once
#include <units/dimension.h> #include <units/dimension.h>
#include <units/isq/base_dimensions.h> #include <units/isq/base_quantities.h>
#include <units/isq/space_and_time.h> #include <units/isq/space_and_time.h>
namespace units::isq { namespace units::isq {
// inline constexpr struct mass : base_dimension<"M"> {} mass; // inline constexpr struct mass : base_dimension<"M"> {} mass;
DERIVED_DIMENSION(mass_density, decltype(mass / volume)); QUANTITY_SPEC(mass_density, mass / volume);
DERIVED_DIMENSION(specific_volume, decltype(1 / mass_density)); inline constexpr auto density = mass_density;
DERIVED_DIMENSION(relative_mass_density, decltype(mass_density / mass_density)); QUANTITY_SPEC(specific_volume, 1 / mass_density);
DERIVED_DIMENSION(surface_mass_density, decltype(mass / area)); QUANTITY_SPEC(relative_mass_density, mass_density / mass_density);
DERIVED_DIMENSION(linear_mass_density, decltype(mass / length)); inline constexpr auto relative_density = relative_mass_density;
DERIVED_DIMENSION(momentum, decltype(mass * speed)); // TODO velocity? QUANTITY_SPEC(surface_mass_density, mass / area);
DERIVED_DIMENSION(force, decltype(mass * acceleration)); // TODO what is a correct equation here? inline constexpr auto surface_density = surface_mass_density;
// DERIVED_DIMENSION(weight, decltype(mass * acceleration)); // TODO should we add it as a quantity or should it be a QUANTITY_SPEC(linear_mass_density, mass / length);
// quantity_kind? inline constexpr auto linear_density = linear_mass_density;
// TODO Should we add other forces as well: static_friction_force, kinematic_friction_force, rolling_resistance, QUANTITY_SPEC(momentum, mass* velocity);
// drag_force QUANTITY_SPEC(force, mass* acceleration); // vector // TODO what is a correct equation here?
DERIVED_DIMENSION(impulse, decltype(force / time)); QUANTITY_SPEC(weight, force); // vector // TODO g?
DERIVED_DIMENSION(angular_momentum, decltype(length * momentum)); // TODO position_vector QUANTITY_SPEC(static_friction_force, force); // vector
DERIVED_DIMENSION(moment_of_inertia, decltype(angular_momentum * angular_velocity)); inline constexpr auto static_friction = static_friction_force;
DERIVED_DIMENSION(moment_of_force, decltype(length * force)); // TODO position_vector QUANTITY_SPEC(kinetic_friction_force, force); // vector
DERIVED_DIMENSION(torque, decltype(moment_of_force)); // TODO angle? inline constexpr auto dynamic_friction_force = kinetic_friction_force;
DERIVED_DIMENSION(angular_impulse, decltype(moment_of_force * time)); QUANTITY_SPEC(rolling_resistance, force); // vector
DERIVED_DIMENSION(pressure, decltype(force / area)); inline constexpr auto rolling_drag = rolling_resistance;
DERIVED_DIMENSION(stress, decltype(pressure)); // TODO tensor? inline constexpr auto rolling_friction_force = rolling_resistance;
DERIVED_DIMENSION(normal_stress, decltype(force / area)); QUANTITY_SPEC(drag_force, force); // vector
DERIVED_DIMENSION(strain, decltype(stress / stress)); // TODO what is a correct equation here? QUANTITY_SPEC(impulse, force* time); // vector
DERIVED_DIMENSION(poisson_number, decltype(length / length)); // TODO width? QUANTITY_SPEC(angular_momentum, position_vector* momentum); // vector
// TODO modulus quantities QUANTITY_SPEC(moment_of_inertia, angular_momentum / angular_velocity, quantity_character::tensor);
DERIVED_DIMENSION(compressibility, decltype(volume / volume / pressure)); QUANTITY_SPEC(moment_of_force, position_vector* force); // vector
DERIVED_DIMENSION(second_axial_moment_of_area, decltype(area * area)); // TODO what is a correct equation here? QUANTITY_SPEC(torque, moment_of_force, quantity_character::scalar);
DERIVED_DIMENSION(section_modulus, decltype(second_axial_moment_of_area / length)); // TODO radial distance QUANTITY_SPEC(angular_impulse, moment_of_force* time); // vector
// TODO friction coefficients? QUANTITY_SPEC(pressure, force / area, quantity_character::scalar);
DERIVED_DIMENSION(dynamic_viscosity, decltype(stress * length / speed)); // TODO shear stress, velocity QUANTITY_SPEC(gauge_pressure, pressure);
DERIVED_DIMENSION(kinematic_viscosity, decltype(dynamic_viscosity / mass_density)); QUANTITY_SPEC(stress, pressure, quantity_character::tensor);
DERIVED_DIMENSION(surface_tension, decltype(force / length)); // TODO what is a correct equation here? QUANTITY_SPEC(normal_stress, pressure, quantity_character::scalar);
DERIVED_DIMENSION(power, decltype(force * speed)); QUANTITY_SPEC(shear_stress, pressure, quantity_character::scalar);
// TODO what about energy (potential and kinetic as separate quantities will prevent an equation for mechanical one, is QUANTITY_SPEC(strain, dimensionless, quantity_character::tensor);
// it expected?) QUANTITY_SPEC(relative_linear_strain, length / length);
DERIVED_DIMENSION(efficiency, decltype(power / power)); QUANTITY_SPEC(shear_strain, displacement / thickness);
DERIVED_DIMENSION(mass_flow, decltype(mass_density * speed)); // TODO velocity QUANTITY_SPEC(relative_volume_strain, volume / volume);
DERIVED_DIMENSION(mass_flow_rate, decltype(mass_flow * area)); QUANTITY_SPEC(Poisson_number, width / length);
DERIVED_DIMENSION(mass_change_rate, decltype(mass / time)); QUANTITY_SPEC(modulus_of_elasticity, normal_stress / relative_linear_strain);
DERIVED_DIMENSION(volume_flow_rate, decltype(speed * area)); // TODO velocity inline constexpr auto Young_modulus = modulus_of_elasticity;
// DERIVED_DIMENSION(action, decltype(energy * time)); // TODO make it compile 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 } // namespace units::isq

View File

@ -23,44 +23,64 @@
#pragma once #pragma once
#include <units/dimension.h> #include <units/dimension.h>
#include <units/isq/base_dimensions.h> #include <units/isq/base_quantities.h>
namespace units::isq { namespace units::isq {
// inline constexpr struct length : base_dimension<"L"> {} length; // clang-format off
DERIVED_DIMENSION(curvature, decltype(1 / length)); QUANTITY_SPEC(width, length);
DERIVED_DIMENSION(area, decltype(length * length)); inline constexpr auto breadth = width;
DERIVED_DIMENSION(volume, decltype(length * length * length)); QUANTITY_SPEC(height, length);
DERIVED_DIMENSION(angular_measure, decltype(length / length)); inline constexpr auto depth = height;
DERIVED_DIMENSION(angular_displacement, decltype(length / length)); inline constexpr auto altitude = height;
DERIVED_DIMENSION(phase_angle, decltype(length / length)); QUANTITY_SPEC(thickness, width);
inline constexpr struct solid_angular_measure : decltype(area / (length * length)) { QUANTITY_SPEC(diameter, width);
} solid_angular_measure; // QUANTITY_SPEC(radius, mag<ratio{1, 2}> * diameter);
// inline constexpr struct time : base_dimension<"T"> {} time; // TODO called duration in ISO 80000 QUANTITY_SPEC(radius, diameter);
// TODO there is also a velocity in ISO 80000 QUANTITY_SPEC(path_length, length);
DERIVED_DIMENSION(speed, decltype(length / time)); inline constexpr auto arc_length = path_length;
DERIVED_DIMENSION(acceleration, decltype(speed / time)); QUANTITY_SPEC(distance, path_length);
DERIVED_DIMENSION(angular_velocity, decltype(angular_displacement / time)); QUANTITY_SPEC(radial_distance, distance);
DERIVED_DIMENSION(angular_acceleration, decltype(angular_velocity / time)); QUANTITY_SPEC(position_vector, length, quantity_character::vector);
inline constexpr struct period_duration : time { QUANTITY_SPEC(displacement, length, quantity_character::vector);
} period_duration; QUANTITY_SPEC(radius_of_curvature, radius);
inline constexpr struct time_constant : time { QUANTITY_SPEC(curvature, 1 / radius_of_curvature);
} time_constant; QUANTITY_SPEC(area, pow<2>(length));
inline constexpr struct rotation : angular_displacement { QUANTITY_SPEC(volume, pow<3>(length));
} rotation; QUANTITY_SPEC(angular_measure, arc_length / radius);
DERIVED_DIMENSION(frequency, decltype(1 / time)); QUANTITY_SPEC(rotational_displacement, path_length / radius);
DERIVED_DIMENSION(rotational_frequency, decltype(rotation / time)); inline constexpr auto angular_displacement = rotational_displacement;
DERIVED_DIMENSION(angular_frequency, decltype(angular_measure / time)); QUANTITY_SPEC(phase_angle, angular_measure);
inline constexpr struct wavelength : length { QUANTITY_SPEC(solid_angular_measure, angular_measure * angular_measure);
} wavelength; inline constexpr auto duration = time;
DERIVED_DIMENSION(repetency, decltype(1 / wavelength)); QUANTITY_SPEC(velocity, position_vector / duration); // vector
DERIVED_DIMENSION(wave_vector, decltype(1 / length)); QUANTITY_SPEC(speed, distance / duration); // TODO length, path_length?
DERIVED_DIMENSION(angular_repetency, decltype(1 / wavelength)); QUANTITY_SPEC(acceleration, velocity / duration); // vector
DERIVED_DIMENSION(phase_velocity, decltype(angular_frequency / angular_repetency)); QUANTITY_SPEC(angular_velocity, angular_displacement / duration, quantity_character::vector);
DERIVED_DIMENSION(damping_coefficient, decltype(1 / time_constant)); QUANTITY_SPEC(angular_acceleration, angular_velocity / duration);
DERIVED_DIMENSION(logarithmic_decrement, decltype(damping_coefficient * period_duration)); QUANTITY_SPEC(period_duration, duration);
DERIVED_DIMENSION(attenuation, decltype(1 / length)); inline constexpr auto period = period_duration;
DERIVED_DIMENSION(phase_coefficient, decltype(phase_angle / length)); QUANTITY_SPEC(time_constant, duration);
DERIVED_DIMENSION(propagation_coefficient, decltype(1 / length)); 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 } // namespace units::isq

View File

@ -23,7 +23,7 @@
#pragma once #pragma once
#include <units/dimension.h> #include <units/dimension.h>
#include <units/isq/base_dimensions.h> #include <units/isq/base_quantities.h>
#include <units/isq/space_and_time.h> #include <units/isq/space_and_time.h>
namespace units::isq { namespace units::isq {
@ -34,6 +34,6 @@ namespace units::isq {
// DERIVED_DIMENSION(mass_density, decltype(mass / volume)); // 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 } // namespace units::isq

View File

@ -641,5 +641,6 @@ inline constexpr auto d = day;
inline constexpr auto m2 = square<metre>; inline constexpr auto m2 = square<metre>;
inline constexpr auto m3 = cubic<metre>; inline constexpr auto m3 = cubic<metre>;
inline constexpr auto s2 = square<second>; inline constexpr auto s2 = square<second>;
inline constexpr auto s3 = cubic<second>;
} // namespace units::si::unit_symbols } // namespace units::si::unit_symbols

View File

@ -22,7 +22,7 @@
#pragma once #pragma once
#include <units/isq/base_dimensions.h> #include <units/isq/base_quantities.h>
#include <units/si/prefixes.h> #include <units/si/prefixes.h>
#include <units/unit.h> #include <units/unit.h>

View File

@ -26,9 +26,7 @@ find_package(Catch2 3 CONFIG REQUIRED)
add_executable( add_executable(
unit_tests_runtime unit_tests_runtime
distribution_test.cpp distribution_test.cpp fmt_test.cpp
fmt_test.cpp
# fmt_units_test.cpp # fmt_units_test.cpp
math_test.cpp math_test.cpp
) )

View File

@ -35,7 +35,6 @@ cmake_minimum_required(VERSION 3.2)
add_library( add_library(
unit_tests_static unit_tests_static
dimension_test.cpp dimension_test.cpp
# angle_test.cpp # angle_test.cpp
# cgs_test.cpp # cgs_test.cpp
# chrono_test.cpp # chrono_test.cpp
@ -43,27 +42,26 @@ add_library(
# custom_rep_test_min_expl.cpp # custom_rep_test_min_expl.cpp
# custom_unit_test.cpp # custom_unit_test.cpp
# dimension_op_test.cpp # dimension_op_test.cpp
dimension_test.cpp
# dimensions_concepts_test.cpp # dimensions_concepts_test.cpp
# fixed_string_test.cpp # fixed_string_test.cpp
# fps_test.cpp # fps_test.cpp
# iec80000_test.cpp # iec80000_test.cpp
# kind_test.cpp # kind_test.cpp
magnitude_test.cpp magnitude_test.cpp
# math_test.cpp # math_test.cpp
# point_origin_test.cpp # point_origin_test.cpp
# prime_test.cpp # prime_test.cpp
quantity_spec_test.cpp
ratio_test.cpp ratio_test.cpp
reference_test.cpp reference_test.cpp
# si_test.cpp
# si_cgs_test.cpp # si_cgs_test.cpp
# si_fps_test.cpp # si_fps_test.cpp
# si_hep_test.cpp # si_hep_test.cpp
# si_test.cpp
# symbol_text_test.cpp # symbol_text_test.cpp
type_list_test.cpp type_list_test.cpp
unit_test.cpp unit_test.cpp
# us_test.cpp # us_test.cpp
) )

View File

@ -33,37 +33,37 @@ using namespace units;
using dimension_one_ = struct dimension_one; using dimension_one_ = struct dimension_one;
// clang-format off // clang-format off
BASE_DIMENSION_(length, "L"); inline constexpr struct length_ : base_dimension<"L"> {} length;
BASE_DIMENSION_(time, "T"); inline constexpr struct mass_ : base_dimension<"M"> {} mass;
BASE_DIMENSION_(mass, "M"); 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)); inline constexpr auto frequency = 1 / time;
DERIVED_DIMENSION_(action, decltype(1 / time)); inline constexpr auto action = 1 / time;
DERIVED_DIMENSION_(area, decltype(length * length)); inline constexpr auto area = length * length;
DERIVED_DIMENSION_(volume, decltype(area * length)); inline constexpr auto volume = area * length;
DERIVED_DIMENSION_(speed, decltype(length / time)); inline constexpr auto speed = length / time;
inline constexpr struct velocity_ : speed_ {} velocity; inline constexpr auto acceleration = speed / time;
DERIVED_DIMENSION_(acceleration, decltype(speed / time)); inline constexpr auto force = mass * acceleration;
DERIVED_DIMENSION_(force, decltype(mass * acceleration)); inline constexpr auto moment_of_force = length * force;
DERIVED_DIMENSION_(moment_of_force, decltype(length * force)); inline constexpr auto torque = moment_of_force;
DERIVED_DIMENSION_(torque, decltype(moment_of_force)); inline constexpr auto pressure = force / area;
DERIVED_DIMENSION_(pressure, decltype(force / area)); inline constexpr auto stress = pressure;
DERIVED_DIMENSION_(stress, decltype(pressure)); inline constexpr auto strain = stress / stress;
DERIVED_DIMENSION_(strain, decltype(stress / stress)); inline constexpr auto power = force * speed;
DERIVED_DIMENSION_(power, decltype(force * speed)); inline constexpr auto efficiency = power / power;
DERIVED_DIMENSION_(efficiency, decltype(power / power)); inline constexpr auto energy = force * length;
DERIVED_DIMENSION_(energy, decltype(force * length));
// clang-format on // clang-format on
// concepts verification // concepts verification
static_assert(BaseDimension<length_>); static_assert(BaseDimension<length_>);
static_assert(!BaseDimension<frequency_>); static_assert(!BaseDimension<std::remove_const_t<decltype(frequency)>>);
static_assert(!DerivedDimension<length_>); static_assert(!DerivedDimension<length_>);
static_assert(DerivedDimension<frequency_>); static_assert(DerivedDimension<std::remove_const_t<decltype(frequency)>>);
static_assert(Dimension<length_>); 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<dimension_one_>);
static_assert(DerivedDimension<decltype(length / length)>); // dimension_one static_assert(DerivedDimension<decltype(length / length)>); // dimension_one
@ -137,29 +137,29 @@ concept invalid_operations = requires {
requires !requires { 2 == t; }; requires !requires { 2 == t; };
requires !requires { t < 2; }; requires !requires { t < 2; };
requires !requires { 2 < t; }; requires !requires { 2 < t; };
requires !requires { t + time[second]; }; requires !requires { t + q_time[second]; };
requires !requires { t - time[second]; }; requires !requires { t - q_time[second]; };
requires !requires { t* time[second]; }; requires !requires { t* q_time[second]; };
requires !requires { t / time[second]; }; requires !requires { t / q_time[second]; };
requires !requires { t == time[second]; }; requires !requires { t == q_time[second]; };
requires !requires { t < time[second]; }; requires !requires { t < q_time[second]; };
requires !requires { time[second] + t; }; requires !requires { q_time[second] + t; };
requires !requires { time[second] - t; }; requires !requires { q_time[second] - t; };
requires !requires { time[second] * t; }; requires !requires { q_time[second] * t; };
requires !requires { time[second] / t; }; requires !requires { q_time[second] / t; };
requires !requires { time[second] == t; }; requires !requires { q_time[second] == t; };
requires !requires { time[second] < t; }; requires !requires { q_time[second] < t; };
requires !requires { t + 1 * time[second]; }; requires !requires { t + 1 * q_time[second]; };
requires !requires { t - 1 * time[second]; }; requires !requires { t - 1 * q_time[second]; };
requires !requires { t * 1 * time[second]; }; requires !requires { t * 1 * q_time[second]; };
requires !requires { t / 1 * time[second]; }; requires !requires { t / 1 * q_time[second]; };
requires !requires { t == 1 * time[second]; }; requires !requires { t == 1 * q_time[second]; };
requires !requires { t == 1 * time[second]; }; requires !requires { t == 1 * q_time[second]; };
requires !requires { 1 * time[second] + t; }; requires !requires { 1 * q_time[second] + t; };
requires !requires { 1 * time[second] - t; }; requires !requires { 1 * q_time[second] - t; };
requires !requires { 1 * time[second] * t; }; requires !requires { 1 * q_time[second] * t; };
requires !requires { 1 * time[second] == t; }; requires !requires { 1 * q_time[second] == t; };
requires !requires { 1 * time[second] < t; }; requires !requires { 1 * q_time[second] < t; };
}; };
static_assert(invalid_operations<time>); static_assert(invalid_operations<time>);
@ -170,95 +170,62 @@ static_assert(speed == speed);
// comparisons of equivalent dimensions (named vs unnamed/derived) // comparisons of equivalent dimensions (named vs unnamed/derived)
static_assert(length / length == dimension_one); static_assert(length / length == dimension_one);
static_assert(1 / time != frequency); static_assert(1 / time == frequency);
static_assert(interconvertible(1 / time, frequency));
static_assert(1 / frequency == time); static_assert(1 / frequency == time);
static_assert(frequency * time == dimension_one); 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(length * length == area);
static_assert(interconvertible(length * length, area));
static_assert(length * length != volume); static_assert(length * length != volume);
static_assert(area / length == length); 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(length * length * length == volume);
static_assert(area * length != volume); static_assert(area * length == volume);
static_assert(volume / length != area); static_assert(volume / length == area);
static_assert(volume / length / length == length); 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(volume / (length * length) == length);
static_assert(length / time != speed); static_assert(length / time == speed);
static_assert(length * time != speed); static_assert(length * time != speed);
static_assert(length / time / time != speed); static_assert(length / time / time != speed);
static_assert(length / speed == time); static_assert(length / speed == time);
static_assert(speed * time == length); 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(length / (time * time) != acceleration); static_assert(length / (time * time) == acceleration);
static_assert(speed / time != acceleration); static_assert(speed / time == acceleration);
static_assert(speed / acceleration == time); static_assert(speed / acceleration == time);
static_assert(acceleration * time != speed); static_assert(acceleration * time == speed);
static_assert(acceleration * (time * time) == length); static_assert(acceleration * (time * time) == length);
static_assert(acceleration / speed != frequency); 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_>);
// comparison of convertible unnamed dimensions // 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<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(is_of_type<acceleration * mass, derived_dimension<length_, mass_, per<units::power<time_, 2>>>>);
static_assert(mass * acceleration == acceleration * mass); static_assert(mass * acceleration == acceleration * mass);
static_assert(interconvertible(mass * acceleration, acceleration* mass));
// comparisons of equivalent but not convertible dimensions // comparisons of equivalent but not convertible dimensions
static_assert(energy != torque); static_assert(energy == torque);
static_assert(!interconvertible(energy, torque));
static_assert(force * length != energy); static_assert(force * length == energy);
static_assert(force * length != torque); 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(frequency == action);
static_assert(!interconvertible(frequency, action));
static_assert(no_common_type<frequency, action>);
// dimension_one // dimension_one
static_assert(interconvertible(power / power, efficiency)); static_assert(power / power == efficiency);
static_assert(power / power != efficiency); static_assert(dimension_one == 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 == strain);
static_assert(stress / stress != efficiency); static_assert(stress / stress == efficiency);
static_assert(interconvertible(stress / stress, strain));
static_assert(interconvertible(stress / stress, efficiency));
// comparison of not equivalent dimensions // comparison of not equivalent dimensions
static_assert(length != time); static_assert(length != time);
static_assert(!interconvertible(length, time));
static_assert(acceleration != speed); static_assert(acceleration != speed);
static_assert(!interconvertible(acceleration, speed));
// power // power
static_assert(is_of_type<pow<2>(length), derived_dimension<units::power<length_, 2>>>); static_assert(is_of_type<pow<2>(length), derived_dimension<units::power<length_, 2>>>);

View 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

View File

@ -23,6 +23,7 @@
#include "test_tools.h" #include "test_tools.h"
#include <units/dimension.h> #include <units/dimension.h>
#include <units/quantity.h> #include <units/quantity.h>
#include <units/quantity_spec.h>
#include <units/reference.h> #include <units/reference.h>
#include <units/si/prefixes.h> #include <units/si/prefixes.h>
#include <units/system_reference.h> #include <units/system_reference.h>
@ -31,30 +32,34 @@
namespace { namespace {
using namespace units; using namespace units;
using namespace units::detail;
using dimension_one_ = struct dimension_one; using dimensionless_ = struct dimensionless;
using one_ = struct one; using one_ = struct one;
// base dimensions // 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 // 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 // base units
inline constexpr struct second_ : named_unit<"s", time> {} second; inline constexpr struct second_ : named_unit<"s", time> {} second;
inline constexpr struct metre_ : named_unit<"m", length> {} metre; 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( static_assert(
is_same_v< is_same_v<
decltype(10 * length[metre] / (2 * time[second])), 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 // Base quantity as a result of dimensional transformation
static_assert( static_assert(is_same_v<decltype(5 * speed[metre / second] * (5 * time[second])),
is_same_v<decltype(5 * speed[metre / second] * (5 * time[second])), quantity<reference<length, metre>{}, int>>); 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])), 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> template<auto s>
concept invalid_operations = requires { concept invalid_operations = requires {
@ -142,11 +147,11 @@ static_assert(invalid_operations<time[second]>);
static_assert( static_assert(
is_same_v< is_same_v<
decltype(2 * length[metre] / (1 * time[second])), 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( static_assert(
is_same_v< is_same_v<
decltype(2 * (length[metre] / time[second])), 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])), static_assert(is_same_v<decltype(2 * (speed[metre / second])),
quantity<reference<speed, derived_unit<metre_, per<second_>>{}>{}, int>>); quantity<reference<speed, derived_unit<metre_, per<second_>>{}>{}, int>>);
@ -157,7 +162,7 @@ static_assert(
static_assert( static_assert(
is_same_v< is_same_v<
decltype(120 * length[kilometre] / (2 * time[hour])), 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(120 * length[kilometre] / (2 * time[hour]) == 60 * speed[kilometre / hour]);
static_assert( static_assert(
is_same_v< is_same_v<
@ -166,14 +171,14 @@ static_assert(
const auto duration = 2; const auto duration = 2;
return distance * length[kilometre] / (duration * time[hour]); 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( static_assert(
is_same_v<decltype(std::int64_t{120} * length[kilometre] / (2 * time[hour])), 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>>); std::int64_t>>);
static_assert( static_assert(
is_same_v<decltype(120.L * length[kilometre] / (2 * time[hour])), 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>>); long double>>);
static_assert(is_same_v<decltype(1. / 4 * area[square<metre>]), decltype(1. * area[square<metre>] / 4)>); 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::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::minute]), quantity<reference<length, nu::minute>{}, int>>);
static_assert(is_same_v<decltype(42 * (nu::length[nu::second] / nu::time[nu::second])), 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])), 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[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 * 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])), 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> template<auto dim, auto unit>
concept invalid_nu_unit = !requires { dim[unit]; }; concept invalid_nu_unit = !requires { dim[unit]; };

View File

@ -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 #ifdef __cpp_explicit_this_parameter
#define BASE_DIMENSION_(name, symbol) \ #define QUANTITY_SPEC_(name, ...) \
inline constexpr struct name##_ : base_dimension<symbol> { \ inline constexpr struct name##_ : quantity_spec<##__VA_ARGS__> { \
} name
#define DERIVED_DIMENSION_(name, base) \
inline constexpr struct name##_ : base { \
} name } name
#else #else
#define BASE_DIMENSION_(name, symbol) \ #define QUANTITY_SPEC_(name, ...) \
inline constexpr struct name##_ : base_dimension<name##_, symbol> { \ inline constexpr struct name##_ : quantity_spec<name##_, ##__VA_ARGS__> { \
} name
#define DERIVED_DIMENSION_(name, base) \
inline constexpr struct name##_ : derived_dimension<name##_, base> { \
} name } name
#endif #endif

View File

@ -35,12 +35,18 @@ using namespace units::detail;
using one_ = struct one; using one_ = struct one;
// base dimensions // base dimensions
BASE_DIMENSION_(length, "L");
BASE_DIMENSION_(time, "T");
BASE_DIMENSION_(mass, "M");
BASE_DIMENSION_(thermodynamic_temperature, "Θ");
// clang-format off // 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 // base units
inline constexpr struct second_ : named_unit<"s", time> {} second; inline constexpr struct second_ : named_unit<"s", time> {} second;
inline constexpr struct metre_ : named_unit<"m", length> {} metre; inline constexpr struct metre_ : named_unit<"m", length> {} metre;