feat: constant_unit support added

This commit is contained in:
Mateusz Pusz
2022-11-10 09:55:43 -10:00
parent 7b6fc18e20
commit d75f22ecc4
2 changed files with 99 additions and 1 deletions

View File

@@ -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>;

View File

@@ -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