component: automatic traits deduction

This commit is contained in:
Michele Caini
2022-02-11 10:35:43 +01:00
parent ed34a3f5d4
commit ed11bda9fd
11 changed files with 140 additions and 85 deletions

View File

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

View File

@@ -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;
};
/**

View File

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

View File

@@ -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<auto>
struct comp { int x; };
template<>
struct entt::component_traits<stable_position>: basic_component_traits {
static constexpr auto in_place_delete = true;
};
struct timer final {
timer()
: start{std::chrono::system_clock::now()} {}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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