From ed11bda9fd45bc8e466bc9352e4f72ffb5a1118b Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Fri, 11 Feb 2022 10:35:43 +0100 Subject: [PATCH] component: automatic traits deduction --- docs/md/entity.md | 70 ++++++++++++++----------- src/entt/entity/component.hpp | 53 +++++++++++++++---- test/CMakeLists.txt | 1 + test/benchmark/benchmark.cpp | 10 ++-- test/entt/entity/component.cpp | 42 +++++++++++++++ test/entt/entity/helper.cpp | 6 +-- test/entt/entity/registry.cpp | 6 +-- test/entt/entity/runtime_view.cpp | 6 +-- test/entt/entity/sigh_storage_mixin.cpp | 6 +-- test/entt/entity/storage.cpp | 19 +++---- test/entt/entity/view.cpp | 6 +-- 11 files changed, 140 insertions(+), 85 deletions(-) create mode 100644 test/entt/entity/component.cpp diff --git a/docs/md/entity.md b/docs/md/entity.md index 728f546e3..85ac2fc91 100644 --- a/docs/md/entity.md +++ b/docs/md/entity.md @@ -27,6 +27,7 @@ * [Organizer](#organizer) * [Context variables](#context-variables) * [Aliased properties](#aliased-properties) + * [Component traits](#component-traits) * [Pointer stability](#pointer-stability) * [In-place delete](#in-place-delete) * [Hierarchies and the like](#hierarchies-and-the-like) @@ -942,6 +943,38 @@ const my_type &var = registry.ctx().at(); Aliased properties can be erased as it happens with any other variable. Similarly, they can also be associated with user-generated _names_ (or ids). +## Component traits + +In `EnTT`, almost everything is customizable. Components are no exception.
+In this case, the _standardized_ way to access all component properties is the +`component_traits` class. + +Various parts of the library access component properties through this class. It +makes it possible to use any type as a component, as long as its specialization +of `component_traits` implements all the required functionalities.
+The non-specialized version of this class contains the following members: + +* `in_place_delete`: `Type::in_place_delete` if present, false otherwise. +* `ignore_if_empty`: `Type::ignore_if_empty` if present, `ENTT_IGNORE_IF_EMPTY` + otherwise. +* `page_size`: `Type::ignore_if_empty` if present, `ENTT_PACKED_PAGE` otherwise. + +Where `Type` is any type of component. All properties can be customized by +specializing the above class and defining all its members, or by adding only +those of interest to a component definition: + +```cpp +struct transform { + static constexpr auto in_place_delete = true; + // ... other data members ... +}; +``` + +The `component_traits` class template will take care of correctly extracting the +properties from the supplied type to pass them to the rest of the library.
+In the case of a direct specialization, the class is also _sfinae-friendly_. It +supports single and multi type specializations as well as feature-based ones. + ## Pointer stability The ability to achieve pointer stability for one, several or all components is a @@ -962,33 +995,9 @@ In other words, pointer stability is not automatic but is enabled on request. ### In-place delete The library offers out of the box support for in-place deletion, thus offering -storage with completely stable pointers.
-This is achieved by specializing the `component_traits` class. The compile-time -definition common to all components is the following: - -```cpp -struct basic_component_traits { - static constexpr auto in_place_delete = false; - static constexpr auto ignore_if_empty = ENTT_IGNORE_IF_EMPTY; - static constexpr auto page_size = ENTT_PACKED_PAGE; -}; -``` - -Where `in_place_delete` instructs the library on the deletion policy for a given -type while `ignore_if_empty` selectively disables empty type optimization and -`page_size` dictates the storage behavior for non-empty types.
-The `component_traits` class template is _sfinae-friendly_, it supports single -and multi type specializations as well as feature-based ones: - -```cpp -template<> -struct entt::component_traits: basic_component_traits { - static constexpr auto in_place_delete = true; -}; -``` - -This will ensure in-place deletion for the `position` component without further -user intervention.
+storage with completely stable pointers. This is achieved by specializing the +`component_traits` class or by adding the required properties to the component +definition when needed.
Views and groups adapt accordingly when they detect a storage with a different deletion policy than the default. In particular: @@ -1028,14 +1037,11 @@ advantages: ```cpp struct transform { + static constexpr auto in_place_delete = true; + transform *parent; // ... other data members ... }; - -template<> -struct entt::component_traits: basic_component_traits { - static constexpr auto in_place_delete = true; -}; ``` Furthermore, it's quite common for a group of elements to be created close in diff --git a/src/entt/entity/component.hpp b/src/entt/entity/component.hpp index 665064b1e..9716be6dd 100644 --- a/src/entt/entity/component.hpp +++ b/src/entt/entity/component.hpp @@ -1,28 +1,61 @@ #ifndef ENTT_ENTITY_COMPONENT_HPP #define ENTT_ENTITY_COMPONENT_HPP +#include #include #include "../config/config.h" namespace entt { -/*! @brief Commonly used default traits for all types. */ -struct basic_component_traits { - /*! @brief Pointer stability, default is `false`. */ - static constexpr auto in_place_delete = false; - /*! @brief Empty type optimization, default is `ENTT_IGNORE_IF_EMPTY`. */ - static constexpr auto ignore_if_empty = ENTT_IGNORE_IF_EMPTY; - /*! @brief Page size, default is `ENTT_PACKED_PAGE`. */ - static constexpr auto page_size = ENTT_PACKED_PAGE; -}; +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct in_place_delete: std::false_type {}; + +template +struct in_place_delete> + : std::true_type {}; + +template +struct ignore_if_empty: std::bool_constant {}; + +template +struct ignore_if_empty> + : std::true_type {}; + +template +struct page_size: std::integral_constant {}; + +template +struct page_size>> + : std::integral_constant {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ /** * @brief Common way to access various properties of components. * @tparam Type Type of component. */ template -struct component_traits: basic_component_traits { +struct component_traits { static_assert(std::is_same_v, Type>, "Unsupported type"); + + /*! @brief Pointer stability, default is `false`. */ + static constexpr bool in_place_delete = internal::in_place_delete::value; + /*! @brief Empty type optimization, default is `ENTT_IGNORE_IF_EMPTY`. */ + static constexpr bool ignore_if_empty = internal::ignore_if_empty::value; + /*! @brief Page size, default is `ENTT_PACKED_PAGE`. */ + static constexpr std::size_t page_size = internal::page_size::value; }; /** diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 401bb3729..d242fc2fb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -184,6 +184,7 @@ SETUP_BASIC_TEST(utility entt/core/utility.cpp) # Test entity +SETUP_BASIC_TEST(component entt/entity/component.cpp) SETUP_BASIC_TEST(entity entt/entity/entity.cpp) SETUP_BASIC_TEST(group entt/entity/group.cpp) SETUP_BASIC_TEST(handle entt/entity/handle.cpp) diff --git a/test/benchmark/benchmark.cpp b/test/benchmark/benchmark.cpp index cb9a93a53..d56d11521 100644 --- a/test/benchmark/benchmark.cpp +++ b/test/benchmark/benchmark.cpp @@ -13,16 +13,14 @@ struct position { }; struct velocity: position {}; -struct stable_position: position {}; + +struct stable_position: position { + static constexpr auto in_place_delete = true; +}; template struct comp { int x; }; -template<> -struct entt::component_traits: basic_component_traits { - static constexpr auto in_place_delete = true; -}; - struct timer final { timer() : start{std::chrono::system_clock::now()} {} diff --git a/test/entt/entity/component.cpp b/test/entt/entity/component.cpp new file mode 100644 index 000000000..b4ad0d9bd --- /dev/null +++ b/test/entt/entity/component.cpp @@ -0,0 +1,42 @@ +#include +#include + +struct self_contained { + static constexpr auto in_place_delete = true; + static constexpr auto page_size = 4u; +}; + +struct traits_based {}; + +template<> +struct entt::component_traits { + static constexpr auto in_place_delete = false; + static constexpr auto ignore_if_empty = false; + static constexpr auto page_size = 8u; +}; + +struct default_params {}; + +TEST(Component, DefaultParams) { + using traits = entt::component_traits; + + static_assert(!traits::in_place_delete); + static_assert(traits::ignore_if_empty); + static_assert(traits::page_size == ENTT_PACKED_PAGE); +} + +TEST(Component, SelfContained) { + using traits = entt::component_traits; + + static_assert(traits::in_place_delete); + static_assert(traits::ignore_if_empty); + static_assert(traits::page_size == 4u); +} + +TEST(Component, TraitsBased) { + using traits = entt::component_traits; + + static_assert(!traits::in_place_delete); + static_assert(!traits::ignore_if_empty); + static_assert(traits::page_size == 8u); +} diff --git a/test/entt/entity/helper.cpp b/test/entt/entity/helper.cpp index c15a177ee..e24650faa 100644 --- a/test/entt/entity/helper.cpp +++ b/test/entt/entity/helper.cpp @@ -13,12 +13,8 @@ struct clazz { }; struct stable_type { - int value; -}; - -template<> -struct entt::component_traits: basic_component_traits { static constexpr auto in_place_delete = true; + int value; }; TEST(Helper, AsView) { diff --git a/test/entt/entity/registry.cpp b/test/entt/entity/registry.cpp index 02e3d3f27..b363601fd 100644 --- a/test/entt/entity/registry.cpp +++ b/test/entt/entity/registry.cpp @@ -16,12 +16,8 @@ struct empty_type {}; struct stable_type { - int value; -}; - -template<> -struct entt::component_traits: basic_component_traits { static constexpr auto in_place_delete = true; + int value; }; struct non_default_constructible { diff --git a/test/entt/entity/runtime_view.cpp b/test/entt/entity/runtime_view.cpp index e5f8a9b12..960e4c6ce 100644 --- a/test/entt/entity/runtime_view.cpp +++ b/test/entt/entity/runtime_view.cpp @@ -7,12 +7,8 @@ #include struct stable_type { - int value; -}; - -template<> -struct entt::component_traits: basic_component_traits { static constexpr auto in_place_delete = true; + int value; }; TEST(RuntimeView, Functionalities) { diff --git a/test/entt/entity/sigh_storage_mixin.cpp b/test/entt/entity/sigh_storage_mixin.cpp index c5c544b71..7e44db890 100644 --- a/test/entt/entity/sigh_storage_mixin.cpp +++ b/test/entt/entity/sigh_storage_mixin.cpp @@ -7,6 +7,7 @@ struct empty_type {}; struct stable_type { + static constexpr auto in_place_delete = true; int value{}; }; @@ -19,11 +20,6 @@ struct non_default_constructible { int value{}; }; -template<> -struct entt::component_traits: basic_component_traits { - static constexpr auto in_place_delete = true; -}; - struct counter { int value{}; }; diff --git a/test/entt/entity/storage.cpp b/test/entt/entity/storage.cpp index 4f33db014..8ebadd4d1 100644 --- a/test/entt/entity/storage.cpp +++ b/test/entt/entity/storage.cpp @@ -12,13 +12,16 @@ #include "../common/throwing_allocator.hpp" #include "../common/throwing_type.hpp" -struct empty_stable_type {}; +struct empty_stable_type { + static constexpr auto in_place_delete = true; +}; struct boxed_int { int value; }; struct stable_type { + static constexpr auto in_place_delete = true; int value; }; @@ -72,18 +75,10 @@ struct crete_from_constructor { }; template<> -struct entt::component_traits: basic_component_traits { - static constexpr auto in_place_delete = true; -}; - -template<> -struct entt::component_traits: basic_component_traits { - static constexpr auto in_place_delete = true; -}; - -template<> -struct entt::component_traits>: basic_component_traits { +struct entt::component_traits> { static constexpr auto in_place_delete = true; + static constexpr auto ignore_if_empty = ENTT_IGNORE_IF_EMPTY; + static constexpr auto page_size = ENTT_PACKED_PAGE; }; bool operator==(const boxed_int &lhs, const boxed_int &rhs) { diff --git a/test/entt/entity/view.cpp b/test/entt/entity/view.cpp index a0a091b7e..f26c63358 100644 --- a/test/entt/entity/view.cpp +++ b/test/entt/entity/view.cpp @@ -11,12 +11,8 @@ struct empty_type {}; struct stable_type { - int value; -}; - -template<> -struct entt::component_traits: basic_component_traits { static constexpr auto in_place_delete = true; + int value; }; TEST(SingleComponentView, Functionalities) {