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