Flecs v4.0
A fast entity component system (ECS) for C & C++
Loading...
Searching...
No Matches
lifecycle_traits.hpp
Go to the documentation of this file.
1
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(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 & assign dst to src
122 dst_arr[i] = FLECS_MOV(src_arr[i]);
123 // Destruct src. Move should have left 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 // Cleanup 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} // _
152
153// Trait to test if type is constructible by flecs
154template <typename T>
156 static constexpr bool value =
157 std::is_default_constructible<actual_type_t<T>>::value;
158};
159
160namespace _
161{
162
163// Trivially constructible
164template <typename T, if_t< std::is_trivially_constructible<T>::value > = 0>
165ecs_xtor_t ctor(ecs_flags32_t &) {
166 return nullptr;
167}
168
169// Not constructible by flecs
170template <typename T, if_t<
171 ! std::is_default_constructible<T>::value > = 0>
172ecs_xtor_t ctor(ecs_flags32_t &flags) {
173 flags |= ECS_TYPE_HOOK_CTOR_ILLEGAL;
174 return nullptr;
175}
176
177// Default constructible
178template <typename T, if_t<
179 ! std::is_trivially_constructible<T>::value &&
180 std::is_default_constructible<T>::value > = 0>
181ecs_xtor_t ctor(ecs_flags32_t &) {
182 return ctor_impl<T>;
183}
184
185// No dtor
186template <typename T, if_t< std::is_trivially_destructible<T>::value > = 0>
187ecs_xtor_t dtor(ecs_flags32_t &) {
188 return nullptr;
189}
190
191// Dtor
192template <typename T, if_t<
193 std::is_destructible<T>::value &&
194 ! std::is_trivially_destructible<T>::value > = 0>
195ecs_xtor_t dtor(ecs_flags32_t &) {
196 return dtor_impl<T>;
197}
198
199// Assert when the type cannot be destructed
200template <typename T, if_not_t< std::is_destructible<T>::value > = 0>
201ecs_xtor_t dtor(ecs_flags32_t &flags) {
202 flecs_static_assert(always_false<T>::value,
203 "component type must be destructible");
204 flags |= ECS_TYPE_HOOK_DTOR_ILLEGAL;
205 return nullptr;
206}
207
208// Trivially copyable
209template <typename T, if_t< std::is_trivially_copyable<T>::value > = 0>
210ecs_copy_t copy(ecs_flags32_t &) {
211 return nullptr;
212}
213
214// Not copyable
215template <typename T, if_t<
216 ! std::is_trivially_copyable<T>::value &&
217 ! std::is_copy_assignable<T>::value > = 0>
218ecs_copy_t copy(ecs_flags32_t &flags) {
219 flags |= ECS_TYPE_HOOK_COPY_ILLEGAL;
220 return nullptr;
221}
222
223// Copy assignment
224template <typename T, if_t<
225 std::is_copy_assignable<T>::value &&
226 ! std::is_trivially_copyable<T>::value > = 0>
227ecs_copy_t copy(ecs_flags32_t &) {
228 return copy_impl<T>;
229}
230
231// Trivially move assignable
232template <typename T, if_t< std::is_trivially_move_assignable<T>::value > = 0>
233ecs_move_t move(ecs_flags32_t &) {
234 return nullptr;
235}
236
237// Component types must be move assignable
238template <typename T, if_not_t< std::is_move_assignable<T>::value > = 0>
239ecs_move_t move(ecs_flags32_t &flags) {
240 flags |= ECS_TYPE_HOOK_MOVE_ILLEGAL;
241 return nullptr;
242}
243
244// Move assignment
245template <typename T, if_t<
246 std::is_move_assignable<T>::value &&
247 ! std::is_trivially_move_assignable<T>::value > = 0>
248ecs_move_t move(ecs_flags32_t &) {
249 return move_impl<T>;
250}
251
252// Trivially copy constructible
253template <typename T, if_t<
254 std::is_trivially_copy_constructible<T>::value > = 0>
255ecs_copy_t copy_ctor(ecs_flags32_t &) {
256 return nullptr;
257}
258
259// No copy ctor
260template <typename T, if_t< ! std::is_copy_constructible<T>::value > = 0>
261ecs_copy_t copy_ctor(ecs_flags32_t &flags) {
262 flags |= ECS_TYPE_HOOK_COPY_CTOR_ILLEGAL;
263 return nullptr;
264
265}
266
267// Copy ctor
268template <typename T, if_t<
269 std::is_copy_constructible<T>::value &&
270 ! std::is_trivially_copy_constructible<T>::value > = 0>
271ecs_copy_t copy_ctor(ecs_flags32_t &) {
272 return copy_ctor_impl<T>;
273}
274
275// Trivially move constructible
276template <typename T, if_t<
277 std::is_trivially_move_constructible<T>::value > = 0>
278ecs_move_t move_ctor(ecs_flags32_t &) {
279 return nullptr;
280}
281
282// Component types must be move constructible
283template <typename T, if_not_t< std::is_move_constructible<T>::value > = 0>
284ecs_move_t move_ctor(ecs_flags32_t &flags) {
285 flags |= ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL;
286 return nullptr;
287}
288
289// Move ctor
290template <typename T, if_t<
291 std::is_move_constructible<T>::value &&
292 ! std::is_trivially_move_constructible<T>::value > = 0>
293ecs_move_t move_ctor(ecs_flags32_t &) {
294 return move_ctor_impl<T>;
295}
296
297// Trivial merge (move assign + dtor)
298template <typename T, if_t<
299 std::is_trivially_move_constructible<T>::value &&
300 std::is_trivially_destructible<T>::value > = 0>
301ecs_move_t ctor_move_dtor(ecs_flags32_t &) {
302 return nullptr;
303}
304
305// Component types must be move constructible and destructible
306template <typename T, if_t<
307 ! std::is_move_constructible<T>::value ||
308 ! std::is_destructible<T>::value > = 0>
309ecs_move_t ctor_move_dtor(ecs_flags32_t &flags) {
310 flags |= ECS_TYPE_HOOK_CTOR_MOVE_DTOR_ILLEGAL;
311 return nullptr;
312}
313
314// Merge ctor + dtor
315template <typename T, if_t<
316 !(std::is_trivially_move_constructible<T>::value &&
317 std::is_trivially_destructible<T>::value) &&
318 std::is_move_constructible<T>::value &&
319 std::is_destructible<T>::value > = 0>
320ecs_move_t ctor_move_dtor(ecs_flags32_t &) {
321 return ctor_move_dtor_impl<T>;
322}
323
324// Trivial merge (move assign + dtor)
325template <typename T, if_t<
326 std::is_trivially_move_assignable<T>::value &&
327 std::is_trivially_destructible<T>::value > = 0>
328ecs_move_t move_dtor(ecs_flags32_t &) {
329 return nullptr;
330}
331
332// Component types must be move constructible and destructible
333template <typename T, if_t<
334 ! std::is_move_assignable<T>::value ||
335 ! std::is_destructible<T>::value > = 0>
336ecs_move_t move_dtor(ecs_flags32_t &flags) {
337 flags |= ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL;
338 return nullptr;
339}
340
341// Merge assign + dtor
342template <typename T, if_t<
343 !(std::is_trivially_move_assignable<T>::value &&
344 std::is_trivially_destructible<T>::value) &&
345 std::is_move_assignable<T>::value &&
346 std::is_destructible<T>::value > = 0>
347ecs_move_t move_dtor(ecs_flags32_t &) {
348 return move_dtor_impl<T>;
349}
350
351// Traits to check for operator<, operator>, and operator==
352template<typename...>
353using void_t = void;
354
355// These traits causes a "float comparison warning" in some compilers
356// when `T` is float or double.
357// Disable this warning with the following pragmas:
358#if defined(__clang__)
359 #pragma clang diagnostic push
360 #pragma clang diagnostic ignored "-Wfloat-equal"
361#elif defined(__GNUC__) && !defined(__clang__)
362 #pragma GCC diagnostic push
363 #pragma GCC diagnostic ignored "-Wfloat-equal"
364#endif
365
366// Trait to check for operator<
367template <typename T, typename = void>
368struct has_operator_less : std::false_type {};
369
370// Only enable if T has an operator< that takes T as the right-hand side (no implicit conversion)
371template <typename T>
372struct has_operator_less<T, void_t<decltype(std::declval<const T&>() < std::declval<const T&>())>> :
373 std::is_same<decltype(std::declval<const T&>() < std::declval<const T&>()), bool> {};
374
375// Trait to check for operator>
376template <typename T, typename = void>
377struct has_operator_greater : std::false_type {};
378
379// Only enable if T has an operator> that takes T as the right-hand side (no implicit conversion)
380template <typename T>
382 std::is_same<decltype(std::declval<const T&>() > std::declval<const T&>()), bool> {};
383
384// Trait to check for operator==
385template <typename T, typename = void>
386struct has_operator_equal : std::false_type {};
387
388// Only enable if T has an operator== that takes T as the right-hand side (no implicit conversion)
389template <typename T>
390struct has_operator_equal<T, void_t<decltype(std::declval<const T&>() == std::declval<const T&>())>> :
391 std::is_same<decltype(std::declval<const T&>() == std::declval<const T&>()), bool> {};
392
393// 1. Compare function if `<`, `>`, are defined
394template <typename T, if_t<
398int compare_impl(const void *a, const void *b, const ecs_type_info_t *) {
399 const T& lhs = *static_cast<const T*>(a);
400 const T& rhs = *static_cast<const T*>(b);
401 if (lhs < rhs) return -1;
402 if (lhs > rhs) return 1;
403 return 0;
404}
405
406// 2. Compare function if `<` and `==` are defined, ignoring `>`
407// if defined.
408template <typename T, if_t<
409 has_operator_less<T>::value &&
410 has_operator_equal<T>::value > = 0>
411int compare_impl(const void *a, const void *b, const ecs_type_info_t *) {
412 const T& lhs = *static_cast<const T*>(a);
413 const T& rhs = *static_cast<const T*>(b);
414 if (lhs == rhs) return 0;
415 if (lhs < rhs) return -1;
416 return 1; // If not less and not equal, must be greater
417}
418
419// 3. Compare function if `>` and `==` are defined, deducing `<`
420template <typename T, if_t<
421 has_operator_greater<T>::value &&
422 has_operator_equal<T>::value &&
423 !has_operator_less<T>::value > = 0>
424int compare_impl(const void *a, const void *b, const ecs_type_info_t *) {
425 const T& lhs = *static_cast<const T*>(a);
426 const T& rhs = *static_cast<const T*>(b);
427 if (lhs == rhs) return 0;
428 if (lhs > rhs) return 1;
429 return -1; // If not greater and not equal, must be less
430}
431
432// 4. Compare function if only `<` is defined, deducing the rest
433template <typename T, if_t<
434 has_operator_less<T>::value &&
435 !has_operator_greater<T>::value &&
436 !has_operator_equal<T>::value > = 0>
437int compare_impl(const void *a, const void *b, const ecs_type_info_t *) {
438 const T& lhs = *static_cast<const T*>(a);
439 const T& rhs = *static_cast<const T*>(b);
440 if (lhs < rhs) return -1;
441 if (rhs < lhs) return 1;
442 return 0; // If neither is less, they must be equal
443}
444
445// 5. Compare function if only `>` is defined, deducing the rest
446template <typename T, if_t<
447 has_operator_greater<T>::value &&
448 !has_operator_less<T>::value &&
449 !has_operator_equal<T>::value > = 0>
450int compare_impl(const void *a, const void *b, const ecs_type_info_t *) {
451 const T& lhs = *static_cast<const T*>(a);
452 const T& rhs = *static_cast<const T*>(b);
453 if (lhs > rhs) return 1;
454 if (rhs > lhs) return -1;
455 return 0; // If neither is greater, they must be equal
456}
457
458// In order to have a generated compare hook, at least
459// operator> or operator< must be defined:
460template <typename T, if_t<
461 has_operator_less<T>::value ||
462 has_operator_greater<T>::value > = 0>
463ecs_cmp_t compare() {
464 return compare_impl<T>;
465}
466
467template <typename T, if_t<
468 !has_operator_less<T>::value &&
469 !has_operator_greater<T>::value > = 0>
470ecs_cmp_t compare() {
471 return NULL;
472}
473
474// Equals function enabled only if `==` is defined
475template <typename T, if_t<
476 has_operator_equal<T>::value > = 0>
477bool equals_impl(const void *a, const void *b, const ecs_type_info_t *) {
478 const T& lhs = *static_cast<const T*>(a);
479 const T& rhs = *static_cast<const T*>(b);
480 return lhs == rhs;
481}
482
483template <typename T, if_t<
484 has_operator_equal<T>::value > = 0>
485ecs_equals_t equals() {
486 return equals_impl<T>;
487}
488
489template <typename T, if_t<
490 !has_operator_equal<T>::value > = 0>
491ecs_equals_t equals() {
492 return NULL;
493}
494
495// re-enable the float comparison warning:
496#if defined(__clang__)
497 #pragma clang diagnostic pop
498#elif defined(__GNUC__) && !defined(__clang__)
499 #pragma GCC diagnostic pop
500#endif
501
502} // _
503} // flecs
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:368
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:658
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:644
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:664
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:651
void(* ecs_xtor_t)(void *ptr, int32_t count, const ecs_type_info_t *type_info)
Constructor/destructor callback.
Definition flecs.h:638
Type that contains component information (passed to ctors/dtors/...)
Definition flecs.h:989
ecs_size_t size
Size of type.
Definition flecs.h:990