From 5f67523a8a6c47dc9da07e4b2875ea5426dca126 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 13 Jun 2024 12:05:16 +0200 Subject: [PATCH] feat: :boom: from now on quantity specifications have to be marked as `final` --- .../character_of_a_quantity.md | 12 +- .../framework_basics/design_overview.md | 24 ++-- .../dimensionless_quantities.md | 12 +- .../simple_and_typed_quantities.md | 16 +-- .../framework_basics/systems_of_quantities.md | 52 ++++---- .../framework_basics/value_conversions.md | 4 +- .../use_cases/wide_compatibility.md | 8 +- src/core/CMakeLists.txt | 2 +- ...ommon_base.h => quantity_spec_hierarchy.h} | 15 +++ src/core/include/mp-units/compat_macros.h | 8 +- .../mp-units/framework/quantity_spec.h | 122 +++++++++++------- .../framework/quantity_spec_concepts.h | 22 +--- test/static/concepts_test.cpp | 30 ++--- test/static/quantity_spec_test.cpp | 38 +++--- test/static/test_tools.h | 8 +- 15 files changed, 207 insertions(+), 166 deletions(-) rename src/core/include/mp-units/bits/{get_common_base.h => quantity_spec_hierarchy.h} (86%) diff --git a/docs/users_guide/framework_basics/character_of_a_quantity.md b/docs/users_guide/framework_basics/character_of_a_quantity.md index ad366078..26b358af 100644 --- a/docs/users_guide/framework_basics/character_of_a_quantity.md +++ b/docs/users_guide/framework_basics/character_of_a_quantity.md @@ -97,15 +97,15 @@ enumeration can be appended to the `quantity_spec` describing such a quantity ty === "C++23" ```cpp - inline constexpr struct position_vector : quantity_spec {} position_vector; - inline constexpr struct displacement : quantity_spec {} displacement; + inline constexpr struct position_vector final : quantity_spec {} position_vector; + inline constexpr struct displacement final : quantity_spec {} displacement; ``` === "C++20" ```cpp - inline constexpr struct position_vector : quantity_spec {} position_vector; - inline constexpr struct displacement : quantity_spec {} displacement; + inline constexpr struct position_vector final : quantity_spec {} position_vector; + inline constexpr struct displacement final : quantity_spec {} displacement; ``` === "Portable" @@ -126,13 +126,13 @@ character override is needed): === "C++23" ```cpp - inline constexpr struct velocity : quantity_spec {} velocity; + inline constexpr struct velocity final : quantity_spec {} velocity; ``` === "C++20" ```cpp - inline constexpr struct velocity : quantity_spec {} velocity; + inline constexpr struct velocity final : quantity_spec {} velocity; ``` === "Portable" diff --git a/docs/users_guide/framework_basics/design_overview.md b/docs/users_guide/framework_basics/design_overview.md index 3f557249..df6554e4 100644 --- a/docs/users_guide/framework_basics/design_overview.md +++ b/docs/users_guide/framework_basics/design_overview.md @@ -71,9 +71,9 @@ provided in the [quantity specification](../../appendix/glossary.md#quantity_spe === "C++23" ```cpp - inline constexpr struct length : quantity_spec {} length; - inline constexpr struct time : quantity_spec {} time; - inline constexpr struct speed : quantity_spec {} speed; + inline constexpr struct length final : quantity_spec {} length; + inline constexpr struct time final : quantity_spec {} time; + inline constexpr struct speed final : quantity_spec {} speed; static_assert(speed.dimension == dim_length / dim_time); ``` @@ -81,9 +81,9 @@ provided in the [quantity specification](../../appendix/glossary.md#quantity_spe === "C++20" ```cpp - inline constexpr struct length : quantity_spec {} length; - inline constexpr struct time : quantity_spec {} time; - inline constexpr struct speed : quantity_spec {} speed; + inline constexpr struct length final : quantity_spec {} length; + inline constexpr struct time final : quantity_spec {} time; + inline constexpr struct speed final : quantity_spec {} speed; static_assert(speed.dimension == dim_length / dim_time); ``` @@ -183,17 +183,17 @@ Quantity specification can be defined by the user in one of the following ways: === "C++23" ```cpp - inline constexpr struct length : quantity_spec {} length; - inline constexpr struct height : quantity_spec {} height; - inline constexpr struct speed : quantity_spec {} speed; + inline constexpr struct length final : quantity_spec {} length; + inline constexpr struct height final : quantity_spec {} height; + inline constexpr struct speed final : quantity_spec {} speed; ``` === "C++20" ```cpp - inline constexpr struct length : quantity_spec {} length; - inline constexpr struct height : quantity_spec {} height; - inline constexpr struct speed : quantity_spec {} speed; + inline constexpr struct length final : quantity_spec {} length; + inline constexpr struct height final : quantity_spec {} height; + inline constexpr struct speed final : quantity_spec {} speed; ``` === "Portable" diff --git a/docs/users_guide/framework_basics/dimensionless_quantities.md b/docs/users_guide/framework_basics/dimensionless_quantities.md index 05bbca18..16d272f2 100644 --- a/docs/users_guide/framework_basics/dimensionless_quantities.md +++ b/docs/users_guide/framework_basics/dimensionless_quantities.md @@ -217,17 +217,17 @@ to the quantity specification: === "C++23" ```cpp - inline constexpr struct angular_measure : quantity_spec {} angular_measure; - inline constexpr struct solid_angular_measure : quantity_spec(radius), is_kind> {} solid_angular_measure; - inline constexpr struct storage_capacity : quantity_spec {} storage_capacity; + inline constexpr struct angular_measure final : quantity_spec {} angular_measure; + inline constexpr struct solid_angular_measure final : quantity_spec(radius), is_kind> {} solid_angular_measure; + inline constexpr struct storage_capacity final : quantity_spec {} storage_capacity; ``` === "C++20" ```cpp - inline constexpr struct angular_measure : quantity_spec {} angular_measure; - inline constexpr struct solid_angular_measure : quantity_spec(radius), is_kind> {} solid_angular_measure; - inline constexpr struct storage_capacity : quantity_spec {} storage_capacity; + inline constexpr struct angular_measure final : quantity_spec {} angular_measure; + inline constexpr struct solid_angular_measure final : quantity_spec(radius), is_kind> {} solid_angular_measure; + inline constexpr struct storage_capacity final : quantity_spec {} storage_capacity; ``` === "Portable" diff --git a/docs/users_guide/framework_basics/simple_and_typed_quantities.md b/docs/users_guide/framework_basics/simple_and_typed_quantities.md index 8dc98ee8..178c36e2 100644 --- a/docs/users_guide/framework_basics/simple_and_typed_quantities.md +++ b/docs/users_guide/framework_basics/simple_and_typed_quantities.md @@ -316,13 +316,13 @@ Let's see another example: using namespace mp_units; // add a custom quantity type of kind isq::length - inline constexpr struct horizontal_length - : quantity_spec {} horizontal_length; + inline constexpr struct horizontal_length final : + quantity_spec {} horizontal_length; // add a custom derived quantity type of kind isq::area // with a constrained quantity equation - inline constexpr struct horizontal_area - : quantity_spec {} horizontal_area; + inline constexpr struct horizontal_area final : + quantity_spec {} horizontal_area; class StorageTank { quantity base_; @@ -429,13 +429,13 @@ Let's see another example: using namespace mp_units; // add a custom quantity type of kind isq::length - inline constexpr struct horizontal_length - : quantity_spec {} horizontal_length; + inline constexpr struct horizontal_length final : + quantity_spec {} horizontal_length; // add a custom derived quantity type of kind isq::area // with a constrained quantity equation - inline constexpr struct horizontal_area - : quantity_spec {} horizontal_area; + inline constexpr struct horizontal_area final : + quantity_spec {} horizontal_area; class StorageTank { quantity base_; diff --git a/docs/users_guide/framework_basics/systems_of_quantities.md b/docs/users_guide/framework_basics/systems_of_quantities.md index 65605f2b..07660e1d 100644 --- a/docs/users_guide/framework_basics/systems_of_quantities.md +++ b/docs/users_guide/framework_basics/systems_of_quantities.md @@ -148,45 +148,45 @@ For example, here is how the above quantity kind tree can be modeled in the libr === "C++23" ```cpp - inline constexpr struct length : quantity_spec {} length; - inline constexpr struct width : quantity_spec {} width; + inline constexpr struct length final : quantity_spec {} length; + inline constexpr struct width final : quantity_spec {} width; inline constexpr auto breadth = width; - inline constexpr struct height : quantity_spec {} height; + inline constexpr struct height final : quantity_spec {} height; inline constexpr auto depth = height; inline constexpr auto altitude = height; - inline constexpr struct thickness : quantity_spec {} thickness; - inline constexpr struct diameter : quantity_spec {} diameter; - inline constexpr struct radius : quantity_spec {} radius; - inline constexpr struct radius_of_curvature : quantity_spec {} radius_of_curvature; - inline constexpr struct path_length : quantity_spec {} path_length; + inline constexpr struct thickness final : quantity_spec {} thickness; + inline constexpr struct diameter final : quantity_spec {} diameter; + inline constexpr struct radius final : quantity_spec {} radius; + inline constexpr struct radius_of_curvature final : quantity_spec {} radius_of_curvature; + inline constexpr struct path_length final : quantity_spec {} path_length; inline constexpr auto arc_length = path_length; - inline constexpr struct distance : quantity_spec {} distance; - inline constexpr struct radial_distance : quantity_spec {} radial_distance; - inline constexpr struct wavelength : quantity_spec {} wavelength; - inline constexpr struct position_vector : quantity_spec {} position_vector; - inline constexpr struct displacement : quantity_spec {} displacement; + inline constexpr struct distance final : quantity_spec {} distance; + inline constexpr struct radial_distance final : quantity_spec {} radial_distance; + inline constexpr struct wavelength final : quantity_spec {} wavelength; + inline constexpr struct position_vector final : quantity_spec {} position_vector; + inline constexpr struct displacement final : quantity_spec {} displacement; ``` === "C++20" ```cpp - inline constexpr struct length : quantity_spec {} length; - inline constexpr struct width : quantity_spec {} width; + inline constexpr struct length final : quantity_spec {} length; + inline constexpr struct width final : quantity_spec {} width; inline constexpr auto breadth = width; - inline constexpr struct height : quantity_spec {} height; + inline constexpr struct height final : quantity_spec {} height; inline constexpr auto depth = height; inline constexpr auto altitude = height; - inline constexpr struct thickness : quantity_spec {} thickness; - inline constexpr struct diameter : quantity_spec {} diameter; - inline constexpr struct radius : quantity_spec {} radius; - inline constexpr struct radius_of_curvature : quantity_spec {} radius_of_curvature; - inline constexpr struct path_length : quantity_spec {} path_length; + inline constexpr struct thickness final : quantity_spec {} thickness; + inline constexpr struct diameter final : quantity_spec {} diameter; + inline constexpr struct radius final : quantity_spec {} radius; + inline constexpr struct radius_of_curvature final : quantity_spec {} radius_of_curvature; + inline constexpr struct path_length final : quantity_spec {} path_length; inline constexpr auto arc_length = path_length; - inline constexpr struct distance : quantity_spec {} distance; - inline constexpr struct radial_distance : quantity_spec {} radial_distance; - inline constexpr struct wavelength : quantity_spec {} wavelength; - inline constexpr struct position_vector : quantity_spec {} position_vector; - inline constexpr struct displacement : quantity_spec {} displacement; + inline constexpr struct distance final : quantity_spec {} distance; + inline constexpr struct radial_distance final : quantity_spec {} radial_distance; + inline constexpr struct wavelength final : quantity_spec {} wavelength; + inline constexpr struct position_vector final : quantity_spec {} position_vector; + inline constexpr struct displacement final : quantity_spec {} displacement; ``` === "Portable" diff --git a/docs/users_guide/framework_basics/value_conversions.md b/docs/users_guide/framework_basics/value_conversions.md index c1df3e6f..5e091206 100644 --- a/docs/users_guide/framework_basics/value_conversions.md +++ b/docs/users_guide/framework_basics/value_conversions.md @@ -86,7 +86,7 @@ the `value_cast(q)` which always returns the most precise result: ```cpp inline constexpr struct dim_currency final : base_dimension<"$"> {} dim_currency; - inline constexpr struct currency : quantity_spec {} currency; + inline constexpr struct currency final : quantity_spec {} currency; inline constexpr struct us_dollar final : named_unit<"USD", kind_of> {} us_dollar; inline constexpr struct scaled_us_dollar final : named_unit<"USD_s", mag_power<10, -8> * us_dollar> {} scaled_us_dollar; @@ -106,7 +106,7 @@ the `value_cast(q)` which always returns the most precise result: ```cpp inline constexpr struct dim_currency final : base_dimension<"$"> {} dim_currency; - inline constexpr struct currency : quantity_spec {} currency; + inline constexpr struct currency final : quantity_spec {} currency; inline constexpr struct us_dollar final : named_unit<"USD", kind_of> {} us_dollar; inline constexpr struct scaled_us_dollar final : named_unit<"USD_s", mag_power<10, -8> * us_dollar> {} scaled_us_dollar; diff --git a/docs/users_guide/use_cases/wide_compatibility.md b/docs/users_guide/use_cases/wide_compatibility.md index c8aa6211..b3420d12 100644 --- a/docs/users_guide/use_cases/wide_compatibility.md +++ b/docs/users_guide/use_cases/wide_compatibility.md @@ -29,7 +29,7 @@ your code using **mp-units**: // ... - inline constexpr struct horizontal_length : quantity_spec {} horizontal_length; + inline constexpr struct horizontal_length final : quantity_spec {} horizontal_length; // ... @@ -45,7 +45,7 @@ your code using **mp-units**: // ... - inline constexpr struct horizontal_length : quantity_spec {} horizontal_length; + inline constexpr struct horizontal_length final : quantity_spec {} horizontal_length; // ... @@ -65,7 +65,7 @@ your code using **mp-units**: // ... - inline constexpr struct horizontal_length : quantity_spec {} horizontal_length; + inline constexpr struct horizontal_length final : quantity_spec {} horizontal_length; // ... @@ -85,7 +85,7 @@ your code using **mp-units**: // ... - inline constexpr struct horizontal_length : quantity_spec {} horizontal_length; + inline constexpr struct horizontal_length final : quantity_spec {} horizontal_length; // ... diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9f3d4f5b..e49a1203 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -35,10 +35,10 @@ add_mp_units_module( core mp-units-core HEADERS include/mp-units/bits/core_gmf.h include/mp-units/bits/get_associated_quantity.h - include/mp-units/bits/get_common_base.h include/mp-units/bits/hacks.h include/mp-units/bits/math_concepts.h include/mp-units/bits/module_macros.h + include/mp-units/bits/quantity_spec_hierarchy.h include/mp-units/bits/ratio.h include/mp-units/bits/sudo_cast.h include/mp-units/bits/text_tools.h diff --git a/src/core/include/mp-units/bits/get_common_base.h b/src/core/include/mp-units/bits/quantity_spec_hierarchy.h similarity index 86% rename from src/core/include/mp-units/bits/get_common_base.h rename to src/core/include/mp-units/bits/quantity_spec_hierarchy.h index e703a41f..1346fa89 100644 --- a/src/core/include/mp-units/bits/get_common_base.h +++ b/src/core/include/mp-units/bits/quantity_spec_hierarchy.h @@ -88,4 +88,19 @@ template return get_common_base_for_hierarchy_of_equal_length(a, hierarchy_path_advance(b)); } +template +[[nodiscard]] consteval auto is_child_of(Child ch, Parent) +{ + if constexpr (Child{} == Parent{}) + return std::true_type{}; + else { + constexpr auto child_length = hierarchy_path_length(Child{}); + constexpr auto parent_length = hierarchy_path_length(Parent{}); + if constexpr (parent_length > child_length) + return std::false_type{}; + else + return std::bool_constant(ch) == Parent{}>{}; + } +} + } // namespace mp_units::detail diff --git a/src/core/include/mp-units/compat_macros.h b/src/core/include/mp-units/compat_macros.h index 81931dc2..99fafa40 100644 --- a/src/core/include/mp-units/compat_macros.h +++ b/src/core/include/mp-units/compat_macros.h @@ -28,14 +28,14 @@ #ifdef MP_UNITS_API_NO_CRTP -#define QUANTITY_SPEC(name, ...) \ - inline constexpr struct name : ::mp_units::quantity_spec<__VA_ARGS__> { \ +#define QUANTITY_SPEC(name, ...) \ + inline constexpr struct name final : ::mp_units::quantity_spec<__VA_ARGS__> { \ } name #else -#define QUANTITY_SPEC(name, ...) \ - inline constexpr struct name : ::mp_units::quantity_spec { \ +#define QUANTITY_SPEC(name, ...) \ + inline constexpr struct name final : ::mp_units::quantity_spec { \ } name #endif diff --git a/src/core/include/mp-units/framework/quantity_spec.h b/src/core/include/mp-units/framework/quantity_spec.h index 2613d447..958778d4 100644 --- a/src/core/include/mp-units/framework/quantity_spec.h +++ b/src/core/include/mp-units/framework/quantity_spec.h @@ -23,9 +23,9 @@ #pragma once // IWYU pragma: private, include -#include #include #include +#include #include #include #include @@ -203,9 +203,9 @@ MP_UNITS_EXPORT_END * inline constexpr struct dim_mass final : base_dimension<"M"> {} dim_mass; * inline constexpr struct dim_time final : base_dimension<"T"> {} dim_time; * - * inline constexpr struct length : quantity_spec {} length; - * inline constexpr struct mass : quantity_spec {} mass; - * inline constexpr struct time : quantity_spec {} time; + * inline constexpr struct length final : quantity_spec {} length; + * inline constexpr struct mass final : quantity_spec {} mass; + * inline constexpr struct time final : quantity_spec {} time; * @endcode * * @note A common convention in this library is to assign the same name for a type and an object of this type. @@ -225,6 +225,7 @@ template) struct quantity_spec : detail::quantity_spec_interface { #endif + using _base_type_ = quantity_spec; static constexpr detail::BaseDimension auto dimension = Dim; static constexpr quantity_character character = detail::quantity_character_init(quantity_character::scalar); }; @@ -243,12 +244,12 @@ struct quantity_spec : detail::quantity_spec_interface * For example: * * @code{.cpp} - * inline constexpr struct area : quantity_spec(length)> {} area; - * inline constexpr struct volume : quantity_spec(length)> {} volume; - * inline constexpr struct velocity : quantity_spec {} velocity; - * inline constexpr struct speed : quantity_spec {} speed; - * inline constexpr struct force : quantity_spec {} force; - * inline constexpr struct power : quantity_spec {} power; + * inline constexpr struct area final : quantity_spec(length)> {} area; + * inline constexpr struct volume final : quantity_spec(length)> {} volume; + * inline constexpr struct velocity final : quantity_spec {} velocity; + * inline constexpr struct speed final : quantity_spec {} speed; + * inline constexpr struct force final : quantity_spec {} force; + * inline constexpr struct power final : quantity_spec {} power; * @endcode * * @note A common convention in this library is to assign the same name for a type and an object of this type. @@ -268,11 +269,24 @@ template) struct quantity_spec : detail::quantity_spec_interface { #endif + using _base_type_ = quantity_spec; static constexpr auto _equation_ = Eq; static constexpr Dimension auto dimension = Eq.dimension; static constexpr quantity_character character = detail::quantity_character_init(Eq.character); }; +namespace detail { + +template +struct propagate_equation {}; + +template +struct propagate_equation { + static constexpr auto _equation_ = Q._equation_; +}; + +} // namespace detail + /** * @brief Specialization defining a leaf quantity in the hierarchy * @@ -285,10 +299,10 @@ struct quantity_spec : detail::quantity_spec_interface * For example: * * @code{.cpp} - * inline constexpr struct width : quantity_spec {} width; - * inline constexpr struct height : quantity_spec {} height; - * inline constexpr struct diameter : quantity_spec {} diameter; - * inline constexpr struct position_vector : quantity_spec {} position_vector; + * inline constexpr struct width final : quantity_spec {} width; + * inline constexpr struct height final : quantity_spec {} height; + * inline constexpr struct diameter final : quantity_spec {} diameter; + * inline constexpr struct position_vector final : quantity_spec {} position_vector; * @endcode * * @note A common convention in this library is to assign the same name for a type and an object of this type. @@ -303,13 +317,15 @@ struct quantity_spec : detail::quantity_spec_interface #ifdef MP_UNITS_API_NO_CRTP template auto... Args> requires(... && !QuantitySpec) -struct quantity_spec : decltype(QS) { +struct quantity_spec : detail::propagate_equation, detail::quantity_spec_interface { #else template auto... Args> requires(... && !QuantitySpec) -struct quantity_spec : decltype(QS) { +struct quantity_spec : detail::propagate_equation, detail::quantity_spec_interface { #endif + using _base_type_ = quantity_spec; static constexpr auto _parent_ = QS; + static constexpr Dimension auto dimension = _parent_.dimension; static constexpr quantity_character character = detail::quantity_character_init(QS.character); #ifndef MP_UNITS_API_NO_CRTP @@ -330,6 +346,7 @@ struct quantity_spec : decltype(QS) { #endif }; +// clang-format off /** * @brief Specialization defining a leaf derived quantity in the hierarchy and refining paren't equation * @@ -343,10 +360,10 @@ struct quantity_spec : decltype(QS) { * For example: * * @code{.cpp} - * inline constexpr struct angular_measure : quantity_spec {} - * angular_measure; inline constexpr struct velocity : quantity_spec {} velocity; - * inline constexpr struct weight : quantity_spec {} weight; - * inline constexpr struct kinetic_energy : quantity_spec(speed)> {} kinetic_energy; + * inline constexpr struct angular_measure final : quantity_spec {} angular_measure; + * inline constexpr struct velocity final : quantity_spec {} velocity; + * inline constexpr struct weight final : quantity_spec {} weight; + * inline constexpr struct kinetic_energy final : quantity_spec(speed)> {} kinetic_energy; * @endcode * * @note A common convention in this library is to assign the same name for a type and an object of this type. @@ -358,25 +375,50 @@ struct quantity_spec : decltype(QS) { * @tparam Args optionally a value of a `quantity_character` in case the base quantity should not be scalar * or `is_kind` in case the quantity starts a new hierarchy tree of a kind */ +// clang-format on #ifdef MP_UNITS_API_NO_CRTP template auto... Args> requires(!requires { QS._equation_; } || (requires { QS._equation_; } && (explicitly_convertible(Eq, QS._equation_)))) && (... && !QuantitySpec) -struct quantity_spec : quantity_spec { +struct quantity_spec : detail::quantity_spec_interface { #else template auto... Args> requires(!requires { QS._equation_; } || (requires { QS._equation_; } && (explicitly_convertible(Eq, QS._equation_)))) && (... && !QuantitySpec) -struct quantity_spec : quantity_spec { +struct quantity_spec : detail::quantity_spec_interface { #endif + using _base_type_ = quantity_spec; + static constexpr auto _parent_ = QS; static constexpr auto _equation_ = Eq; + static constexpr Dimension auto dimension = _parent_.dimension; static constexpr quantity_character character = detail::quantity_character_init(Eq.character); }; +namespace detail { + +template +struct derived_quantity_spec_impl : +#ifdef MP_UNITS_API_NO_CRTP + detail::quantity_spec_interface, +#else + detail::quantity_spec_interface>, +#endif + detail::expr_fractions { + using _base_type_ = derived_quantity_spec_impl; + using _base_ = detail::expr_fractions; + + static constexpr Dimension auto dimension = + detail::expr_map(_base_{}); + static constexpr quantity_character character = + detail::derived_quantity_character(typename _base_::_num_{}, typename _base_::_den_{}); +}; + +} // namespace detail /** * @brief A specification of a derived quantity @@ -422,21 +464,7 @@ struct quantity_spec : quantity_spec { * instantiate this type automatically based on the dimensional arithmetic equation provided by the user. */ template -struct derived_quantity_spec : -#ifdef MP_UNITS_API_NO_CRTP - detail::quantity_spec_interface, -#else - detail::quantity_spec_interface>, -#endif - detail::expr_fractions { - using _base_ = detail::expr_fractions; - - static constexpr Dimension auto dimension = - detail::expr_map(_base_{}); - static constexpr quantity_character character = - detail::derived_quantity_character(typename _base_::_num_{}, typename _base_::_den_{}); -}; +struct derived_quantity_spec final : detail::derived_quantity_spec_impl {}; /** * @brief Quantity of dimension one @@ -446,6 +474,13 @@ struct derived_quantity_spec : */ MP_UNITS_EXPORT QUANTITY_SPEC(dimensionless, derived_quantity_spec<>{}); +namespace detail { + +template<> +struct is_dimensionless : std::true_type {}; + +} // namespace detail + /** * @brief Quantity kind specifier * @@ -464,7 +499,8 @@ template #ifdef MP_UNITS_API_NO_CRTP template requires detail::QuantitySpecWithNoSpecifiers && (detail::get_kind_tree_root(Q{}) == Q{}) -struct kind_of_ : Q { +struct kind_of_ final : Q::_base_type_ { + using _base_type_ = kind_of_; static constexpr auto _quantity_spec_ = Q{}; }; #else @@ -476,7 +512,8 @@ template template requires(detail::get_kind_tree_root(Q{}) == Q{}) #endif -struct kind_of_ : quantity_spec, Q{}> { +struct kind_of_ final : quantity_spec, Q{}>::_base_type_ { + using _base_type_ = kind_of_; static constexpr auto _quantity_spec_ = Q{}; }; #endif @@ -487,9 +524,6 @@ inline constexpr kind_of_ kind_of; namespace detail { -template<> -struct is_dimensionless : std::true_type {}; - template [[nodiscard]] consteval QuantitySpec auto clone_kind_of(Q q) { @@ -1353,8 +1387,8 @@ template using enum specs_convertible_result; if constexpr (have_common_base(From{}, To{})) { - if constexpr (std::derived_from) return yes; - if constexpr (std::derived_from) return explicit_conversion; + if constexpr (decltype(is_child_of(From{}, To{}))::value) return yes; + if constexpr (decltype(is_child_of(To{}, From{}))::value) return explicit_conversion; if constexpr (get_kind(From{}) == get_kind(To{})) return cast; return no; } else if constexpr (get_kind(From{}) != get_kind(To{})) diff --git a/src/core/include/mp-units/framework/quantity_spec_concepts.h b/src/core/include/mp-units/framework/quantity_spec_concepts.h index 3d511b5a..647ac86d 100644 --- a/src/core/include/mp-units/framework/quantity_spec_concepts.h +++ b/src/core/include/mp-units/framework/quantity_spec_concepts.h @@ -64,25 +64,14 @@ template inline constexpr bool is_derived_from_specialization_of_quantity_spec = requires(T* t) { to_base_specialization_of_quantity_spec(t); }; -template -inline constexpr bool is_specialization_of_quantity_spec = false; - -#ifdef MP_UNITS_API_NO_CRTP -template -inline constexpr bool is_specialization_of_quantity_spec> = true; -#else -template -inline constexpr bool is_specialization_of_quantity_spec> = true; -#endif - /** * @brief Concept matching all named quantity specification types * * Satisfied by all types that derive from `quantity_spec`. */ template -concept NamedQuantitySpec = is_derived_from_specialization_of_quantity_spec && - (!is_specialization_of_quantity_spec)&&(!QuantityKindSpec); +concept NamedQuantitySpec = + is_derived_from_specialization_of_quantity_spec && std::is_final_v && (!QuantityKindSpec); template struct is_dimensionless : std::false_type {}; @@ -136,12 +125,15 @@ MP_UNITS_EXPORT template namespace detail { +template +[[nodiscard]] consteval auto is_child_of(Child ch, Parent); + template concept NestedQuantityKindSpecOf = QuantitySpec && QuantitySpec && get_kind(From) != get_kind(To) && - std::derived_from>; + decltype(is_child_of(To, get_kind(From)._quantity_spec_))::value; -} +} // namespace detail MP_UNITS_EXPORT template concept QuantitySpecOf = diff --git a/test/static/concepts_test.cpp b/test/static/concepts_test.cpp index df19711f..78d785b9 100644 --- a/test/static/concepts_test.cpp +++ b/test/static/concepts_test.cpp @@ -85,7 +85,7 @@ static_assert(!Dimension); // TODO add tests // QuantitySpec -struct speed : decltype(isq::length / isq::time) {}; // this is not recommended +inline constexpr auto speed = isq::length / isq::time; static_assert(QuantitySpec); static_assert(QuantitySpec); @@ -94,7 +94,7 @@ static_assert(QuantitySpec)>); static_assert(QuantitySpec); static_assert(QuantitySpec(isq::length))>); static_assert(QuantitySpec); -static_assert(!QuantitySpec); +static_assert(QuantitySpec>); static_assert(!QuantitySpec); static_assert(!QuantitySpec); @@ -106,21 +106,21 @@ static_assert(!detail::NamedQuantitySpec); static_assert(!detail::NamedQuantitySpec(isq::length))>); static_assert(detail::NamedQuantitySpec); -static_assert(!detail::NamedQuantitySpec); +static_assert(!detail::NamedQuantitySpec>); static_assert(!detail::NamedQuantitySpec); static_assert(!detail::NamedQuantitySpec); -// IntermediateDerivedQuantitySpec -static_assert(!detail::IntermediateDerivedQuantitySpec); -static_assert(!detail::IntermediateDerivedQuantitySpec); -static_assert(!detail::IntermediateDerivedQuantitySpec)>); -static_assert(!detail::IntermediateDerivedQuantitySpec); -static_assert(detail::IntermediateDerivedQuantitySpec); -static_assert(detail::IntermediateDerivedQuantitySpec(isq::length))>); -static_assert(!detail::IntermediateDerivedQuantitySpec); -static_assert(!detail::IntermediateDerivedQuantitySpec); -static_assert(!detail::IntermediateDerivedQuantitySpec); -static_assert(!detail::IntermediateDerivedQuantitySpec); +// DerivedQuantitySpec +static_assert(!detail::DerivedQuantitySpec); +static_assert(!detail::DerivedQuantitySpec); +static_assert(!detail::DerivedQuantitySpec)>); +static_assert(!detail::DerivedQuantitySpec); +static_assert(detail::DerivedQuantitySpec); +static_assert(detail::DerivedQuantitySpec(isq::length))>); +static_assert(!detail::DerivedQuantitySpec); +static_assert(detail::DerivedQuantitySpec>); +static_assert(!detail::DerivedQuantitySpec); +static_assert(!detail::DerivedQuantitySpec); // QuantityKindSpec static_assert(!detail::QuantityKindSpec); @@ -130,7 +130,7 @@ static_assert(!detail::QuantityKindSpec); static_assert(!detail::QuantityKindSpec); static_assert(!detail::QuantityKindSpec(isq::length))>); static_assert(!detail::QuantityKindSpec); -static_assert(!detail::QuantityKindSpec); +static_assert(!detail::QuantityKindSpec>); static_assert(!detail::QuantityKindSpec); static_assert(!detail::QuantityKindSpec); diff --git a/test/static/quantity_spec_test.cpp b/test/static/quantity_spec_test.cpp index 7395b6a2..fcd10fc5 100644 --- a/test/static/quantity_spec_test.cpp +++ b/test/static/quantity_spec_test.cpp @@ -88,98 +88,98 @@ QUANTITY_SPEC_(kinetic_energy, mechanical_energy, mass* pow<2>(speed)); // concepts verification static_assert(QuantitySpec); static_assert(detail::NamedQuantitySpec); -static_assert(!detail::IntermediateDerivedQuantitySpec); +static_assert(!detail::DerivedQuantitySpec); static_assert(!detail::QuantityKindSpec); static_assert(QuantitySpec); static_assert(detail::NamedQuantitySpec); -static_assert(!detail::IntermediateDerivedQuantitySpec); +static_assert(!detail::DerivedQuantitySpec); static_assert(!detail::QuantityKindSpec); static_assert(QuantitySpec); static_assert(!detail::NamedQuantitySpec); -static_assert(detail::IntermediateDerivedQuantitySpec); +static_assert(detail::DerivedQuantitySpec); static_assert(!detail::QuantityKindSpec); static_assert(QuantitySpec); static_assert(detail::NamedQuantitySpec); -static_assert(!detail::IntermediateDerivedQuantitySpec); +static_assert(!detail::DerivedQuantitySpec); static_assert(!detail::QuantityKindSpec); static_assert(QuantitySpec>); static_assert(!detail::NamedQuantitySpec>); -static_assert(!detail::IntermediateDerivedQuantitySpec>); +static_assert(!detail::DerivedQuantitySpec>); static_assert(detail::QuantityKindSpec>); static_assert(QuantitySpec); static_assert(detail::NamedQuantitySpec); -static_assert(!detail::IntermediateDerivedQuantitySpec); +static_assert(!detail::DerivedQuantitySpec); static_assert(!detail::QuantityKindSpec); static_assert(QuantitySpec); static_assert(!detail::NamedQuantitySpec); -static_assert(detail::IntermediateDerivedQuantitySpec); +static_assert(detail::DerivedQuantitySpec); static_assert(!detail::QuantityKindSpec); static_assert(QuantitySpec>); static_assert(!detail::NamedQuantitySpec>); -static_assert(detail::IntermediateDerivedQuantitySpec>); +static_assert(detail::DerivedQuantitySpec>); static_assert(detail::QuantityKindSpec>); static_assert(QuantitySpec / kind_of