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