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 if (terms.populate(iter)) {
203 invoke_callback< each_ref_column >(iter, m_func, 0, terms.m_terms);
204 } else {
205 invoke_callback< each_column >(iter, m_func, 0, terms.m_terms);
206 }
207 }
208
209 // Static function that can be used as callback for systems/triggers
210 static void run(ecs_iter_t *iter) {
211 auto self = static_cast<const each_delegate*>(iter->binding_ctx);
212 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
213 self->invoke(iter);
214 }
215
216 // Create instance of delegate
217 static each_delegate* make(const Func& func) {
218 return FLECS_NEW(each_delegate)(func);
219 }
220
221 // Function that can be used as callback to free delegate
222 static void destruct(void *obj) {
223 _::free_obj<each_delegate>(static_cast<each_delegate*>(obj));
224 }
225
226 // Static function to call for component on_add hook
227 static void run_add(ecs_iter_t *iter) {
228 component_binding_ctx *ctx = reinterpret_cast<component_binding_ctx*>(
229 iter->binding_ctx);
230 iter->binding_ctx = ctx->on_add;
231 run(iter);
232 }
233
234 // Static function to call for component on_remove hook
235 static void run_remove(ecs_iter_t *iter) {
236 component_binding_ctx *ctx = reinterpret_cast<component_binding_ctx*>(
237 iter->binding_ctx);
238 iter->binding_ctx = ctx->on_remove;
239 run(iter);
240 }
241
242 // Static function to call for component on_set hook
243 static void run_set(ecs_iter_t *iter) {
244 component_binding_ctx *ctx = reinterpret_cast<component_binding_ctx*>(
245 iter->binding_ctx);
246 iter->binding_ctx = ctx->on_set;
247 run(iter);
248 }
249
250 // Each delegates always use instanced iterators
251 static bool instanced() {
252 return true;
253 }
254
255private:
256 // Number of function arguments is one more than number of components, pass
257 // entity as argument.
258 template <template<typename X, typename = int> class ColumnType,
259 typename... Args, if_t<
260 sizeof...(Components) == sizeof...(Args) && PassEntity> = 0>
261 static void invoke_callback(
262 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
263 {
264 ECS_TABLE_LOCK(iter->world, iter->table);
265
266 ecs_world_t *world = iter->world;
267 size_t count = static_cast<size_t>(iter->count);
268
269 ecs_assert(count > 0, ECS_INVALID_OPERATION,
270 "no entities returned, use each() without flecs::entity argument");
271
272 for (size_t i = 0; i < count; i ++) {
273 func(flecs::entity(world, iter->entities[i]),
274 (ColumnType< remove_reference_t<Components> >(comps, i)
275 .get_row())...);
276 }
277
278 ECS_TABLE_UNLOCK(iter->world, iter->table);
279 }
280
281 // Number of function arguments is two more than number of components, pass
282 // iter + index as argument.
283 template <template<typename X, typename = int> class ColumnType,
284 typename... Args, int Enabled = PassIter, if_t<
285 sizeof...(Components) == sizeof...(Args) && Enabled> = 0>
286 static void invoke_callback(
287 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
288 {
289 size_t count = static_cast<size_t>(iter->count);
290 if (count == 0) {
291 // If query has no This terms, count can be 0. Since each does not
292 // have an entity parameter, just pass through components
293 count = 1;
294 }
295
296 flecs::iter it(iter);
297
298 ECS_TABLE_LOCK(iter->world, iter->table);
299
300 for (size_t i = 0; i < count; i ++) {
301 func(it, i, (ColumnType< remove_reference_t<Components> >(comps, i)
302 .get_row())...);
303 }
304
305 ECS_TABLE_UNLOCK(iter->world, iter->table);
306 }
307
308 // Number of function arguments is equal to number of components, no entity
309 template <template<typename X, typename = int> class ColumnType,
310 typename... Args, if_t<
311 sizeof...(Components) == sizeof...(Args) && !PassEntity && !PassIter> = 0>
312 static void invoke_callback(
313 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
314 {
315 size_t count = static_cast<size_t>(iter->count);
316 if (count == 0) {
317 // If query has no This terms, count can be 0. Since each does not
318 // have an entity parameter, just pass through components
319 count = 1;
320 }
321
322 flecs::iter it(iter);
323
324 ECS_TABLE_LOCK(iter->world, iter->table);
325
326 for (size_t i = 0; i < count; i ++) {
327 func( (ColumnType< remove_reference_t<Components> >(comps, i)
328 .get_row())...);
329 }
330
331 ECS_TABLE_UNLOCK(iter->world, iter->table);
332 }
333
334 template <template<typename X, typename = int> class ColumnType,
335 typename... Args, if_t< sizeof...(Components) != sizeof...(Args) > = 0>
336 static void invoke_callback(ecs_iter_t *iter, const Func& func,
337 size_t index, Terms& columns, Args... comps)
338 {
339 invoke_callback<ColumnType>(
340 iter, func, index + 1, columns, comps..., columns[index]);
341 }
342
343 Func m_func;
344};
345
346template <typename Func, typename ... Components>
347struct find_delegate : public delegate {
348 // If the number of arguments in the function signature is one more than the
349 // number of components in the query, an extra entity arg is required.
350 static constexpr bool PassEntity =
351 (sizeof...(Components) + 1) == (arity<Func>::value);
352
353 // If the number of arguments in the function is two more than the number of
354 // components in the query, extra iter + index arguments are required.
355 static constexpr bool PassIter =
356 (sizeof...(Components) + 2) == (arity<Func>::value);
357
358 static_assert(arity<Func>::value > 0,
359 "each() must have at least one argument");
360
361 using Terms = typename term_ptrs<Components ...>::array;
362
363 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
364 explicit find_delegate(Func&& func) noexcept
365 : m_func(FLECS_MOV(func)) { }
366
367 explicit find_delegate(const Func& func) noexcept
368 : m_func(func) { }
369
370 // Invoke object directly. This operation is useful when the calling
371 // function has just constructed the delegate, such as what happens when
372 // iterating a query.
373 flecs::entity invoke(ecs_iter_t *iter) const {
374 term_ptrs<Components...> terms;
375
376 if (terms.populate(iter)) {
377 return invoke_callback< each_ref_column >(iter, m_func, 0, terms.m_terms);
378 } else {
379 return invoke_callback< each_column >(iter, m_func, 0, terms.m_terms);
380 }
381 }
382
383 // Find delegates always use instanced iterators
384 static bool instanced() {
385 return true;
386 }
387
388private:
389 // Number of function arguments is one more than number of components, pass
390 // entity as argument.
391 template <template<typename X, typename = int> class ColumnType,
392 typename... Args, if_t<
393 sizeof...(Components) == sizeof...(Args) && PassEntity> = 0>
394 static flecs::entity invoke_callback(
395 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
396 {
397 ECS_TABLE_LOCK(iter->world, iter->table);
398
399 ecs_world_t *world = iter->world;
400 size_t count = static_cast<size_t>(iter->count);
401 flecs::entity result;
402
403 ecs_assert(count > 0, ECS_INVALID_OPERATION,
404 "no entities returned, use find() without flecs::entity argument");
405
406 for (size_t i = 0; i < count; i ++) {
407 if (func(flecs::entity(world, iter->entities[i]),
408 (ColumnType< remove_reference_t<Components> >(comps, i)
409 .get_row())...))
410 {
411 result = flecs::entity(world, iter->entities[i]);
412 break;
413 }
414 }
415
416 ECS_TABLE_UNLOCK(iter->world, iter->table);
417
418 return result;
419 }
420
421 // Number of function arguments is two more than number of components, pass
422 // iter + index as argument.
423 template <template<typename X, typename = int> class ColumnType,
424 typename... Args, int Enabled = PassIter, if_t<
425 sizeof...(Components) == sizeof...(Args) && Enabled> = 0>
426 static flecs::entity invoke_callback(
427 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
428 {
429 size_t count = static_cast<size_t>(iter->count);
430 if (count == 0) {
431 // If query has no This terms, count can be 0. Since each does not
432 // have an entity parameter, just pass through components
433 count = 1;
434 }
435
436 flecs::iter it(iter);
437 flecs::entity result;
438
439 ECS_TABLE_LOCK(iter->world, iter->table);
440
441 for (size_t i = 0; i < count; i ++) {
442 if (func(it, i, (ColumnType< remove_reference_t<Components> >(comps, i)
443 .get_row())...))
444 {
445 result = flecs::entity(iter->world, iter->entities[i]);
446 break;
447 }
448 }
449
450 ECS_TABLE_UNLOCK(iter->world, iter->table);
451
452 return result;
453 }
454
455 // Number of function arguments is equal to number of components, no entity
456 template <template<typename X, typename = int> class ColumnType,
457 typename... Args, if_t<
458 sizeof...(Components) == sizeof...(Args) && !PassEntity && !PassIter> = 0>
459 static flecs::entity invoke_callback(
460 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
461 {
462 size_t count = static_cast<size_t>(iter->count);
463 if (count == 0) {
464 // If query has no This terms, count can be 0. Since each does not
465 // have an entity parameter, just pass through components
466 count = 1;
467 }
468
469 flecs::iter it(iter);
470 flecs::entity result;
471
472 ECS_TABLE_LOCK(iter->world, iter->table);
473
474 for (size_t i = 0; i < count; i ++) {
475 if (func( (ColumnType< remove_reference_t<Components> >(comps, i)
476 .get_row())...))
477 {
478 result = flecs::entity(iter->world, iter->entities[i]);
479 break;
480 }
481 }
482
483 ECS_TABLE_UNLOCK(iter->world, iter->table);
484
485 return result;
486 }
487
488 template <template<typename X, typename = int> class ColumnType,
489 typename... Args, if_t< sizeof...(Components) != sizeof...(Args) > = 0>
490 static flecs::entity invoke_callback(ecs_iter_t *iter, const Func& func,
491 size_t index, Terms& columns, Args... comps)
492 {
493 return invoke_callback<ColumnType>(
494 iter, func, index + 1, columns, comps..., columns[index]);
495 }
496
497 Func m_func;
498};
499
503
504template <typename Func, typename ... Components>
506private:
507 static constexpr bool IterOnly = arity<Func>::value == 1;
508
509 using Terms = typename term_ptrs<Components ...>::array;
510
511public:
512 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
513 explicit iter_delegate(Func&& func) noexcept
514 : m_func(FLECS_MOV(func)) { }
515
516 explicit iter_delegate(const Func& func) noexcept
517 : m_func(func) { }
518
519 // Invoke object directly. This operation is useful when the calling
520 // function has just constructed the delegate, such as what happens when
521 // iterating a query.
522 void invoke(ecs_iter_t *iter) const {
523 term_ptrs<Components...> terms;
524 terms.populate(iter);
525 invoke_callback(iter, m_func, 0, terms.m_terms);
526 }
527
528 // Static function that can be used as callback for systems/triggers
529 static void run(ecs_iter_t *iter) {
530 auto self = static_cast<const iter_delegate*>(iter->binding_ctx);
531 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
532 self->invoke(iter);
533 }
534
535 // Instancing needs to be enabled explicitly for iter delegates
536 static bool instanced() {
537 return false;
538 }
539
540private:
541 template <typename... Args, if_t<!sizeof...(Args) && IterOnly> = 0>
542 static void invoke_callback(ecs_iter_t *iter, const Func& func,
543 size_t, Terms&, Args...)
544 {
545 flecs::iter it(iter);
546
547 ECS_TABLE_LOCK(iter->world, iter->table);
548
549 func(it);
550
551 ECS_TABLE_UNLOCK(iter->world, iter->table);
552 }
553
554 template <typename... Targs, if_t<!IterOnly &&
555 (sizeof...(Targs) == sizeof...(Components))> = 0>
556 static void invoke_callback(ecs_iter_t *iter, const Func& func, size_t,
557 Terms&, Targs... comps)
558 {
559 flecs::iter it(iter);
560
561 ECS_TABLE_LOCK(iter->world, iter->table);
562
563 func(it, ( static_cast<
564 remove_reference_t<
565 remove_pointer_t<
566 actual_type_t<Components> > >* >
567 (comps.ptr))...);
568
569 ECS_TABLE_UNLOCK(iter->world, iter->table);
570 }
571
572 template <typename... Targs, if_t<!IterOnly &&
573 (sizeof...(Targs) != sizeof...(Components)) > = 0>
574 static void invoke_callback(ecs_iter_t *iter, const Func& func,
575 size_t index, Terms& columns, Targs... comps)
576 {
577 invoke_callback(iter, func, index + 1, columns, comps...,
578 columns[index]);
579 }
580
581 Func m_func;
582};
583
584
588
589template <typename Func>
591 explicit entity_observer_delegate(Func&& func) noexcept
592 : m_func(FLECS_MOV(func)) { }
593
594 // Static function that can be used as callback for systems/triggers
595 static void run(ecs_iter_t *iter) {
596 invoke<Func>(iter);
597 }
598private:
599 template <typename F, if_t<arity<F>::value == 1> = 0>
600 static void invoke(ecs_iter_t *iter) {
601 auto self = static_cast<const entity_observer_delegate*>(iter->binding_ctx);
602 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
603 self->m_func(flecs::entity(iter->world, ecs_field_src(iter, 1)));
604 }
605
606 template <typename F, if_t<arity<F>::value == 0> = 0>
607 static void invoke(ecs_iter_t *iter) {
608 auto self = static_cast<const entity_observer_delegate*>(iter->binding_ctx);
609 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
610 self->m_func();
611 }
612
613 Func m_func;
614};
615
616template <typename Func, typename Event>
618 explicit entity_payload_observer_delegate(Func&& func) noexcept
619 : m_func(FLECS_MOV(func)) { }
620
621 // Static function that can be used as callback for systems/triggers
622 static void run(ecs_iter_t *iter) {
623 invoke<Func>(iter);
624 }
625
626private:
627 template <typename F, if_t<arity<F>::value == 1> = 0>
628 static void invoke(ecs_iter_t *iter) {
629 auto self = static_cast<const entity_payload_observer_delegate*>(
630 iter->binding_ctx);
631 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
632 ecs_assert(iter->param != nullptr, ECS_INVALID_OPERATION,
633 "entity observer invoked without payload");
634
635 Event *data = static_cast<Event*>(iter->param);
636 self->m_func(*data);
637 }
638
639 template <typename F, if_t<arity<F>::value == 2> = 0>
640 static void invoke(ecs_iter_t *iter) {
641 auto self = static_cast<const entity_payload_observer_delegate*>(
642 iter->binding_ctx);
643 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
644 ecs_assert(iter->param != nullptr, ECS_INVALID_OPERATION,
645 "entity observer invoked without payload");
646
647 Event *data = static_cast<Event*>(iter->param);
648 self->m_func(flecs::entity(iter->world, ecs_field_src(iter, 1)), *data);
649 }
650
651 Func m_func;
652};
653
654
658
659template<typename ... Args>
661
662template<typename ... Args>
664 using ColumnArray = flecs::array<int32_t, sizeof...(Args)>;
665 using ArrayType = flecs::array<void*, sizeof...(Args)>;
666 using DummyArray = flecs::array<int, sizeof...(Args)>;
667 using IdArray = flecs::array<id_t, sizeof...(Args)>;
668
669 static bool const_args() {
670 static flecs::array<bool, sizeof...(Args)> is_const_args ({
671 flecs::is_const<flecs::remove_reference_t<Args>>::value...
672 });
673
674 for (auto is_const : is_const_args) {
675 if (!is_const) {
676 return false;
677 }
678 }
679 return true;
680 }
681
682 static
683 bool get_ptrs(world_t *world, const ecs_record_t *r, ecs_table_t *table,
684 ArrayType& ptrs)
685 {
686 ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL);
688 return false;
689 }
690
691 /* table_index_of needs real world */
692 const flecs::world_t *real_world = ecs_get_world(world);
693
694 /* Get column indices for components */
695 ColumnArray columns ({
697 _::cpp_type<Args>().id(world))...
698 });
699
700 /* Get pointers for columns for entity */
701 size_t i = 0;
702 for (int32_t column : columns) {
703 if (column == -1) {
704 return false;
705 }
706
707 ptrs[i ++] = ecs_record_get_column(r, column, 0);
708 }
709
710 return true;
711 }
712
713 static bool get_mut_ptrs(world_t *world, ecs_entity_t e, ArrayType& ptrs) {
714 /* Get pointers w/get_mut */
715 size_t i = 0;
716 DummyArray dummy ({
717 (ptrs[i ++] = ecs_get_mut_id(world, e,
718 _::cpp_type<Args>().id(world)), 0)...
719 });
720
721 return true;
722 }
723
724 template <typename Func>
725 static bool invoke_read(world_t *world, entity_t e, const Func& func) {
726 const ecs_record_t *r = ecs_read_begin(world, e);
727 if (!r) {
728 return false;
729 }
730
731 ecs_table_t *table = r->table;
732 if (!table) {
733 return false;
734 }
735
736 ArrayType ptrs;
737 bool has_components = get_ptrs(world, r, table, ptrs);
738 if (has_components) {
739 invoke_callback(func, 0, ptrs);
740 }
741
742 ecs_read_end(r);
743
744 return has_components;
745 }
746
747 template <typename Func>
748 static bool invoke_write(world_t *world, entity_t e, const Func& func) {
750 if (!r) {
751 return false;
752 }
753
754 ecs_table_t *table = r->table;
755 if (!table) {
756 return false;
757 }
758
759 ArrayType ptrs;
760 bool has_components = get_ptrs(world, r, table, ptrs);
761 if (has_components) {
762 invoke_callback(func, 0, ptrs);
763 }
764
765 ecs_write_end(r);
766
767 return has_components;
768 }
769
770 template <typename Func>
771 static bool invoke_get(world_t *world, entity_t e, const Func& func) {
772 if (const_args()) {
773 return invoke_read(world, e, func);
774 } else {
775 return invoke_write(world, e, func);
776 }
777 }
778
779 // Utility for storing id in array in pack expansion
780 static size_t store_added(IdArray& added, size_t elem, ecs_table_t *prev,
781 ecs_table_t *next, id_t id)
782 {
783 // Array should only contain ids for components that are actually added,
784 // so check if the prev and next tables are different.
785 if (prev != next) {
786 added[elem] = id;
787 elem ++;
788 }
789 return elem;
790 }
791
792 template <typename Func>
793 static bool invoke_get_mut(world_t *world, entity_t id, const Func& func) {
795
796 ArrayType ptrs;
797 ecs_table_t *table = NULL;
798
799 // When not deferred take the fast path.
800 if (!w.is_deferred()) {
801 // Bit of low level code so we only do at most one table move & one
802 // entity lookup for the entire operation.
803
804 // Make sure the object is not a stage. Operations on a stage are
805 // only allowed when the stage is in deferred mode, which is when
806 // the world is in readonly mode.
807 ecs_assert(!w.is_stage(), ECS_INVALID_PARAMETER, NULL);
808
809 // Find table for entity
811 if (r) {
812 table = r->table;
813 }
814
815 // Find destination table that has all components
816 ecs_table_t *prev = table, *next;
817 size_t elem = 0;
818 IdArray added;
819
820 // Iterate components, only store added component ids in added array
821 DummyArray dummy_before ({ (
822 next = ecs_table_add_id(world, prev, w.id<Args>()),
823 elem = store_added(added, elem, prev, next, w.id<Args>()),
824 prev = next, 0
825 )... });
826 (void)dummy_before;
827
828 // If table is different, move entity straight to it
829 if (table != next) {
830 ecs_type_t ids;
831 ids.array = added.ptr();
832 ids.count = static_cast<ecs_size_t>(elem);
833 ecs_commit(world, id, r, next, &ids, NULL);
834 table = next;
835 }
836
837 if (!get_ptrs(w, r, table, ptrs)) {
838 ecs_abort(ECS_INTERNAL_ERROR, NULL);
839 }
840
841 ECS_TABLE_LOCK(world, table);
842
843 // When deferred, obtain pointers with regular get_mut
844 } else {
845 get_mut_ptrs(world, id, ptrs);
846 }
847
848 invoke_callback(func, 0, ptrs);
849
850 if (!w.is_deferred()) {
851 ECS_TABLE_UNLOCK(world, table);
852 }
853
854 // Call modified on each component
855 DummyArray dummy_after ({
856 ( ecs_modified_id(world, id, w.id<Args>()), 0)...
857 });
858 (void)dummy_after;
859
860 return true;
861 }
862
863private:
864 template <typename Func, typename ... TArgs,
865 if_t<sizeof...(TArgs) == sizeof...(Args)> = 0>
866 static void invoke_callback(
867 const Func& f, size_t, ArrayType&, TArgs&& ... comps)
868 {
869 f(*static_cast<typename base_arg_type<Args>::type*>(comps)...);
870 }
871
872 template <typename Func, typename ... TArgs,
873 if_t<sizeof...(TArgs) != sizeof...(Args)> = 0>
874 static void invoke_callback(const Func& f, size_t arg, ArrayType& ptrs,
875 TArgs&& ... comps)
876 {
877 invoke_callback(f, arg + 1, ptrs, comps..., ptrs[arg]);
878 }
879};
880
881template <typename Func, typename U = int>
883 static_assert(function_traits<Func>::value, "type is not callable");
884};
885
886template <typename Func>
887struct entity_with_delegate<Func, if_t< is_callable<Func>::value > >
888 : entity_with_delegate_impl< arg_list_t<Func> >
889{
890 static_assert(function_traits<Func>::arity > 0,
891 "function must have at least one argument");
892};
893
894} // namespace _
895
896// Experimental: allows using the each delegate for use cases outside of flecs
897template <typename Func, typename ... Args>
899
900} // 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:305
struct ecs_world_t ecs_world_t
A world is the container for all ECS data and supporting features.
Definition flecs.h:349
struct ecs_record_t ecs_record_t
Information about an entity, like its table and row.
Definition flecs.h:458
struct ecs_table_t ecs_table_t
A table stores entities and components for a specific type.
Definition flecs.h:352
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:613
void ecs_read_end(const ecs_record_t *record)
End read access to entity.
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.
void * ecs_get_mut_id(ecs_world_t *world, ecs_entity_t entity, ecs_id_t id)
Get a mutable pointer to a component.
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:322
Wrapper class around a column.
Definition iter.hpp:58
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:169
flecs::column< const flecs::entity_t > entities() const
Get readonly access to entity ids.
Definition iter.hpp:376
void * param()
Access param.
Definition iter.hpp:240
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:354
bool is_deferred() const
Test whether deferring is enabled.
Definition world.hpp:309