Flecs v3.2
A fast entity component system (ECS) for C & C++
Loading...
Searching...
No Matches
delegate.hpp
Go to the documentation of this file.
1
6#pragma once
7
8namespace flecs
9{
10
11namespace _
12{
13
14// Binding ctx for component hooks
16 void *on_add = nullptr;
17 void *on_remove = nullptr;
18 void *on_set = nullptr;
19 ecs_ctx_free_t free_on_add = nullptr;
20 ecs_ctx_free_t free_on_remove = nullptr;
21 ecs_ctx_free_t free_on_set = nullptr;
22
24 if (on_add && free_on_add) {
25 free_on_add(on_add);
26 }
27 if (on_remove && free_on_remove) {
28 free_on_remove(on_remove);
29 }
30 if (on_set && free_on_set) {
31 free_on_set(on_set);
32 }
33 }
34};
35
36// Utility to convert template argument pack to array of term ptrs
37struct term_ptr {
38 void *ptr;
39 bool is_ref;
40};
41
42template <typename ... Components>
43struct term_ptrs {
44 using array = flecs::array<_::term_ptr, sizeof...(Components)>;
45
46 bool populate(const ecs_iter_t *iter) {
47 return populate(iter, 0, static_cast<
48 remove_reference_t<
49 remove_pointer_t<Components>>
50 *>(nullptr)...);
51 }
52
53 array m_terms;
54
55private:
56 /* Populate terms array without checking for references */
57 bool populate(const ecs_iter_t*, size_t) { return false; }
58
59 template <typename T, typename... Targs>
60 bool populate(const ecs_iter_t *iter, size_t index, T, Targs... comps) {
61 m_terms[index].ptr = iter->ptrs[index];
62 bool is_ref = iter->sources && iter->sources[index] != 0;
63 m_terms[index].is_ref = is_ref;
64 is_ref |= populate(iter, index + 1, comps ...);
65 return is_ref;
66 }
67};
68
69struct delegate { };
70
71// Template that figures out from the template parameters of a query/system
72// how to pass the value to the each callback
73template <typename T, typename = int>
74struct each_column { };
75
76// Base class
78 each_column_base(const _::term_ptr& term, size_t row)
79 : m_term(term), m_row(row) { }
80
81protected:
82 const _::term_ptr& m_term;
83 size_t m_row;
84};
85
86// If type is not a pointer, return a reference to the type (default case)
87template <typename T>
88struct each_column<T, if_t< !is_pointer<T>::value &&
89 !is_empty<actual_type_t<T>>::value && is_actual<T>::value > >
91{
92 each_column(const _::term_ptr& term, size_t row)
93 : each_column_base(term, row) { }
94
95 T& get_row() {
96 return static_cast<T*>(this->m_term.ptr)[this->m_row];
97 }
98};
99
100// If argument type is not the same as actual component type, return by value.
101// This requires that the actual type can be converted to the type.
102// A typical scenario where this happens is when using flecs::pair types.
103template <typename T>
104struct each_column<T, if_t< !is_pointer<T>::value &&
105 !is_empty<actual_type_t<T>>::value && !is_actual<T>::value> >
107{
108 each_column(const _::term_ptr& term, size_t row)
109 : each_column_base(term, row) { }
110
111 T get_row() {
112 return static_cast<actual_type_t<T>*>(this->m_term.ptr)[this->m_row];
113 }
114};
115
116
117// If type is empty (indicating a tag) the query will pass a nullptr. To avoid
118// returning nullptr to reference arguments, return a temporary value.
119template <typename T>
120struct each_column<T, if_t< is_empty<actual_type_t<T>>::value &&
121 !is_pointer<T>::value > >
123{
124 each_column(const _::term_ptr& term, size_t row)
125 : each_column_base(term, row) { }
126
127 T get_row() {
128 return actual_type_t<T>();
129 }
130};
131
132
133// If type is a pointer (indicating an optional value) return the type as is
134template <typename T>
135struct each_column<T, if_t< is_pointer<T>::value &&
136 !is_empty<actual_type_t<T>>::value > >
138{
139 each_column(const _::term_ptr& term, size_t row)
140 : each_column_base(term, row) { }
141
142 actual_type_t<T> get_row() {
143 if (this->m_term.ptr) {
144 return &static_cast<actual_type_t<T>>(this->m_term.ptr)[this->m_row];
145 } else {
146 // optional argument doesn't have a value
147 return nullptr;
148 }
149 }
150};
151
152// If the query contains component references to other entities, check if the
153// current argument is one.
154template <typename T, typename = int>
155struct each_ref_column : public each_column<T> {
156 each_ref_column(const _::term_ptr& term, size_t row)
157 : each_column<T>(term, row) {
158
159 if (term.is_ref) {
160 // If this is a reference, set the row to 0 as a ref always is a
161 // single value, not an array. This prevents the application from
162 // having to do an if-check on whether the column is owned.
163 //
164 // This check only happens when the current table being iterated
165 // over caused the query to match a reference. The check is
166 // performed once per iterated table.
167 this->m_row = 0;
168 }
169 }
170};
171
172template <typename Func, typename ... Components>
173struct each_delegate : public delegate {
174 // If the number of arguments in the function signature is one more than the
175 // number of components in the query, an extra entity arg is required.
176 static constexpr bool PassEntity =
177 (sizeof...(Components) + 1) == (arity<Func>::value);
178
179 // If the number of arguments in the function is two more than the number of
180 // components in the query, extra iter + index arguments are required.
181 static constexpr bool PassIter =
182 (sizeof...(Components) + 2) == (arity<Func>::value);
183
184 static_assert(arity<Func>::value > 0,
185 "each() must have at least one argument");
186
187 using Terms = typename term_ptrs<Components ...>::array;
188
189 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
190 explicit each_delegate(Func&& func) noexcept
191 : m_func(FLECS_MOV(func)) { }
192
193 explicit each_delegate(const Func& func) noexcept
194 : m_func(func) { }
195
196 // Invoke object directly. This operation is useful when the calling
197 // function has just constructed the delegate, such as what happens when
198 // iterating a query.
199 void invoke(ecs_iter_t *iter) const {
200 term_ptrs<Components...> terms;
201
202 iter->flags |= EcsIterCppEach;
203
204 if (terms.populate(iter)) {
205 invoke_callback< each_ref_column >(iter, m_func, 0, terms.m_terms);
206 } else {
207 invoke_callback< each_column >(iter, m_func, 0, terms.m_terms);
208 }
209 }
210
211 // Static function that can be used as callback for systems/triggers
212 static void run(ecs_iter_t *iter) {
213 auto self = static_cast<const each_delegate*>(iter->binding_ctx);
214 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
215 self->invoke(iter);
216 }
217
218 // Create instance of delegate
219 static each_delegate* make(const Func& func) {
220 return FLECS_NEW(each_delegate)(func);
221 }
222
223 // Function that can be used as callback to free delegate
224 static void destruct(void *obj) {
225 _::free_obj<each_delegate>(static_cast<each_delegate*>(obj));
226 }
227
228 // Static function to call for component on_add hook
229 static void run_add(ecs_iter_t *iter) {
230 component_binding_ctx *ctx = reinterpret_cast<component_binding_ctx*>(
231 iter->binding_ctx);
232 iter->binding_ctx = ctx->on_add;
233 run(iter);
234 }
235
236 // Static function to call for component on_remove hook
237 static void run_remove(ecs_iter_t *iter) {
238 component_binding_ctx *ctx = reinterpret_cast<component_binding_ctx*>(
239 iter->binding_ctx);
240 iter->binding_ctx = ctx->on_remove;
241 run(iter);
242 }
243
244 // Static function to call for component on_set hook
245 static void run_set(ecs_iter_t *iter) {
246 component_binding_ctx *ctx = reinterpret_cast<component_binding_ctx*>(
247 iter->binding_ctx);
248 iter->binding_ctx = ctx->on_set;
249 run(iter);
250 }
251
252 // Each delegates always use instanced iterators
253 static bool instanced() {
254 return true;
255 }
256
257private:
258 // Number of function arguments is one more than number of components, pass
259 // entity as argument.
260 template <template<typename X, typename = int> class ColumnType,
261 typename... Args, if_t<
262 sizeof...(Components) == sizeof...(Args) && PassEntity> = 0>
263 static void invoke_callback(
264 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
265 {
266 ECS_TABLE_LOCK(iter->world, iter->table);
267
268 ecs_world_t *world = iter->world;
269 size_t count = static_cast<size_t>(iter->count);
270
271 for (size_t i = 0; i < count; i ++) {
272 func(flecs::entity(world, iter->entities[i]),
273 (ColumnType< remove_reference_t<Components> >(comps, i)
274 .get_row())...);
275 }
276
277 ECS_TABLE_UNLOCK(iter->world, iter->table);
278 }
279
280 // Number of function arguments is two more than number of components, pass
281 // iter + index as argument.
282 template <template<typename X, typename = int> class ColumnType,
283 typename... Args, int Enabled = PassIter, if_t<
284 sizeof...(Components) == sizeof...(Args) && Enabled> = 0>
285 static void invoke_callback(
286 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
287 {
288 size_t count = static_cast<size_t>(iter->count);
289 if (count == 0 && !iter->table) {
290 // If query has no This terms, count can be 0. Since each does not
291 // have an entity parameter, just pass through components
292 count = 1;
293 }
294
295 flecs::iter it(iter);
296
297 ECS_TABLE_LOCK(iter->world, iter->table);
298
299 for (size_t i = 0; i < count; i ++) {
300 func(it, i, (ColumnType< remove_reference_t<Components> >(comps, i)
301 .get_row())...);
302 }
303
304 ECS_TABLE_UNLOCK(iter->world, iter->table);
305 }
306
307 // Number of function arguments is equal to number of components, no entity
308 template <template<typename X, typename = int> class ColumnType,
309 typename... Args, if_t<
310 sizeof...(Components) == sizeof...(Args) && !PassEntity && !PassIter> = 0>
311 static void invoke_callback(
312 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
313 {
314 size_t count = static_cast<size_t>(iter->count);
315 if (count == 0 && !iter->table) {
316 // If query has no This terms, count can be 0. Since each does not
317 // have an entity parameter, just pass through components
318 count = 1;
319 }
320
321 flecs::iter it(iter);
322
323 ECS_TABLE_LOCK(iter->world, iter->table);
324
325 for (size_t i = 0; i < count; i ++) {
326 func( (ColumnType< remove_reference_t<Components> >(comps, i)
327 .get_row())...);
328 }
329
330 ECS_TABLE_UNLOCK(iter->world, iter->table);
331 }
332
333 template <template<typename X, typename = int> class ColumnType,
334 typename... Args, if_t< sizeof...(Components) != sizeof...(Args) > = 0>
335 static void invoke_callback(ecs_iter_t *iter, const Func& func,
336 size_t index, Terms& columns, Args... comps)
337 {
338 invoke_callback<ColumnType>(
339 iter, func, index + 1, columns, comps..., columns[index]);
340 }
341
342 Func m_func;
343};
344
345template <typename Func, typename ... Components>
346struct find_delegate : public delegate {
347 // If the number of arguments in the function signature is one more than the
348 // number of components in the query, an extra entity arg is required.
349 static constexpr bool PassEntity =
350 (sizeof...(Components) + 1) == (arity<Func>::value);
351
352 // If the number of arguments in the function is two more than the number of
353 // components in the query, extra iter + index arguments are required.
354 static constexpr bool PassIter =
355 (sizeof...(Components) + 2) == (arity<Func>::value);
356
357 static_assert(arity<Func>::value > 0,
358 "each() must have at least one argument");
359
360 using Terms = typename term_ptrs<Components ...>::array;
361
362 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
363 explicit find_delegate(Func&& func) noexcept
364 : m_func(FLECS_MOV(func)) { }
365
366 explicit find_delegate(const Func& func) noexcept
367 : m_func(func) { }
368
369 // Invoke object directly. This operation is useful when the calling
370 // function has just constructed the delegate, such as what happens when
371 // iterating a query.
372 flecs::entity invoke(ecs_iter_t *iter) const {
373 term_ptrs<Components...> terms;
374
375 if (terms.populate(iter)) {
376 return invoke_callback< each_ref_column >(iter, m_func, 0, terms.m_terms);
377 } else {
378 return invoke_callback< each_column >(iter, m_func, 0, terms.m_terms);
379 }
380 }
381
382 // Find delegates always use instanced iterators
383 static bool instanced() {
384 return true;
385 }
386
387private:
388 // Number of function arguments is one more than number of components, pass
389 // entity as argument.
390 template <template<typename X, typename = int> class ColumnType,
391 typename... Args, if_t<
392 sizeof...(Components) == sizeof...(Args) && PassEntity> = 0>
393 static flecs::entity invoke_callback(
394 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
395 {
396 ECS_TABLE_LOCK(iter->world, iter->table);
397
398 ecs_world_t *world = iter->world;
399 size_t count = static_cast<size_t>(iter->count);
400 flecs::entity result;
401
402 ecs_assert(count > 0, ECS_INVALID_OPERATION,
403 "no entities returned, use find() without flecs::entity argument");
404
405 for (size_t i = 0; i < count; i ++) {
406 if (func(flecs::entity(world, iter->entities[i]),
407 (ColumnType< remove_reference_t<Components> >(comps, i)
408 .get_row())...))
409 {
410 result = flecs::entity(world, iter->entities[i]);
411 break;
412 }
413 }
414
415 ECS_TABLE_UNLOCK(iter->world, iter->table);
416
417 return result;
418 }
419
420 // Number of function arguments is two more than number of components, pass
421 // iter + index as argument.
422 template <template<typename X, typename = int> class ColumnType,
423 typename... Args, int Enabled = PassIter, if_t<
424 sizeof...(Components) == sizeof...(Args) && Enabled> = 0>
425 static flecs::entity invoke_callback(
426 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
427 {
428 size_t count = static_cast<size_t>(iter->count);
429 if (count == 0) {
430 // If query has no This terms, count can be 0. Since each does not
431 // have an entity parameter, just pass through components
432 count = 1;
433 }
434
435 flecs::iter it(iter);
436 flecs::entity result;
437
438 ECS_TABLE_LOCK(iter->world, iter->table);
439
440 for (size_t i = 0; i < count; i ++) {
441 if (func(it, i, (ColumnType< remove_reference_t<Components> >(comps, i)
442 .get_row())...))
443 {
444 result = flecs::entity(iter->world, iter->entities[i]);
445 break;
446 }
447 }
448
449 ECS_TABLE_UNLOCK(iter->world, iter->table);
450
451 return result;
452 }
453
454 // Number of function arguments is equal to number of components, no entity
455 template <template<typename X, typename = int> class ColumnType,
456 typename... Args, if_t<
457 sizeof...(Components) == sizeof...(Args) && !PassEntity && !PassIter> = 0>
458 static flecs::entity invoke_callback(
459 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
460 {
461 size_t count = static_cast<size_t>(iter->count);
462 if (count == 0) {
463 // If query has no This terms, count can be 0. Since each does not
464 // have an entity parameter, just pass through components
465 count = 1;
466 }
467
468 flecs::iter it(iter);
469 flecs::entity result;
470
471 ECS_TABLE_LOCK(iter->world, iter->table);
472
473 for (size_t i = 0; i < count; i ++) {
474 if (func( (ColumnType< remove_reference_t<Components> >(comps, i)
475 .get_row())...))
476 {
477 result = flecs::entity(iter->world, iter->entities[i]);
478 break;
479 }
480 }
481
482 ECS_TABLE_UNLOCK(iter->world, iter->table);
483
484 return result;
485 }
486
487 template <template<typename X, typename = int> class ColumnType,
488 typename... Args, if_t< sizeof...(Components) != sizeof...(Args) > = 0>
489 static flecs::entity invoke_callback(ecs_iter_t *iter, const Func& func,
490 size_t index, Terms& columns, Args... comps)
491 {
492 return invoke_callback<ColumnType>(
493 iter, func, index + 1, columns, comps..., columns[index]);
494 }
495
496 Func m_func;
497};
498
502
503template <typename Func, typename ... Components>
505private:
506 static constexpr bool IterOnly = arity<Func>::value == 1;
507
508 using Terms = typename term_ptrs<Components ...>::array;
509
510public:
511 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
512 explicit iter_delegate(Func&& func) noexcept
513 : m_func(FLECS_MOV(func)) { }
514
515 explicit iter_delegate(const Func& func) noexcept
516 : m_func(func) { }
517
518 // Invoke object directly. This operation is useful when the calling
519 // function has just constructed the delegate, such as what happens when
520 // iterating a query.
521 void invoke(ecs_iter_t *iter) const {
522 term_ptrs<Components...> terms;
523 terms.populate(iter);
524 invoke_callback(iter, m_func, 0, terms.m_terms);
525 }
526
527 // Static function that can be used as callback for systems/triggers
528 static void run(ecs_iter_t *iter) {
529 auto self = static_cast<const iter_delegate*>(iter->binding_ctx);
530 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
531 self->invoke(iter);
532 }
533
534 // Instancing needs to be enabled explicitly for iter delegates
535 static bool instanced() {
536 return false;
537 }
538
539private:
540 template <typename... Args, if_t<!sizeof...(Args) && IterOnly> = 0>
541 static void invoke_callback(ecs_iter_t *iter, const Func& func,
542 size_t, Terms&, Args...)
543 {
544 flecs::iter it(iter);
545
546 ECS_TABLE_LOCK(iter->world, iter->table);
547
548 func(it);
549
550 ECS_TABLE_UNLOCK(iter->world, iter->table);
551 }
552
553 template <typename... Targs, if_t<!IterOnly &&
554 (sizeof...(Targs) == sizeof...(Components))> = 0>
555 static void invoke_callback(ecs_iter_t *iter, const Func& func, size_t,
556 Terms&, Targs... comps)
557 {
558 flecs::iter it(iter);
559
560 ECS_TABLE_LOCK(iter->world, iter->table);
561
562 func(it, ( static_cast<
563 remove_reference_t<
564 remove_pointer_t<
565 actual_type_t<Components> > >* >
566 (comps.ptr))...);
567
568 ECS_TABLE_UNLOCK(iter->world, iter->table);
569 }
570
571 template <typename... Targs, if_t<!IterOnly &&
572 (sizeof...(Targs) != sizeof...(Components)) > = 0>
573 static void invoke_callback(ecs_iter_t *iter, const Func& func,
574 size_t index, Terms& columns, Targs... comps)
575 {
576 invoke_callback(iter, func, index + 1, columns, comps...,
577 columns[index]);
578 }
579
580 Func m_func;
581};
582
583
587
588template <typename Func>
590 explicit entity_observer_delegate(Func&& func) noexcept
591 : m_func(FLECS_MOV(func)) { }
592
593 // Static function that can be used as callback for systems/triggers
594 static void run(ecs_iter_t *iter) {
595 invoke<Func>(iter);
596 }
597private:
598 template <typename F, if_t<arity<F>::value == 1> = 0>
599 static void invoke(ecs_iter_t *iter) {
600 auto self = static_cast<const entity_observer_delegate*>(iter->binding_ctx);
601 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
602 self->m_func(flecs::entity(iter->world, ecs_field_src(iter, 1)));
603 }
604
605 template <typename F, if_t<arity<F>::value == 0> = 0>
606 static void invoke(ecs_iter_t *iter) {
607 auto self = static_cast<const entity_observer_delegate*>(iter->binding_ctx);
608 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
609 self->m_func();
610 }
611
612 Func m_func;
613};
614
615template <typename Func, typename Event>
617 explicit entity_payload_observer_delegate(Func&& func) noexcept
618 : m_func(FLECS_MOV(func)) { }
619
620 // Static function that can be used as callback for systems/triggers
621 static void run(ecs_iter_t *iter) {
622 invoke<Func>(iter);
623 }
624
625private:
626 template <typename F, if_t<arity<F>::value == 1> = 0>
627 static void invoke(ecs_iter_t *iter) {
628 auto self = static_cast<const entity_payload_observer_delegate*>(
629 iter->binding_ctx);
630 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
631 ecs_assert(iter->param != nullptr, ECS_INVALID_OPERATION,
632 "entity observer invoked without payload");
633
634 Event *data = static_cast<Event*>(iter->param);
635 self->m_func(*data);
636 }
637
638 template <typename F, if_t<arity<F>::value == 2> = 0>
639 static void invoke(ecs_iter_t *iter) {
640 auto self = static_cast<const entity_payload_observer_delegate*>(
641 iter->binding_ctx);
642 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
643 ecs_assert(iter->param != nullptr, ECS_INVALID_OPERATION,
644 "entity observer invoked without payload");
645
646 Event *data = static_cast<Event*>(iter->param);
647 self->m_func(flecs::entity(iter->world, ecs_field_src(iter, 1)), *data);
648 }
649
650 Func m_func;
651};
652
653
657
658template<typename ... Args>
660
661template<typename ... Args>
663 using ColumnArray = flecs::array<int32_t, sizeof...(Args)>;
664 using ArrayType = flecs::array<void*, sizeof...(Args)>;
665 using DummyArray = flecs::array<int, sizeof...(Args)>;
666 using IdArray = flecs::array<id_t, sizeof...(Args)>;
667
668 static bool const_args() {
669 static flecs::array<bool, sizeof...(Args)> is_const_args ({
670 flecs::is_const<flecs::remove_reference_t<Args>>::value...
671 });
672
673 for (auto is_const : is_const_args) {
674 if (!is_const) {
675 return false;
676 }
677 }
678 return true;
679 }
680
681 static
682 bool get_ptrs(world_t *world, const ecs_record_t *r, ecs_table_t *table,
683 ArrayType& ptrs)
684 {
685 ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL);
687 return false;
688 }
689
690 /* table_index_of needs real world */
691 const flecs::world_t *real_world = ecs_get_world(world);
692
693 /* Get column indices for components */
694 ColumnArray columns ({
696 _::cpp_type<Args>().id(world))...
697 });
698
699 /* Get pointers for columns for entity */
700 size_t i = 0;
701 for (int32_t column : columns) {
702 if (column == -1) {
703 return false;
704 }
705
706 ptrs[i ++] = ecs_record_get_column(r, column, 0);
707 }
708
709 return true;
710 }
711
712 static bool ensure_ptrs(world_t *world, ecs_entity_t e, ArrayType& ptrs) {
713 /* Get pointers w/ensure */
714 size_t i = 0;
715 DummyArray dummy ({
716 (ptrs[i ++] = ecs_ensure_id(world, e,
717 _::cpp_type<Args>().id(world)), 0)...
718 });
719
720 return true;
721 }
722
723 template <typename Func>
724 static bool invoke_read(world_t *world, entity_t e, const Func& func) {
725 const ecs_record_t *r = ecs_read_begin(world, e);
726 if (!r) {
727 return false;
728 }
729
730 ecs_table_t *table = r->table;
731 if (!table) {
732 return false;
733 }
734
735 ArrayType ptrs;
736 bool has_components = get_ptrs(world, r, table, ptrs);
737 if (has_components) {
738 invoke_callback(func, 0, ptrs);
739 }
740
741 ecs_read_end(r);
742
743 return has_components;
744 }
745
746 template <typename Func>
747 static bool invoke_write(world_t *world, entity_t e, const Func& func) {
749 if (!r) {
750 return false;
751 }
752
753 ecs_table_t *table = r->table;
754 if (!table) {
755 return false;
756 }
757
758 ArrayType ptrs;
759 bool has_components = get_ptrs(world, r, table, ptrs);
760 if (has_components) {
761 invoke_callback(func, 0, ptrs);
762 }
763
764 ecs_write_end(r);
765
766 return has_components;
767 }
768
769 template <typename Func>
770 static bool invoke_get(world_t *world, entity_t e, const Func& func) {
771 if (const_args()) {
772 return invoke_read(world, e, func);
773 } else {
774 return invoke_write(world, e, func);
775 }
776 }
777
778 // Utility for storing id in array in pack expansion
779 static size_t store_added(IdArray& added, size_t elem, ecs_table_t *prev,
780 ecs_table_t *next, id_t id)
781 {
782 // Array should only contain ids for components that are actually added,
783 // so check if the prev and next tables are different.
784 if (prev != next) {
785 added[elem] = id;
786 elem ++;
787 }
788 return elem;
789 }
790
791 template <typename Func>
792 static bool invoke_ensure(world_t *world, entity_t id, const Func& func) {
794
795 ArrayType ptrs;
796 ecs_table_t *table = NULL;
797
798 // When not deferred take the fast path.
799 if (!w.is_deferred()) {
800 // Bit of low level code so we only do at most one table move & one
801 // entity lookup for the entire operation.
802
803 // Make sure the object is not a stage. Operations on a stage are
804 // only allowed when the stage is in deferred mode, which is when
805 // the world is in readonly mode.
806 ecs_assert(!w.is_stage(), ECS_INVALID_PARAMETER, NULL);
807
808 // Find table for entity
810 if (r) {
811 table = r->table;
812 }
813
814 // Find destination table that has all components
815 ecs_table_t *prev = table, *next;
816 size_t elem = 0;
817 IdArray added;
818
819 // Iterate components, only store added component ids in added array
820 DummyArray dummy_before ({ (
821 next = ecs_table_add_id(world, prev, w.id<Args>()),
822 elem = store_added(added, elem, prev, next, w.id<Args>()),
823 prev = next, 0
824 )... });
825 (void)dummy_before;
826
827 // If table is different, move entity straight to it
828 if (table != next) {
829 ecs_type_t ids;
830 ids.array = added.ptr();
831 ids.count = static_cast<ecs_size_t>(elem);
832 ecs_commit(world, id, r, next, &ids, NULL);
833 table = next;
834 }
835
836 if (!get_ptrs(w, r, table, ptrs)) {
837 ecs_abort(ECS_INTERNAL_ERROR, NULL);
838 }
839
840 ECS_TABLE_LOCK(world, table);
841
842 // When deferred, obtain pointers with regular ensure
843 } else {
844 ensure_ptrs(world, id, ptrs);
845 }
846
847 invoke_callback(func, 0, ptrs);
848
849 if (!w.is_deferred()) {
850 ECS_TABLE_UNLOCK(world, table);
851 }
852
853 // Call modified on each component
854 DummyArray dummy_after ({
855 ( ecs_modified_id(world, id, w.id<Args>()), 0)...
856 });
857 (void)dummy_after;
858
859 return true;
860 }
861
862private:
863 template <typename Func, typename ... TArgs,
864 if_t<sizeof...(TArgs) == sizeof...(Args)> = 0>
865 static void invoke_callback(
866 const Func& f, size_t, ArrayType&, TArgs&& ... comps)
867 {
868 f(*static_cast<typename base_arg_type<Args>::type*>(comps)...);
869 }
870
871 template <typename Func, typename ... TArgs,
872 if_t<sizeof...(TArgs) != sizeof...(Args)> = 0>
873 static void invoke_callback(const Func& f, size_t arg, ArrayType& ptrs,
874 TArgs&& ... comps)
875 {
876 invoke_callback(f, arg + 1, ptrs, comps..., ptrs[arg]);
877 }
878};
879
880template <typename Func, typename U = int>
882 static_assert(function_traits<Func>::value, "type is not callable");
883};
884
885template <typename Func>
886struct entity_with_delegate<Func, if_t< is_callable<Func>::value > >
887 : entity_with_delegate_impl< arg_list_t<Func> >
888{
889 static_assert(function_traits<Func>::arity > 0,
890 "function must have at least one argument");
891};
892
893} // namespace _
894
895// Experimental: allows using the each delegate for use cases outside of flecs
896template <typename Func, typename ... Args>
898
899} // namespace flecs
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:351
#define ecs_abort(error_code,...)
Abort.
Definition log.h:342
ecs_id_t ecs_entity_t
An entity identifier.
Definition flecs.h:318
struct ecs_world_t ecs_world_t
A world is the container for all ECS data and supporting features.
Definition flecs.h:362
struct ecs_record_t ecs_record_t
Information about an entity, like its table and row.
Definition flecs.h:471
struct ecs_table_t ecs_table_t
A table stores entities and components for a specific type.
Definition flecs.h:365
flecs::id id(E value) const
Convert enum constant to entity.
void(* ecs_ctx_free_t)(void *ctx)
Function to cleanup context data.
Definition flecs.h:626
void ecs_read_end(const ecs_record_t *record)
End read access to entity.
void * ecs_ensure_id(ecs_world_t *world, ecs_entity_t entity, ecs_id_t id)
Get a mutable pointer to a component.
void ecs_write_end(ecs_record_t *record)
End exclusive write access to entity.
ecs_record_t * ecs_write_begin(ecs_world_t *world, ecs_entity_t entity)
Begin exclusive write access to entity.
void ecs_modified_id(ecs_world_t *world, ecs_entity_t entity, ecs_id_t id)
Signal that a component has been modified.
const ecs_record_t * ecs_read_begin(ecs_world_t *world, ecs_entity_t entity)
Begin read access to entity.
ecs_entity_t ecs_field_src(const ecs_iter_t *it, int32_t index)
Return field source.
ecs_table_t * ecs_table_add_id(ecs_world_t *world, ecs_table_t *table, ecs_id_t id)
Get table that has all components of current table plus the specified id.
int32_t ecs_table_column_count(const ecs_table_t *table)
Return number of columns in table.
bool ecs_commit(ecs_world_t *world, ecs_entity_t entity, ecs_record_t *record, ecs_table_t *table, const ecs_type_t *added, const ecs_type_t *removed)
Commit (move) entity to a table.
int32_t ecs_table_get_column_index(const ecs_world_t *world, const ecs_table_t *table, ecs_id_t id)
Get column index for id.
void * ecs_record_get_column(const ecs_record_t *r, int32_t column, size_t c_size)
Get component pointer from column/record.
ecs_record_t * ecs_record_find(const ecs_world_t *world, ecs_entity_t entity)
Find record for entity.
const ecs_world_t * ecs_get_world(const ecs_poly_t *poly)
Get world from poly.
A type is a list of (component) ids.
Definition flecs.h:335
Entity.
Definition entity.hpp:30
Class that wraps around a flecs::id_t.
Definition decl.hpp:27
Class for iterating over query results.
Definition iter.hpp:68
void * param()
Access param.
Definition iter.hpp:139
flecs::field< const flecs::entity_t > entities() const
Get readonly access to entity ids.
Definition iter.hpp:299
Class that describes a term.
Definition impl.hpp:16
The world.
Definition world.hpp:132
bool is_stage() const
Test if is a stage.
Definition world.hpp:339
bool is_deferred() const
Test whether deferring is enabled.
Definition world.hpp:294