mirror of
https://github.com/mpusz/mp-units.git
synced 2025-08-05 13:14:29 +02:00
feat: constant_unit
support added
This commit is contained in:
@@ -220,6 +220,53 @@ template<typename T>
|
|||||||
concept NamedUnit = Unit<T> && requires(T* t) { detail::to_base_specialization_of_named_unit(t); } &&
|
concept NamedUnit = Unit<T> && requires(T* t) { detail::to_base_specialization_of_named_unit(t); } &&
|
||||||
(!detail::is_specialization_of_named_unit<T>);
|
(!detail::is_specialization_of_named_unit<T>);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A unit of a physical constant
|
||||||
|
*
|
||||||
|
* Defines a unit of a physical constant together with its value encoded as a unit ratio.
|
||||||
|
*
|
||||||
|
* This allows moving all of the constant-related ratio manipulation to the compile-time domain
|
||||||
|
* (i.e. multiplying and then dividing by the same constant will eliminate the item from the final
|
||||||
|
* type).
|
||||||
|
* As a result we have faster runtime performance and no precision loss due to eager floating-point
|
||||||
|
* operations. Also, if the user prefers integral types for a quantity representation, this will
|
||||||
|
* not force the user to convert to a floating-point type right away. Only when a final quantity
|
||||||
|
* number needs to actually account for the constant value, the floating-point operation (if any)
|
||||||
|
* can be triggered lazily with the `quantity_cast<U>()`.
|
||||||
|
*
|
||||||
|
* For example:
|
||||||
|
*
|
||||||
|
* @code{.cpp}
|
||||||
|
* inline constexpr struct standard_gravity_unit :
|
||||||
|
* constant_unit<"g", mag<ratio{980'665, 100'000}> * metre / square<second>> {} standard_gravity_unit;
|
||||||
|
* @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 unit types in the source code. All operations
|
||||||
|
* are done on the objects. Contrarily, the unit 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 Symbol a short text representation of the constant
|
||||||
|
*
|
||||||
|
* @note Constant symbol is printed in the text output encapsulated inside square brackets `[]`
|
||||||
|
* and before any regular units
|
||||||
|
*/
|
||||||
|
template<basic_symbol_text Symbol, Unit auto U>
|
||||||
|
requires(!Symbol.empty())
|
||||||
|
struct constant_unit : named_unit<'[' + Symbol + ']', U> {};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<basic_symbol_text Symbol, Unit auto U>
|
||||||
|
void to_base_specialization_of_constant_unit(const volatile constant_unit<Symbol, U>*);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline constexpr bool is_derived_from_specialization_of_constant_unit =
|
||||||
|
requires(T * t) { to_base_specialization_of_constant_unit(t); };
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Prevents assignment of a prefix to specific units
|
* @brief Prevents assignment of a prefix to specific units
|
||||||
*
|
*
|
||||||
@@ -444,11 +491,22 @@ template<Unit T, typename... Us>
|
|||||||
|
|
||||||
[[nodiscard]] consteval auto get_canonical_unit(Unit auto u) { return get_canonical_unit_impl(u, u); }
|
[[nodiscard]] consteval auto get_canonical_unit(Unit auto u) { return get_canonical_unit_impl(u, u); }
|
||||||
|
|
||||||
|
template<Unit Lhs, Unit Rhs>
|
||||||
|
[[nodiscard]] consteval bool less(Lhs, Rhs)
|
||||||
|
{
|
||||||
|
if ((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>();
|
||||||
|
else
|
||||||
|
return is_derived_from_specialization_of_constant_unit<Lhs>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO What if the same unit will have different types (i.e. user will inherit its own type from `metre`)?
|
// TODO What if the same unit will have different types (i.e. user will inherit its own type from `metre`)?
|
||||||
// Is there a better way to sort units here? Some of them may not have symbol at all (like all units of
|
// Is there a better way to sort units here? Some of them may not have symbol at all (like all units of
|
||||||
// dimensionless quantities).
|
// dimensionless quantities).
|
||||||
template<Unit Lhs, Unit Rhs>
|
template<Unit Lhs, Unit Rhs>
|
||||||
struct unit_less : std::bool_constant<type_name<Lhs>() < type_name<Rhs>()> {};
|
struct unit_less : std::bool_constant<less(Lhs{}, Rhs{})> {};
|
||||||
|
|
||||||
template<typename T1, typename T2>
|
template<typename T1, typename T2>
|
||||||
using type_list_of_unit_less = expr_less<T1, T2, unit_less>;
|
using type_list_of_unit_less = expr_less<T1, T2, unit_less>;
|
||||||
|
@@ -80,6 +80,11 @@ inline constexpr struct mile_ : named_unit<"mi", mag<1760> * yard> {} mile;
|
|||||||
|
|
||||||
inline constexpr struct kilometre_ : decltype(si::kilo<metre>) {} kilometre;
|
inline constexpr struct kilometre_ : decltype(si::kilo<metre>) {} kilometre;
|
||||||
inline constexpr struct kilojoule_ : decltype(si::kilo<joule>) {} kilojoule;
|
inline constexpr struct kilojoule_ : decltype(si::kilo<joule>) {} kilojoule;
|
||||||
|
|
||||||
|
// physical constant units
|
||||||
|
inline constexpr struct standard_gravity_unit_ : constant_unit<"g", mag<ratio{980'665, 100'000}> * metre / square<second>> {} standard_gravity_unit;
|
||||||
|
inline constexpr struct speed_of_light_unit_ : constant_unit<"c", mag<299'792'458> * metre / second> {} speed_of_light_unit;
|
||||||
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
// concepts verification
|
// concepts verification
|
||||||
@@ -179,6 +184,17 @@ static_assert(joule != newton);
|
|||||||
|
|
||||||
static_assert(is_of_type<nu_second / nu_second, one_>);
|
static_assert(is_of_type<nu_second / nu_second, one_>);
|
||||||
|
|
||||||
|
// constant_unit
|
||||||
|
static_assert(is_of_type<standard_gravity_unit, standard_gravity_unit_>);
|
||||||
|
static_assert(
|
||||||
|
is_of_type<get_canonical_unit(standard_gravity_unit).reference_unit, derived_unit<metre_, per<power<second_, 2>>>>);
|
||||||
|
static_assert(get_canonical_unit(standard_gravity_unit).mag == mag<ratio{980'665, 100'000}>);
|
||||||
|
static_assert(interconvertible(standard_gravity_unit, standard_gravity_unit));
|
||||||
|
static_assert(interconvertible(standard_gravity_unit, metre / square<second>));
|
||||||
|
static_assert(standard_gravity_unit == standard_gravity_unit);
|
||||||
|
static_assert(standard_gravity_unit != metre / square<second>); // magnitude is different
|
||||||
|
static_assert(standard_gravity_unit.symbol == "[g]");
|
||||||
|
|
||||||
// prefixed_unit
|
// prefixed_unit
|
||||||
static_assert(is_of_type<kilometre, kilometre_>);
|
static_assert(is_of_type<kilometre, kilometre_>);
|
||||||
static_assert(is_of_type<get_canonical_unit(kilometre).reference_unit, metre_>);
|
static_assert(is_of_type<get_canonical_unit(kilometre).reference_unit, metre_>);
|
||||||
@@ -308,6 +324,16 @@ static_assert(is_of_type<one * metre * square<second> / gram, derived_unit<metre
|
|||||||
static_assert(is_of_type<(metre * square<second> / gram) * one, derived_unit<metre_, power<second_, 2>, per<gram_>>>);
|
static_assert(is_of_type<(metre * square<second> / gram) * one, derived_unit<metre_, power<second_, 2>, per<gram_>>>);
|
||||||
static_assert(is_of_type<metre * square<second> / gram * one, derived_unit<metre_, power<second_, 2>, per<gram_>>>);
|
static_assert(is_of_type<metre * square<second> / gram * one, derived_unit<metre_, power<second_, 2>, per<gram_>>>);
|
||||||
|
|
||||||
|
static_assert(is_of_type<standard_gravity_unit * gram, derived_unit<standard_gravity_unit_, gram_>>);
|
||||||
|
static_assert(is_of_type<gram * standard_gravity_unit, derived_unit<standard_gravity_unit_, gram_>>);
|
||||||
|
static_assert(is_of_type<standard_gravity_unit / gram, derived_unit<standard_gravity_unit_, per<gram_>>>);
|
||||||
|
static_assert(is_of_type<gram / standard_gravity_unit, derived_unit<gram_, per<standard_gravity_unit_>>>);
|
||||||
|
static_assert(is_of_type<standard_gravity_unit * gram / standard_gravity_unit, gram_>);
|
||||||
|
static_assert(is_of_type<speed_of_light_unit * gram * standard_gravity_unit,
|
||||||
|
derived_unit<speed_of_light_unit_, standard_gravity_unit_, gram_>>);
|
||||||
|
static_assert(is_of_type<gram * standard_gravity_unit * speed_of_light_unit,
|
||||||
|
derived_unit<speed_of_light_unit_, standard_gravity_unit_, gram_>>);
|
||||||
|
|
||||||
static_assert(std::is_same_v<decltype(1 / second * metre), decltype(metre / second)>);
|
static_assert(std::is_same_v<decltype(1 / second * metre), decltype(metre / second)>);
|
||||||
static_assert(std::is_same_v<decltype(metre * (1 / second)), decltype(metre / second)>);
|
static_assert(std::is_same_v<decltype(metre * (1 / second)), decltype(metre / second)>);
|
||||||
static_assert(std::is_same_v<decltype((metre / second) * (1 / second)), decltype(metre / second / second)>);
|
static_assert(std::is_same_v<decltype((metre / second) * (1 / second)), decltype(metre / second / second)>);
|
||||||
@@ -338,6 +364,14 @@ static_assert(
|
|||||||
static_assert(
|
static_assert(
|
||||||
is_of_type<get_canonical_unit(1 / pascal).reference_unit, derived_unit<metre_, power<second_, 2>, per<gram_>>>);
|
is_of_type<get_canonical_unit(1 / pascal).reference_unit, derived_unit<metre_, power<second_, 2>, per<gram_>>>);
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
is_of_type<get_canonical_unit(standard_gravity_unit).reference_unit, derived_unit<metre_, per<power<second_, 2>>>>);
|
||||||
|
static_assert(get_canonical_unit(standard_gravity_unit).mag == mag<ratio{980'665, 100'000}>);
|
||||||
|
static_assert(is_of_type<get_canonical_unit(standard_gravity_unit* gram).reference_unit,
|
||||||
|
derived_unit<gram_, metre_, per<power<second_, 2>>>>);
|
||||||
|
static_assert(is_of_type<get_canonical_unit(standard_gravity_unit / speed_of_light_unit).reference_unit,
|
||||||
|
derived_unit<one_, per<second_>>>);
|
||||||
|
|
||||||
// operations commutativity
|
// operations commutativity
|
||||||
constexpr auto u1 = mag<1000> * kilometre / hour;
|
constexpr auto u1 = mag<1000> * kilometre / hour;
|
||||||
static_assert(is_of_type<u1, scaled_unit<mag<1000>, derived_unit<kilometre_, per<hour_>>>>);
|
static_assert(is_of_type<u1, scaled_unit<mag<1000>, derived_unit<kilometre_, per<hour_>>>>);
|
||||||
@@ -496,6 +530,7 @@ static_assert(is_of_type<detail::common_unit(metre / second, kilometre / hour),
|
|||||||
scaled_unit<mag<ratio{1, 18}>, derived_unit<metre_, per<second_>>>>);
|
scaled_unit<mag<ratio{1, 18}>, derived_unit<metre_, per<second_>>>>);
|
||||||
static_assert(is_of_type<detail::common_unit(kilometre, mile), scaled_unit<mag<ratio{8, 125}>, metre_>>);
|
static_assert(is_of_type<detail::common_unit(kilometre, mile), scaled_unit<mag<ratio{8, 125}>, metre_>>);
|
||||||
static_assert(is_of_type<detail::common_unit(mile, kilometre), scaled_unit<mag<ratio{8, 125}>, metre_>>);
|
static_assert(is_of_type<detail::common_unit(mile, kilometre), scaled_unit<mag<ratio{8, 125}>, metre_>>);
|
||||||
|
static_assert(is_of_type<detail::common_unit(speed_of_light_unit, metre / second), derived_unit<metre_, per<second_>>>);
|
||||||
|
|
||||||
// unit symbols
|
// unit symbols
|
||||||
#ifdef __cpp_lib_constexpr_string
|
#ifdef __cpp_lib_constexpr_string
|
||||||
@@ -571,6 +606,11 @@ static_assert(unit_symbol(pow<1, 2>(metre)) == "m^(1/2)");
|
|||||||
static_assert(unit_symbol(pow<3, 5>(metre)) == "m^(3/5)");
|
static_assert(unit_symbol(pow<3, 5>(metre)) == "m^(3/5)");
|
||||||
static_assert(unit_symbol(pow<1, 2>(metre / second)) == "m^(1/2)/s^(1/2)");
|
static_assert(unit_symbol(pow<1, 2>(metre / second)) == "m^(1/2)/s^(1/2)");
|
||||||
|
|
||||||
|
// Physical constants
|
||||||
|
static_assert(unit_symbol(speed_of_light_unit) == "[c]");
|
||||||
|
static_assert(unit_symbol(gram * standard_gravity_unit * speed_of_light_unit) == "[c] [g] g");
|
||||||
|
static_assert(unit_symbol(gram / standard_gravity_unit) == "g/[g]");
|
||||||
|
|
||||||
#endif // __cpp_lib_constexpr_string
|
#endif // __cpp_lib_constexpr_string
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
Reference in New Issue
Block a user