Skip to content
Flecs v4.1
lifecycle_traits.hpp
Go to the documentation of this file.
1/**
2 * @file addons/cpp/lifecycle_traits.hpp
3 * @brief Utilities for discovering and registering component lifecycle hooks.
4 */
5
6#pragma once
7
8namespace flecs
9{
10
11template <typename T>
13 static constexpr bool value = std::is_default_constructible<actual_type_t<T>>::value;
14};
15
16namespace _ {
17
18template <typename T, bool Destroy>
19ecs_xtor_t xtor(ecs_flags32_t& flags) {
20 constexpr bool trivial = Destroy ? is_trivially_destructible_v<T> : is_trivially_constructible_v<T>;
21 constexpr bool legal = Destroy ? is_destructible_v<T> : is_default_constructible_v<T>;
22 if constexpr (trivial) {
23 return nullptr;
24 } else if constexpr (!legal) {
25 flecs_static_assert(!Destroy || always_false<T>::value, "component type must be destructible");
26 flags |= Destroy ? ECS_TYPE_HOOK_DTOR_ILLEGAL : ECS_TYPE_HOOK_CTOR_ILLEGAL;
27 return nullptr;
28 } else {
29 return [](void *ptr, int32_t count, const ecs_type_info_t *info) {
30 (void)info;
31 ecs_assert(info->size == ECS_SIZEOF(T), ECS_INTERNAL_ERROR, nullptr);
32 T *arr = static_cast<T*>(ptr);
33 for (int32_t i = 0; i < count; i ++) {
34 if constexpr (Destroy) {
35 arr[i].~T();
36 } else {
37 FLECS_PLACEMENT_NEW(&arr[i], T);
38 }
39 }
40 };
41 }
42}
43
44template <typename T, bool Move, bool Construct, bool Destroy = false>
45conditional_t<Move, ecs_move_t, ecs_copy_t> transfer(ecs_flags32_t& flags) {
46 constexpr bool trivial = (Construct
47 ? (Move ? is_trivially_move_constructible_v<T> : is_trivially_copy_constructible_v<T>)
48 : (Move ? is_trivially_move_assignable_v<T> : is_trivially_copyable_v<T>)) &&
49 (!Destroy || is_trivially_destructible_v<T>);
50 constexpr bool legal = (Construct
51 ? (Move ? is_move_constructible_v<T> : is_copy_constructible_v<T>)
52 : (Move ? is_move_assignable_v<T> : is_copy_assignable_v<T>)) &&
53 (!Destroy || is_destructible_v<T>);
54 constexpr auto illegal = Move
55 ? (Construct
56 ? (Destroy ? ECS_TYPE_HOOK_CTOR_MOVE_DTOR_ILLEGAL : ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL)
57 : (Destroy ? ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL : ECS_TYPE_HOOK_MOVE_ILLEGAL))
58 : (Construct ? ECS_TYPE_HOOK_COPY_CTOR_ILLEGAL : ECS_TYPE_HOOK_COPY_ILLEGAL);
59 if constexpr (trivial) {
60 return nullptr;
61 } else if constexpr (!legal) {
62 flags |= illegal;
63 return nullptr;
64 } else {
65 return [](void *dst_ptr, conditional_t<Move, void*, const void*> src_ptr,
66 int32_t count, const ecs_type_info_t *info)
67 {
68 (void)info;
69 ecs_assert(info->size == ECS_SIZEOF(T), ECS_INTERNAL_ERROR, nullptr);
70 T *dst = static_cast<T*>(dst_ptr);
71 auto src = static_cast<conditional_t<Move, T*, const T*>>(src_ptr);
72 for (int32_t i = 0; i < count; i ++) {
73 using Value = conditional_t<Move, T&&, const T&>;
74 if constexpr (Destroy && !Construct && is_trivially_move_assignable_v<T>) {
75 dst[i].~T();
76 }
77 if constexpr (Construct) {
78 FLECS_PLACEMENT_NEW(&dst[i], T(static_cast<Value>(src[i])));
79 } else {
80 dst[i] = static_cast<Value>(src[i]);
81 }
82 if constexpr (Destroy && (Construct || !is_trivially_move_assignable_v<T>)) {
83 src[i].~T();
84 }
85 }
86 };
87 }
88}
89
90// Traits to check for operator<, operator>, and operator==.
91using std::void_t;
92
93// These traits cause a "float comparison warning" in some compilers
94// when `T` is float or double.
95// Disable this warning with the following pragmas.
96#if defined(__clang__)
97 #pragma clang diagnostic push
98 #pragma clang diagnostic ignored "-Wfloat-equal"
99#elif defined(__GNUC__) && !defined(__clang__)
100 #pragma GCC diagnostic push
101 #pragma GCC diagnostic ignored "-Wfloat-equal"
102#endif
103
104// Trait to check for operator<.
105template <typename T, typename = void>
106struct has_operator_less : std::false_type {};
107
108// Only enable if T has an operator< that takes T as the right-hand side (no implicit conversion).
109template <typename T>
110struct has_operator_less<T, void_t<decltype(std::declval<const T&>() < std::declval<const T&>())>> :
111 std::is_same<decltype(std::declval<const T&>() < std::declval<const T&>()), bool> {};
112
113// Trait to check for operator>.
114template <typename T, typename = void>
115struct has_operator_greater : std::false_type {};
116
117// Only enable if T has an operator> that takes T as the right-hand side (no implicit conversion).
118template <typename T>
119struct has_operator_greater<T, void_t<decltype(std::declval<const T&>() > std::declval<const T&>())>> :
120 std::is_same<decltype(std::declval<const T&>() > std::declval<const T&>()), bool> {};
121
122// Trait to check for operator==.
123template <typename T, typename = void>
124struct has_operator_equal : std::false_type {};
125
126// Only enable if T has an operator== that takes T as the right-hand side (no implicit conversion).
127template <typename T>
128struct has_operator_equal<T, void_t<decltype(std::declval<const T&>() == std::declval<const T&>())>> :
129 std::is_same<decltype(std::declval<const T&>() == std::declval<const T&>()), bool> {};
130
131// Selects the best comparison strategy based on available operators.
132template <typename T>
133int compare_impl(const void *a, const void *b, const ecs_type_info_t *) {
134 const T& lhs = *static_cast<const T*>(a);
135 const T& rhs = *static_cast<const T*>(b);
136
138 // 2. Compare function if `<` and `==` are defined (preferred)
139 if (lhs == rhs) return 0;
140 if (lhs < rhs) return -1;
141 return 1;
142 } else if constexpr (has_operator_greater<T>::value && has_operator_equal<T>::value) {
143 // 3. Compare function if `>` and `==` are defined, deducing `<`
144 if (lhs == rhs) return 0;
145 if (lhs > rhs) return 1;
146 return -1;
147 } else if constexpr (has_operator_less<T>::value && has_operator_greater<T>::value) {
148 // 1. Compare function if `<`, `>` are defined
149 if (lhs < rhs) return -1;
150 if (lhs > rhs) return 1;
151 return 0;
152 } else if constexpr (has_operator_less<T>::value) {
153 // 4. Compare function if only `<` is defined
154 if (lhs < rhs) return -1;
155 if (rhs < lhs) return 1;
156 return 0;
157 } else if constexpr (has_operator_greater<T>::value) {
158 // 5. Compare function if only `>` is defined
159 if (lhs > rhs) return 1;
160 if (rhs > lhs) return -1;
161 return 0;
162 } else {
163 // This branch should never be instantiated due to the compare() check.
164 return 0;
165 }
166}
167
168// To have a generated compare hook, at least
169// operator> or operator< must be defined.
170template <typename T>
171ecs_cmp_t compare() {
173 return compare_impl<T>;
174 } else {
175 return nullptr;
176 }
177}
178
179// Equals implementation.
180template <typename T>
181bool equals_impl(const void *a, const void *b, const ecs_type_info_t *) {
182 const T& lhs = *static_cast<const T*>(a);
183 const T& rhs = *static_cast<const T*>(b);
184 return lhs == rhs;
185}
186
187template <typename T>
188ecs_equals_t equals() {
189 if constexpr (has_operator_equal<T>::value) {
190 return equals_impl<T>;
191 } else {
192 return nullptr;
193 }
194}
195
196// Re-enable the float comparison warning.
197#if defined(__clang__)
198 #pragma clang diagnostic pop
199#elif defined(__GNUC__) && !defined(__clang__)
200 #pragma GCC diagnostic pop
201#endif
202
203} // namespace _
204} // namespace flecs
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:473
#define ECS_INTERNAL_ERROR
Internal error code.
Definition log.h:681
struct ecs_type_info_t ecs_type_info_t
Type information.
Definition flecs.h:501
void(*) ecs_xtor_t(void *ptr, int32_t count, const ecs_type_info_t *type_info)
Constructor/destructor callback.
Definition flecs.h:673
bool(*) ecs_equals_t(const void *a_ptr, const void *b_ptr, const ecs_type_info_t *type_info)
Equals operator hook.
Definition flecs.h:699
int(*) ecs_cmp_t(const void *a_ptr, const void *b_ptr, const ecs_type_info_t *type_info)
Compare hook to compare component instances.
Definition flecs.h:693
Int to enum.
Definition component.hpp:18
Type that contains component information (passed to ctors/dtors/...).
Definition flecs.h:1052