Flecs: include/flecs/addons/cpp/lifecycle_traits.hpp Source File
Description: A fast entity component system (ECS) for C & C++

| Flecs v4.1 A fast entity component system (ECS) for C & C++

Loading...
Searching...
No Matches
lifecycle_traits.hpp
Go to the documentation of this file.
1
5
6#pragma once
7
8namespace flecs
9{
10
11namespace _
12{
13
14// T()
15// Can't coexist with T(flecs::entity) or T(flecs::world, flecs::entity).
16template <typename T>
17void ctor_impl(void *ptr, int32_t count, const ecs_type_info_t *info) {
18 (void)info; ecs_assert(info->size == ECS_SIZEOF(T),
19 ECS_INTERNAL_ERROR, NULL);
20 T *arr = static_cast<T*>(ptr);
21 for (int i = 0; i < count; i ++) {
22 FLECS_PLACEMENT_NEW(&arr[i], T);
23 }
24}
25
26// ~T()
27template <typename T>
28void dtor_impl(void *ptr, int32_t count, const ecs_type_info_t *info) {
29 (void)info; ecs_assert(info->size == ECS_SIZEOF(T),
30 ECS_INTERNAL_ERROR, NULL);
31 T *arr = static_cast<T*>(ptr);
32 for (int i = 0; i < count; i ++) {
33 arr[i].~T();
34 }
35}
36
37// T& operator=(const T&)
38template <typename T>
39void copy_impl(void *dst_ptr, const void *src_ptr, int32_t count,
40 const ecs_type_info_t *info)
41{
42 (void)info; ecs_assert(info->size == ECS_SIZEOF(T),
43 ECS_INTERNAL_ERROR, NULL);
44 T *dst_arr = static_cast<T*>(dst_ptr);
45 const T *src_arr = static_cast<const T*>(src_ptr);
46 for (int i = 0; i < count; i ++) {
47 dst_arr[i] = src_arr[i];
48 }
49}
50
51// T& operator=(T&&)
52template <typename T>
53void move_impl(void *dst_ptr, void *src_ptr, int32_t count,
54 const ecs_type_info_t *info)
55{
56 (void)info; ecs_assert(info->size == ECS_SIZEOF(T),
57 ECS_INTERNAL_ERROR, NULL);
58 T *dst_arr = static_cast<T*>(dst_ptr);
59 T *src_arr = static_cast<T*>(src_ptr);
60 for (int i = 0; i < count; i ++) {
61 dst_arr[i] = FLECS_MOV(src_arr[i]);
62 }
63}
64
65// T(const T&)
66template <typename T>
67void copy_ctor_impl(void *dst_ptr, const void *src_ptr, int32_t count,
68 const ecs_type_info_t *info)
69{
70 (void)info; ecs_assert(info->size == ECS_SIZEOF(T),
71 ECS_INTERNAL_ERROR, NULL);
72 T *dst_arr = static_cast<T*>(dst_ptr);
73 const T *src_arr = static_cast<const T*>(src_ptr);
74 for (int i = 0; i < count; i ++) {
75 FLECS_PLACEMENT_NEW(&dst_arr[i], T(src_arr[i]));
76 }
77}
78
79// T(T&&)
80template <typename T>
81void move_ctor_impl(void *dst_ptr, void *src_ptr, int32_t count,
82 const ecs_type_info_t *info)
83{
84 (void)info; ecs_assert(info->size == ECS_SIZEOF(T),
85 ECS_INTERNAL_ERROR, NULL);
86 T *dst_arr = static_cast<T*>(dst_ptr);
87 T *src_arr = static_cast<T*>(src_ptr);
88 for (int i = 0; i < count; i ++) {
89 FLECS_PLACEMENT_NEW(&dst_arr[i], T(FLECS_MOV(src_arr[i])));
90 }
91}
92
93// T(T&&), ~T()
94// Typically used when moving to a new table, and removing from the old table.
95template <typename T>
96void ctor_move_dtor_impl(void *dst_ptr, void *src_ptr, int32_t count,
97 const ecs_type_info_t *info)
98{
99 (void)info; ecs_assert(info->size == ECS_SIZEOF(T),
100 ECS_INTERNAL_ERROR, NULL);
101 T *dst_arr = static_cast<T*>(dst_ptr);
102 T *src_arr = static_cast<T*>(src_ptr);
103 for (int i = 0; i < count; i ++) {
104 FLECS_PLACEMENT_NEW(&dst_arr[i], T(FLECS_MOV(src_arr[i])));
105 src_arr[i].~T();
106 }
107}
108
109// Move assign + dtor (non-trivial move assignment).
110// Typically used when moving a component to a deleted component.
111template <typename T, if_not_t<
112 std::is_trivially_move_assignable<T>::value > = 0>
113void move_dtor_impl(void *dst_ptr, void *src_ptr, int32_t count,
114 const ecs_type_info_t *info)
115{
116 (void)info; ecs_assert(info->size == ECS_SIZEOF(T),
117 ECS_INTERNAL_ERROR, NULL);
118 T *dst_arr = static_cast<T*>(dst_ptr);
119 T *src_arr = static_cast<T*>(src_ptr);
120 for (int i = 0; i < count; i ++) {
121 // Move assignment should free dst and assign dst to src.
122 dst_arr[i] = FLECS_MOV(src_arr[i]);
123 // Destruct src. Move should have left the object in a state where it no
124 // longer holds resources, but it still needs to be destructed.
125 src_arr[i].~T();
126 }
127}
128
129// Move assign + dtor (trivial move assignment).
130// Typically used when moving a component to a deleted component.
131template <typename T, if_t<
132 std::is_trivially_move_assignable<T>::value > = 0>
133void move_dtor_impl(void *dst_ptr, void *src_ptr, int32_t count,
134 const ecs_type_info_t *info)
135{
136 (void)info; ecs_assert(info->size == ECS_SIZEOF(T),
137 ECS_INTERNAL_ERROR, NULL);
138 T *dst_arr = static_cast<T*>(dst_ptr);
139 T *src_arr = static_cast<T*>(src_ptr);
140 for (int i = 0; i < count; i ++) {
141 // Clean up resources of dst.
142 dst_arr[i].~T();
143 // Copy src to dst.
144 dst_arr[i] = FLECS_MOV(src_arr[i]);
145 // No need to destruct src. Since this is a trivial move, the code
146 // should be agnostic to the address of the component, which means we
147 // can pretend nothing got destructed.
148 }
149}
150
151} // namespace _
152
154template <typename T>
155 struct is_flecs_constructible {
156 static constexpr bool value =
157 std::is_default_constructible<actual_type_t<T>>::value;
158};
159
160namespace _
161{
162
163template <typename T>
164 ecs_xtor_t ctor(ecs_flags32_t &flags) {
165 if constexpr (is_trivially_constructible_v<T>) {
166 return nullptr;
167 } else if constexpr (!is_default_constructible_v<T>) {
168 flags |= ECS_TYPE_HOOK_CTOR_ILLEGAL;
169 return nullptr;
170 } else {
171 return ctor_impl<T>;
172 }
173}
174
175template <typename T>
176 ecs_xtor_t dtor(ecs_flags32_t &flags) {
177 if constexpr (is_trivially_destructible_v<T>) {
178 return nullptr;
179 } else if constexpr (!is_destructible_v<T>) {
180 flecs_static_assert(always_false<T>::value,
181 "component type must be destructible");
182 flags |= ECS_TYPE_HOOK_DTOR_ILLEGAL;
183 return nullptr;
184 } else {
185 return dtor_impl<T>;
186 }
187}
188
189template <typename T>
190 ecs_copy_t copy(ecs_flags32_t &flags) {
191 if constexpr (is_trivially_copyable_v<T>) {
192 return nullptr;
193 } else if constexpr (!is_copy_assignable_v<T>) {
194 flags |= ECS_TYPE_HOOK_COPY_ILLEGAL;
195 return nullptr;
196 } else {
197 return copy_impl<T>;
198 }
199}
200
201template <typename T>
202 ecs_move_t move(ecs_flags32_t &flags) {
203 if constexpr (is_trivially_move_assignable_v<T>) {
204 return nullptr;
205 } else if constexpr (!is_move_assignable_v<T>) {
206 flags |= ECS_TYPE_HOOK_MOVE_ILLEGAL;
207 return nullptr;
208 } else {
209 return move_impl<T>;
210 }
211}
212
213template <typename T>
214 ecs_copy_t copy_ctor(ecs_flags32_t &flags) {
215 if constexpr (is_trivially_copy_constructible_v<T>) {
216 return nullptr;
217 } else if constexpr (!is_copy_constructible_v<T>) {
218 flags |= ECS_TYPE_HOOK_COPY_CTOR_ILLEGAL;
219 return nullptr;
220 } else {
221 return copy_ctor_impl<T>;
222 }
223}
224
225template <typename T>
226 ecs_move_t move_ctor(ecs_flags32_t &flags) {
227 if constexpr (is_trivially_move_constructible_v<T>) {
228 return nullptr;
229 } else if constexpr (!is_move_constructible_v<T>) {
230 flags |= ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL;
231 return nullptr;
232 } else {
233 return move_ctor_impl<T>;
234 }
235}
236
237template <typename T>
238 ecs_move_t ctor_move_dtor(ecs_flags32_t &flags) {
239 if constexpr (is_trivially_move_constructible_v<T> && is_trivially_destructible_v<T>) {
240 return nullptr;
241 } else if constexpr (!is_move_constructible_v<T> || !is_destructible_v<T>) {
242 flags |= ECS_TYPE_HOOK_CTOR_MOVE_DTOR_ILLEGAL;
243 return nullptr;
244 } else {
245 return ctor_move_dtor_impl<T>;
246 }
247}
248
249template <typename T>
250 ecs_move_t move_dtor(ecs_flags32_t &flags) {
251 if constexpr (is_trivially_move_assignable_v<T> && is_trivially_destructible_v<T>) {
252 return nullptr;
253 } else if constexpr (!is_move_assignable_v<T> || !is_destructible_v<T>) {
254 flags |= ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL;
255 return nullptr;
256 } else {
257 return move_dtor_impl<T>;
258 }
259}
260
261// Traits to check for operator<, operator>, and operator==.
262using std::void_t;
263
264// These traits cause a "float comparison warning" in some compilers
265// when `T` is float or double.
266// Disable this warning with the following pragmas.
267#if defined(__clang__)
268 #pragma clang diagnostic push
269 #pragma clang diagnostic ignored "-Wfloat-equal"
270#elif defined(__GNUC__) && !defined(__clang__)
271 #pragma GCC diagnostic push
272 #pragma GCC diagnostic ignored "-Wfloat-equal"
273#endif
274
275// Trait to check for operator<.
276template <typename T, typename = void>
277 struct has_operator_less : std::false_type {};
278
279// Only enable if T has an operator< that takes T as the right-hand side (no implicit conversion).
280template <typename T>
281 struct has_operator_less<T, void_t<decltype(std::declval<const T&>() < std::declval<const T&>())>> :
282 std::is_same<decltype(std::declval<const T&>() < std::declval<const T&>()), bool> {};
283
284// Trait to check for operator>.
285template <typename T, typename = void>
286struct has_operator_greater : std::false_type {};
287
288// Only enable if T has an operator> that takes T as the right-hand side (no implicit conversion).
289template <typename T>
290struct has_operator_greater<T, void_t<decltype(std::declval<const T&>() > std::declval<const T&>())>> :
291 std::is_same<decltype(std::declval<const T&>() > std::declval<const T&>()), bool> {};
292
293// Trait to check for operator==.
294template <typename T, typename = void>
295 struct has_operator_equal : std::false_type {};
296
297// Only enable if T has an operator== that takes T as the right-hand side (no implicit conversion).
298template <typename T>
299 struct has_operator_equal<T, void_t<decltype(std::declval<const T&>() == std::declval<const T&>())>> :
300 std::is_same<decltype(std::declval<const T&>() == std::declval<const T&>()), bool> {};
301
302// Selects the best comparison strategy based on available operators.
303template <typename T>
304int compare_impl(const void *a, const void *b, const ecs_type_info_t *) {
305 const T& lhs = *static_cast<const T*>(a);
306 const T& rhs = *static_cast<const T*>(b);
307
308 if constexpr (has_operator_less<T>::value && has_operator_equal<T>::value) {
309 // 2. Compare function if `<` and `==` are defined (preferred)
310 if (lhs == rhs) return 0;
311 if (lhs < rhs) return -1;
312 return 1;
313 } else if constexpr (has_operator_greater<T>::value && has_operator_equal<T>::value) {
314 // 3. Compare function if `>` and `==` are defined, deducing `<`
315 if (lhs == rhs) return 0;
316 if (lhs > rhs) return 1;
317 return -1;
318 } else if constexpr (has_operator_less<T>::value && has_operator_greater<T>::value) {
319 // 1. Compare function if `<`, `>` are defined
320 if (lhs < rhs) return -1;
321 if (lhs > rhs) return 1;
322 return 0;
323 } else if constexpr (has_operator_less<T>::value) {
324 // 4. Compare function if only `<` is defined
325 if (lhs < rhs) return -1;
326 if (rhs < lhs) return 1;
327 return 0;
328 } else if constexpr (has_operator_greater<T>::value) {
329 // 5. Compare function if only `>` is defined
330 if (lhs > rhs) return 1;
331 if (rhs > lhs) return -1;
332 return 0;
333 } else {
334 // This branch should never be instantiated due to the compare() check.
335 return 0;
336 }
337}
338
339// To have a generated compare hook, at least
340// operator> or operator< must be defined.
341template <typename T>
342 ecs_cmp_t compare() {
343 if constexpr (has_operator_less<T>::value || has_operator_greater<T>::value) {
344 return compare_impl<T>;
345 } else {
346 return NULL;
347 }
348}
349
350// Equals implementation.
351template <typename T>
352bool equals_impl(const void *a, const void *b, const ecs_type_info_t *) {
353 const T& lhs = *static_cast<const T*>(a);
354 const T& rhs = *static_cast<const T*>(b);
355 return lhs == rhs;
356}
357
358template <typename T>
359 ecs_equals_t equals() {
360 if constexpr (has_operator_equal<T>::value) {
361 return equals_impl<T>;
362 } else {
363 return NULL;
364 }
365}
366
367// Re-enable the float comparison warning.
368#if defined(__clang__)
369 #pragma clang diagnostic pop
370#elif defined(__GNUC__) && !defined(__clang__)
371 #pragma GCC diagnostic pop
372#endif
373
374} // namespace _
375} // namespace flecs
ecs_assert
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:473
ECS_INTERNAL_ERROR
#define ECS_INTERNAL_ERROR
Internal error code.
Definition log.h:671
ecs_type_info_t
struct ecs_type_info_t ecs_type_info_t
Type information.
Definition flecs.h:487
ecs_cmp_t
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:679
ecs_copy_t
void(* ecs_copy_t)(void *dst_ptr, const void *src_ptr, int32_t count, const ecs_type_info_t *type_info)
Copy is invoked when a component is copied into another component.
Definition flecs.h:665
ecs_equals_t
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:685
ecs_move_t
void(* ecs_move_t)(void *dst_ptr, void *src_ptr, int32_t count, const ecs_type_info_t *type_info)
Move is invoked when a component is moved to another component.
Definition flecs.h:672
ecs_xtor_t
void(* ecs_xtor_t)(void *ptr, int32_t count, const ecs_type_info_t *type_info)
Constructor/destructor callback.
Definition flecs.h:659
flecs::_
Int to enum.
Definition component.hpp:18
ecs_type_info_t
Type that contains component information (passed to ctors/dtors/...).
Definition flecs.h:1019
ecs_type_info_t::size
ecs_size_t size
Size of the type.
Definition flecs.h:1020
flecs::_::has_operator_equal
Definition lifecycle_traits.hpp:295
flecs::_::has_operator_greater
Definition lifecycle_traits.hpp:286
flecs::_::has_operator_less
Definition lifecycle_traits.hpp:277
flecs::is_flecs_constructible
Trait to test if a type is constructible by Flecs.
Definition lifecycle_traits.hpp:155
flecs::if_not_t
enable_if_t< false==V, int > if_not_t
Convenience enable_if alias for negated conditions.
Definition utils.hpp:172
flecs::if_t
enable_if_t< V, int > if_t
Convenience enable_if alias using int as default type.
Definition utils.hpp:168