refactor: library concepts cleanup

This commit is contained in:
Mateusz Pusz
2023-05-30 12:04:07 +02:00
parent d13165d9ae
commit 3713e08a95
8 changed files with 122 additions and 115 deletions

View File

@@ -42,18 +42,13 @@ 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 {
concept BaseDimension = requires(T* t) { to_base_base_dimension(t); } && (!is_specialization_of_base_dimension<T>);
template<typename T>
struct is_dimension_one : std::false_type {};
@@ -71,13 +66,13 @@ template<typename... Ts>
inline constexpr bool is_per_of_dims<per<Ts...>> =
(... && (BaseDimension<Ts> || is_dimension_one<Ts>::value || is_power_of_dim<Ts>));
} // namespace detail
template<typename T>
concept DerivedDimensionExpr =
BaseDimension<T> || detail::is_dimension_one<T>::value || detail::is_power_of_dim<T> || detail::is_per_of_dims<T>;
BaseDimension<T> || is_dimension_one<T>::value || is_power_of_dim<T> || is_per_of_dims<T>;
template<DerivedDimensionExpr... Expr>
} // namespace detail
template<detail::DerivedDimensionExpr... Expr>
struct derived_dimension;
namespace detail {
@@ -99,6 +94,6 @@ concept DerivedDimension = is_derived_from_specialization_of<T, derived_dimensio
* Satisfied by all dimension types for which either `BaseDimension<T>` or `DerivedDimension<T>` is `true`.
*/
template<typename T>
concept Dimension = BaseDimension<T> || detail::DerivedDimension<T>;
concept Dimension = detail::BaseDimension<T> || detail::DerivedDimension<T>;
} // namespace mp_units

View File

@@ -34,8 +34,20 @@ template<typename, auto...>
#endif
struct quantity_spec;
template<auto Q>
struct kind_of_;
namespace detail {
template<typename T>
inline constexpr bool is_specialization_of_kind_of = false;
template<auto Q>
inline constexpr bool is_specialization_of_kind_of<kind_of_<Q>> = true;
template<typename T>
concept QuantityKindSpec = is_specialization_of_kind_of<T>;
#ifdef __cpp_explicit_this_parameter
template<auto... Args>
void to_base_specialization_of_quantity_spec(const volatile quantity_spec<Args...>*);
@@ -70,10 +82,8 @@ inline constexpr bool is_specialization_of_quantity_spec<quantity_spec<T, Args..
* Satisfied by all types that derive from `quantity_spec`.
*/
template<typename T>
concept NamedQuantitySpec = requires(T* t) { detail::to_base_specialization_of_quantity_spec(t); } &&
(!detail::is_specialization_of_quantity_spec<T>);
} // namespace detail
concept NamedQuantitySpec = requires(T* t) { to_base_specialization_of_quantity_spec(t); } &&
(!is_specialization_of_quantity_spec<T>)&&(!QuantityKindSpec<T>);
/**
* @brief Concept matching all named base quantity specification types
@@ -82,10 +92,7 @@ concept NamedQuantitySpec = requires(T* t) { detail::to_base_specialization_of_q
* as a template parameter.
*/
template<typename T>
concept BaseQuantitySpec =
detail::NamedQuantitySpec<T> && requires(T* t) { detail::to_base_specialization_of_base_quantity_spec(t); };
namespace detail {
concept BaseQuantitySpec = NamedQuantitySpec<T> && requires(T* t) { to_base_specialization_of_base_quantity_spec(t); };
template<typename T>
struct is_dimensionless : std::false_type {};
@@ -103,15 +110,14 @@ template<typename... Ts>
inline constexpr bool is_per_of_quantity_specs<per<Ts...>> =
(... && (NamedQuantitySpec<Ts> || is_dimensionless<Ts>::value || is_power_of_quantity_spec<Ts>));
} // namespace detail
template<typename T>
concept IntermediateDerivedQuantitySpecExpr =
detail::NamedQuantitySpec<T> || detail::is_dimensionless<T>::value || detail::is_power_of_quantity_spec<T> ||
detail::is_per_of_quantity_specs<T>;
} // namespace detail
template<IntermediateDerivedQuantitySpecExpr... Expr>
template<detail::IntermediateDerivedQuantitySpecExpr... Expr>
struct derived_quantity_spec;
namespace detail {
@@ -129,45 +135,9 @@ concept IntermediateDerivedQuantitySpec = is_specialization_of<T, derived_quanti
} // namespace detail
template<typename T>
concept QuantitySpec = detail::NamedQuantitySpec<T> || detail::IntermediateDerivedQuantitySpec<T>;
namespace detail {
template<typename T>
inline constexpr bool is_quantity_spec_with_no_specifiers = false;
concept QuantitySpec =
detail::NamedQuantitySpec<T> || detail::IntermediateDerivedQuantitySpec<T> || detail::QuantityKindSpec<T>;
template<typename T>
concept QuantitySpecWithNoSpecifiers = is_quantity_spec_with_no_specifiers<T>;
} // namespace detail
template<QuantitySpec Q>
[[nodiscard]] consteval QuantitySpec auto get_kind(Q q);
template<detail::QuantitySpecWithNoSpecifiers auto Q>
requires(get_kind(Q) == Q)
struct kind_of_;
namespace detail {
template<auto Q>
void to_base_specialization_of_kind_of(const volatile kind_of_<Q>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_kind_of =
requires(T* t) { to_base_specialization_of_kind_of(t); };
} // namespace detail
template<typename T>
concept QuantityKindSpec = QuantitySpec<T> && detail::is_derived_from_specialization_of_kind_of<T>;
namespace detail {
template<typename T>
requires QuantitySpec<T> && (!QuantityKindSpec<T>)
inline constexpr bool is_quantity_spec_with_no_specifiers<T> = true;
} // namespace detail
} // namespace mp_units

View File

@@ -50,27 +50,30 @@ namespace mp_units {
*/
enum class quantity_character { scalar, vector, tensor };
namespace detail {
template<typename T, typename U>
concept common_type_with_ = // exposition only
concept CommonTypeWith =
std::same_as<std::common_type_t<T, U>, std::common_type_t<U, T>> &&
std::constructible_from<std::common_type_t<T, U>, T> && std::constructible_from<std::common_type_t<T, U>, U>;
template<typename T, typename U = T>
concept scalable_number_ = // exposition only
concept ScalableNumber =
std::regular_invocable<std::multiplies<>, T, U> && std::regular_invocable<std::divides<>, T, U>;
template<typename T>
concept castable_number_ = // exposition only
common_type_with_<T, std::intmax_t> && scalable_number_<std::common_type_t<T, std::intmax_t>>;
concept CastableNumber = CommonTypeWith<T, std::intmax_t> && ScalableNumber<std::common_type_t<T, std::intmax_t>>;
// TODO Fix it according to sudo_cast implementation
template<typename T>
concept scalable_ = // exposition only
castable_number_<T> || (requires { typename T::value_type; } && castable_number_<typename T::value_type> &&
scalable_number_<T, std::common_type_t<typename T::value_type, std::intmax_t>>);
concept Scalable =
CastableNumber<T> || (requires { typename T::value_type; } && CastableNumber<typename T::value_type> &&
ScalableNumber<T, std::common_type_t<typename T::value_type, std::intmax_t>>);
} // namespace detail
template<typename T>
concept Representation = (is_scalar<T> || is_vector<T> || is_tensor<T>)&&std::regular<T> && scalable_<T>;
concept Representation = (is_scalar<T> || is_vector<T> || is_tensor<T>)&&std::regular<T> && detail::Scalable<T>;
template<typename T, quantity_character Ch>
concept RepresentationOf = Representation<T> && ((Ch == quantity_character::scalar && is_scalar<T>) ||

View File

@@ -50,27 +50,42 @@ struct scaled_unit;
template<basic_symbol_text Symbol, auto...>
struct named_unit;
template<basic_symbol_text Symbol, Unit auto U>
requires(!Symbol.empty())
struct constant_unit;
namespace detail {
template<basic_symbol_text Symbol, auto... Args>
void to_base_specialization_of_named_unit(const volatile named_unit<Symbol, Args...>*);
template<typename T>
inline constexpr bool is_derived_from_specialization_of_named_unit =
requires(T* t) { to_base_specialization_of_named_unit(t); };
template<typename T>
inline constexpr bool is_specialization_of_named_unit = false;
template<basic_symbol_text Symbol, auto... Args>
inline constexpr bool is_specialization_of_named_unit<named_unit<Symbol, Args...>> = true;
} // namespace detail
template<basic_symbol_text Symbol, 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); };
/**
* @brief A concept matching all units with special names
*
* Satisfied by all unit types derived from the specialization of `named_unit`.
* Satisfied by all unit types derived from the specialization of `named_unit` (but not constant units).
*/
template<typename T>
concept NamedUnit = Unit<T> && requires(T* t) { detail::to_base_specialization_of_named_unit(t); } &&
(!detail::is_specialization_of_named_unit<T>);
concept NamedUnit = Unit<T> && detail::is_derived_from_specialization_of_named_unit<T> &&
(!detail::is_derived_from_specialization_of_constant_unit<T>);
} // namespace detail
/**
* @brief Prevents assignment of a prefix to specific units
@@ -79,14 +94,14 @@ concept NamedUnit = Unit<T> && requires(T* t) { detail::to_base_specialization_o
* `hour` or `degree_Celsius`. For those a partial specialization with the value `false` should be
* provided.
*/
template<NamedUnit auto V>
inline constexpr bool unit_can_be_prefixed = true;
template<Unit auto V>
inline constexpr bool unit_can_be_prefixed = detail::NamedUnit<std::remove_const_t<decltype(V)>>;
/**
* @brief A concept to be used to define prefixes for a unit
*/
template<typename T>
concept PrefixableUnit = NamedUnit<T> && unit_can_be_prefixed<T{}>;
concept PrefixableUnit = detail::NamedUnit<T> && unit_can_be_prefixed<T{}>;
namespace detail {
@@ -100,14 +115,18 @@ inline constexpr bool is_per_of_units = false;
template<typename... Ts>
inline constexpr bool is_per_of_units<per<Ts...>> = (... && (Unit<Ts> || is_power_of_unit<Ts>));
} // namespace detail
template<typename T>
concept DerivedUnitExpr = Unit<T> || detail::is_power_of_unit<T> || detail::is_per_of_units<T>;
template<DerivedUnitExpr... Expr>
} // namespace detail
template<detail::DerivedUnitExpr... Expr>
struct derived_unit;
template<basic_symbol_text Symbol, Magnitude auto M, PrefixableUnit auto U>
requires(!Symbol.empty())
struct prefixed_unit;
namespace detail {
template<auto M, typename U>
@@ -119,9 +138,28 @@ void is_unit_impl(const named_unit<Symbol, Args...>*);
template<typename... Expr>
void is_unit_impl(const derived_unit<Expr...>*);
template<typename T>
inline constexpr bool is_specialization_of_unit = false;
template<basic_symbol_text Symbol, auto... Args>
inline constexpr bool is_specialization_of_unit<named_unit<Symbol, Args...>> = true;
template<typename T>
inline constexpr bool is_specialization_of_constant_unit = false;
template<basic_symbol_text Symbol, auto U>
inline constexpr bool is_specialization_of_constant_unit<constant_unit<Symbol, U>> = true;
template<typename T>
inline constexpr bool is_specialization_of_prefixed_unit = false;
template<basic_symbol_text Symbol, Magnitude auto M, PrefixableUnit auto U>
inline constexpr bool is_specialization_of_prefixed_unit<prefixed_unit<Symbol, M, U>> = true;
template<typename T>
requires requires(T* t) { is_unit_impl(t); }
inline constexpr bool is_unit<T> = true;
inline constexpr bool is_unit<T> = !is_specialization_of_named_unit<T> && !is_specialization_of_constant_unit<T> &&
!is_specialization_of_prefixed_unit<T>;
template<Unit U>
[[nodiscard]] consteval bool has_associated_quantity(U);

View File

@@ -114,7 +114,7 @@ using type_list_of_base_dimension_less = expr_less<T1, T2, base_dimension_less>;
* @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<DerivedDimensionExpr... Expr>
template<detail::DerivedDimensionExpr... Expr>
struct derived_dimension : detail::expr_fractions<detail::is_dimension_one, Expr...> {};
/**
@@ -179,7 +179,7 @@ template<std::intmax_t Num, std::intmax_t Den = 1, Dimension D>
requires detail::non_zero<Den>
[[nodiscard]] consteval Dimension auto pow(D d)
{
if constexpr (BaseDimension<D>) {
if constexpr (detail::BaseDimension<D>) {
if constexpr (Den == 1)
return derived_dimension<power<D, Num>>{};
else

View File

@@ -47,16 +47,15 @@ template<QuantityLike Q>
using quantity_like_type = quantity<quantity_like_traits<Q>::reference, typename quantity_like_traits<Q>::rep>;
template<typename T, typename Arg>
concept RepSafeConstructibleFrom = // exposition only
concept RepSafeConstructibleFrom =
std::constructible_from<T, Arg> && (treat_as_floating_point<T> || !treat_as_floating_point<Arg>);
template<auto UFrom, auto UTo>
concept IntegralConversionFactor = // exposition only
Unit<decltype(UFrom)> && Unit<decltype(UTo)> &&
concept IntegralConversionFactor = Unit<decltype(UFrom)> && Unit<decltype(UTo)> &&
is_integral(get_canonical_unit(UFrom).mag / get_canonical_unit(UTo).mag);
template<typename QFrom, typename QTo>
concept QuantityConvertibleTo = // exposition only
concept QuantityConvertibleTo =
Quantity<QFrom> && Quantity<QTo> && implicitly_convertible(QFrom::quantity_spec, QTo::quantity_spec) &&
convertible(QFrom::unit, QTo::unit) && requires(QFrom q) { detail::sudo_cast<QTo>(q); } &&
(treat_as_floating_point<typename QTo::rep> ||

View File

@@ -189,15 +189,15 @@ inline constexpr struct is_kind {
* @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>
template<detail::BaseDimension auto Dim, one_of<quantity_character> auto... Args>
requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
struct quantity_spec<Dim, Args...> : detail::quantity_spec_interface {
#else
template<typename Self, BaseDimension auto Dim, one_of<quantity_character> auto... Args>
template<typename Self, detail::BaseDimension auto Dim, one_of<quantity_character> auto... Args>
requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
struct quantity_spec<Self, Dim, Args...> : detail::quantity_spec_interface<Self> {
#endif
static constexpr BaseDimension auto dimension = Dim;
static constexpr detail::BaseDimension auto dimension = Dim;
static constexpr quantity_character character = detail::quantity_character_init<Args...>(quantity_character::scalar);
};
@@ -405,7 +405,7 @@ struct quantity_spec<Self, QS, Eq, Args...> : quantity_spec<Self, QS, Args...> {
* @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<IntermediateDerivedQuantitySpecExpr... Expr>
template<detail::IntermediateDerivedQuantitySpecExpr... Expr>
struct derived_quantity_spec :
detail::quantity_spec_interface<derived_quantity_spec<Expr...>>,
detail::expr_fractions<detail::is_dimensionless, Expr...> {
@@ -426,18 +426,31 @@ struct derived_quantity_spec :
*/
QUANTITY_SPEC(dimensionless, derived_quantity_spec<>{});
template<QuantitySpec Q>
[[nodiscard]] consteval QuantitySpec auto get_kind(Q q);
/**
* @brief Quantity kind specifier
*
* Specifies that the provided `Q` should be treated as a quantity kind.
*/
template<auto Q>
struct kind_of_;
namespace detail {
template<typename T>
concept QuantitySpecWithNoSpecifiers = detail::NamedQuantitySpec<T> || detail::IntermediateDerivedQuantitySpec<T>;
} // namespace detail
template<detail::QuantitySpecWithNoSpecifiers auto Q>
requires(get_kind(Q) == Q)
#ifdef __cpp_explicit_this_parameter
struct kind_of_ : Q {
struct kind_of_<Q> : Q {
};
#else
struct kind_of_ : quantity_spec<kind_of_<Q>, Q> {
struct kind_of_<Q> : quantity_spec<kind_of_<Q>, Q> {
};
#endif
@@ -492,13 +505,13 @@ template<QuantitySpec Lhs, QuantitySpec Rhs>
return is_same_v<Lhs, Rhs>;
}
template<QuantityKindSpec Lhs, QuantityKindSpec Rhs>
template<detail::QuantityKindSpec Lhs, detail::QuantityKindSpec Rhs>
[[nodiscard]] consteval bool operator==(Lhs, Rhs)
{
return is_same_v<Lhs, Rhs>;
}
template<QuantitySpec Lhs, QuantityKindSpec Rhs>
template<QuantitySpec Lhs, detail::QuantityKindSpec Rhs>
[[nodiscard]] consteval bool operator==(Lhs, Rhs rhs)
{
return is_same_v<Lhs, std::remove_const_t<decltype(remove_kind(rhs))>>;
@@ -1374,7 +1387,7 @@ template<typename Self, NamedQuantitySpec auto QS, auto... Args>
template<QuantitySpec Q>
[[nodiscard]] consteval auto remove_kind(Q q)
{
if constexpr (QuantityKindSpec<Q>) {
if constexpr (detail::QuantityKindSpec<Q>) {
if constexpr (requires { Q::_parent_; })
return Q::_parent_;
else
@@ -1393,7 +1406,7 @@ template<QuantitySpec Q>
return false;
};
if constexpr (QuantityKindSpec<Q>) {
if constexpr (detail::QuantityKindSpec<Q>) {
return remove_kind(q);
} else if constexpr (defined_as_kind(q)) {
return q;
@@ -1424,11 +1437,11 @@ template<QuantitySpec Q1, QuantitySpec Q2>
else if constexpr (get_kind(q1) != get_kind(q2) && std::derived_from<std::remove_const_t<decltype(get_kind(q2))>,
std::remove_const_t<decltype(get_kind(q1))>>)
return remove_kind(q2);
else if constexpr ((QuantityKindSpec<Q1> && !QuantityKindSpec<Q2>) ||
else if constexpr ((detail::QuantityKindSpec<Q1> && !detail::QuantityKindSpec<Q2>) ||
(detail::IntermediateDerivedQuantitySpec<QQ1> && detail::NamedQuantitySpec<QQ2> &&
implicitly_convertible(q1, q2)))
return q2;
else if constexpr ((!QuantityKindSpec<Q1> && QuantityKindSpec<Q2>) ||
else if constexpr ((!detail::QuantityKindSpec<Q1> && detail::QuantityKindSpec<Q2>) ||
(detail::NamedQuantitySpec<QQ1> && detail::IntermediateDerivedQuantitySpec<QQ2> &&
implicitly_convertible(q2, q1)))
return q1;

View File

@@ -109,8 +109,8 @@ struct named_unit;
* @tparam Symbol a short text representation of the unit
* @tparam QuantitySpec a specification of a base quantity to be measured with this unit
*/
template<basic_symbol_text Symbol, QuantityKindSpec auto QS>
requires(!Symbol.empty()) && BaseDimension<std::remove_const_t<decltype(QS.dimension)>>
template<basic_symbol_text Symbol, detail::QuantityKindSpec auto QS>
requires(!Symbol.empty()) && detail::BaseDimension<std::remove_const_t<decltype(QS.dimension)>>
struct named_unit<Symbol, QS> {
static constexpr auto symbol = Symbol; ///< Unique base unit identifier
static constexpr auto quantity_spec = QS;
@@ -155,7 +155,7 @@ struct named_unit<Symbol, U> : std::remove_const_t<decltype(U)> {
* @tparam Unit a unit for which we provide a special name
* @tparam QuantitySpec a specification of a quantity to be measured with this unit
*/
template<basic_symbol_text Symbol, AssociatedUnit auto U, QuantityKindSpec auto QS>
template<basic_symbol_text Symbol, AssociatedUnit auto U, detail::QuantityKindSpec auto QS>
requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension)
struct named_unit<Symbol, U, QS> : std::remove_const_t<decltype(U)> {
static constexpr auto symbol = Symbol; ///< Unique unit identifier
@@ -197,17 +197,6 @@ 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, 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 A prefixed unit
*
@@ -288,7 +277,7 @@ struct is_one : std::false_type {};
* @note User should not instantiate this type! It is not exported from the C++ module. The library will
* instantiate this type automatically based on the unit arithmetic equation provided by the user.
*/
template<DerivedUnitExpr... Expr>
template<detail::DerivedUnitExpr... Expr>
struct derived_unit : detail::expr_fractions<detail::is_one, Expr...> {};
/**
@@ -328,7 +317,7 @@ struct canonical_unit {
U reference_unit;
};
template<Unit T, basic_symbol_text Symbol, BaseQuantitySpec auto Q>
template<Unit T, basic_symbol_text Symbol, detail::QuantityKindSpec auto Q>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q>&);
template<Unit T, basic_symbol_text Symbol>
@@ -350,7 +339,7 @@ template<Unit T, auto M, typename U>
return canonical_unit{M * base.mag, base.reference_unit};
}
template<Unit T, basic_symbol_text Symbol, BaseQuantitySpec auto Q>
template<Unit T, basic_symbol_text Symbol, detail::QuantityKindSpec auto Q>
[[nodiscard]] consteval auto get_canonical_unit_impl(T t, const named_unit<Symbol, Q>&)
{
return canonical_unit{mag<1>, t};