Просмотр исходного кода

component: automatic traits deduction

Michele Caini 4 лет назад
Родитель
Сommit
ed11bda9fd

+ 38 - 32
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<const my_type>();
 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.<br/>
+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.<br/>
+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.<br/>
+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.<br/>
-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.<br/>
-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<position>: basic_component_traits {
-    static constexpr auto in_place_delete = true;
-};
-```
-
-This will ensure in-place deletion for the `position` component without further
-user intervention.<br/>
+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.<br/>
 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<transform>: 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

+ 43 - 10
src/entt/entity/component.hpp

@@ -1,28 +1,61 @@
 #ifndef ENTT_ENTITY_COMPONENT_HPP
 #define ENTT_ENTITY_COMPONENT_HPP
 
+#include <cstddef>
 #include <type_traits>
 #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<typename, typename = void>
+struct in_place_delete: std::false_type {};
+
+template<typename Type>
+struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>>
+    : std::true_type {};
+
+template<typename, typename = void>
+struct ignore_if_empty: std::bool_constant<ENTT_IGNORE_IF_EMPTY> {};
+
+template<typename Type>
+struct ignore_if_empty<Type, std::enable_if_t<Type::ignore_if_empty>>
+    : std::true_type {};
+
+template<typename, typename = void>
+struct page_size: std::integral_constant<std::size_t, ENTT_PACKED_PAGE> {};
+
+template<typename Type>
+struct page_size<Type, std::enable_if_t<std::is_convertible_v<decltype(Type::page_size), std::size_t>>>
+    : std::integral_constant<std::size_t, Type::page_size> {};
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
 
 /**
  * @brief Common way to access various properties of components.
  * @tparam Type Type of component.
  */
 template<typename Type, typename = void>
-struct component_traits: basic_component_traits {
+struct component_traits {
     static_assert(std::is_same_v<std::decay_t<Type>, Type>, "Unsupported type");
+
+    /*! @brief Pointer stability, default is `false`. */
+    static constexpr bool in_place_delete = internal::in_place_delete<Type>::value;
+    /*! @brief Empty type optimization, default is `ENTT_IGNORE_IF_EMPTY`. */
+    static constexpr bool ignore_if_empty = internal::ignore_if_empty<Type>::value;
+    /*! @brief Page size, default is `ENTT_PACKED_PAGE`. */
+    static constexpr std::size_t page_size = internal::page_size<Type>::value;
 };
 
 /**

+ 1 - 0
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)

+ 4 - 6
test/benchmark/benchmark.cpp

@@ -13,16 +13,14 @@ struct position {
 };
 
 struct velocity: position {};
-struct stable_position: position {};
 
-template<auto>
-struct comp { int x; };
-
-template<>
-struct entt::component_traits<stable_position>: basic_component_traits {
+struct stable_position: position {
     static constexpr auto in_place_delete = true;
 };
 
+template<auto>
+struct comp { int x; };
+
 struct timer final {
     timer()
         : start{std::chrono::system_clock::now()} {}

+ 42 - 0
test/entt/entity/component.cpp

@@ -0,0 +1,42 @@
+#include <gtest/gtest.h>
+#include <entt/entity/component.hpp>
+
+struct self_contained {
+    static constexpr auto in_place_delete = true;
+    static constexpr auto page_size = 4u;
+};
+
+struct traits_based {};
+
+template<>
+struct entt::component_traits<traits_based> {
+    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<default_params>;
+
+    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<self_contained>;
+
+    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<traits_based>;
+
+    static_assert(!traits::in_place_delete);
+    static_assert(!traits::ignore_if_empty);
+    static_assert(traits::page_size == 8u);
+}

+ 1 - 5
test/entt/entity/helper.cpp

@@ -13,12 +13,8 @@ struct clazz {
 };
 
 struct stable_type {
-    int value;
-};
-
-template<>
-struct entt::component_traits<stable_type>: basic_component_traits {
     static constexpr auto in_place_delete = true;
+    int value;
 };
 
 TEST(Helper, AsView) {

+ 1 - 5
test/entt/entity/registry.cpp

@@ -16,12 +16,8 @@
 struct empty_type {};
 
 struct stable_type {
-    int value;
-};
-
-template<>
-struct entt::component_traits<stable_type>: basic_component_traits {
     static constexpr auto in_place_delete = true;
+    int value;
 };
 
 struct non_default_constructible {

+ 1 - 5
test/entt/entity/runtime_view.cpp

@@ -7,12 +7,8 @@
 #include <entt/entity/runtime_view.hpp>
 
 struct stable_type {
-    int value;
-};
-
-template<>
-struct entt::component_traits<stable_type>: basic_component_traits {
     static constexpr auto in_place_delete = true;
+    int value;
 };
 
 TEST(RuntimeView, Functionalities) {

+ 1 - 5
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<stable_type>: basic_component_traits {
-    static constexpr auto in_place_delete = true;
-};
-
 struct counter {
     int value{};
 };

+ 7 - 12
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<stable_type>: basic_component_traits {
-    static constexpr auto in_place_delete = true;
-};
-
-template<>
-struct entt::component_traits<empty_stable_type>: basic_component_traits {
-    static constexpr auto in_place_delete = true;
-};
-
-template<>
-struct entt::component_traits<std::unordered_set<char>>: basic_component_traits {
+struct entt::component_traits<std::unordered_set<char>> {
     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) {

+ 1 - 5
test/entt/entity/view.cpp

@@ -11,12 +11,8 @@
 struct empty_type {};
 
 struct stable_type {
-    int value;
-};
-
-template<>
-struct entt::component_traits<stable_type>: basic_component_traits {
     static constexpr auto in_place_delete = true;
+    int value;
 };
 
 TEST(SingleComponentView, Functionalities) {