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 field_ptr {
40 void *ptr = nullptr;
41 int8_t index = 0;
42 bool is_ref = false;
43 bool is_row = false;
44};
45
46template <typename ... Components>
47struct field_ptrs {
48 using array = flecs::array<_::field_ptr, sizeof...(Components)>;
49
50 void populate(const ecs_iter_t *iter) {
51 populate(iter, 0, static_cast<
52 remove_reference_t<
53 remove_pointer_t<Components>>
54 *>(nullptr)...);
55 }
56
57 void populate_self(const ecs_iter_t *iter) {
58 populate_self(iter, 0, static_cast<
59 remove_reference_t<
60 remove_pointer_t<Components>>
61 *>(nullptr)...);
62 }
63
64 array fields_;
65
66private:
67 void populate(const ecs_iter_t*, size_t) { }
68
69 template <typename T, typename... Targs,
70 typename A = remove_pointer_t<actual_type_t<T>>,
71 if_not_t< is_empty<A>::value > = 0>
72 void populate(const ecs_iter_t *iter, size_t index, T, Targs... comps) {
73 if (iter->row_fields & (1llu << index)) {
74 /* Need to fetch the value with ecs_field_at() */
75 fields_[index].is_row = true;
76 fields_[index].is_ref = true;
77 fields_[index].index = static_cast<int8_t>(index);
78 } else {
79 fields_[index].ptr = ecs_field_w_size(iter, sizeof(A),
80 static_cast<int8_t>(index));
81 fields_[index].is_ref = iter->sources[index] != 0;
82 }
83
84 populate(iter, index + 1, comps ...);
85 }
86
87 template <typename T, typename... Targs,
88 typename A = remove_pointer_t<actual_type_t<T>>,
89 if_t< is_empty<A>::value > = 0>
90 void populate(const ecs_iter_t *iter, size_t index, T, Targs... comps) {
91 populate(iter, index + 1, comps ...);
92 }
93
94 void populate_self(const ecs_iter_t*, size_t) { }
95
96 template <typename T, typename... Targs,
97 typename A = remove_pointer_t<actual_type_t<T>>,
98 if_not_t< is_empty<A>::value > = 0>
99 void populate_self(const ecs_iter_t *iter, size_t index, T, Targs... comps) {
100 fields_[index].ptr = ecs_field_w_size(iter, sizeof(A),
101 static_cast<int8_t>(index));
102 fields_[index].is_ref = false;
103 ecs_assert(iter->sources[index] == 0, ECS_INTERNAL_ERROR, NULL);
104 populate_self(iter, index + 1, comps ...);
105 }
106
107 template <typename T, typename... Targs,
108 typename A = remove_pointer_t<actual_type_t<T>>,
109 if_t< is_empty<A>::value > = 0>
110 void populate_self(const ecs_iter_t *iter, size_t index, T, Targs... comps) {
111 populate(iter, index + 1, comps ...);
112 }
113};
114
115struct delegate { };
116
117// Template that figures out from the template parameters of a query/system
118// how to pass the value to the each callback
119template <typename T, typename = int>
120struct each_field { };
121
122// Base class
124 each_column_base(const _::field_ptr& field, size_t row)
125 : field_(field), row_(row) {
126 }
127
128protected:
129 const _::field_ptr& field_;
130 size_t row_;
131};
132
133// If type is not a pointer, return a reference to the type (default case)
134template <typename T>
135struct each_field<T, if_t< !is_pointer<T>::value &&
136 !is_empty<actual_type_t<T>>::value && is_actual<T>::value > >
138{
139 each_field(const flecs::iter_t*, _::field_ptr& field, size_t row)
140 : each_column_base(field, row) { }
141
142 T& get_row() {
143 return static_cast<T*>(this->field_.ptr)[this->row_];
144 }
145};
146
147// If argument type is not the same as actual component type, return by value.
148// This requires that the actual type can be converted to the type.
149// A typical scenario where this happens is when using flecs::pair types.
150template <typename T>
151struct each_field<T, if_t< !is_pointer<T>::value &&
152 !is_empty<actual_type_t<T>>::value && !is_actual<T>::value> >
154{
155 each_field(const flecs::iter_t*, _::field_ptr& field, size_t row)
156 : each_column_base(field, row) { }
157
158 T get_row() {
159 return static_cast<actual_type_t<T>*>(this->field_.ptr)[this->row_];
160 }
161};
162
163// If type is empty (indicating a tag) the query will pass a nullptr. To avoid
164// returning nullptr to reference arguments, return a temporary value.
165template <typename T>
166struct each_field<T, if_t< is_empty<actual_type_t<T>>::value &&
167 !is_pointer<T>::value > >
169{
170 each_field(const flecs::iter_t*, _::field_ptr& field, size_t row)
171 : each_column_base(field, row) { }
172
173 T get_row() {
174 return actual_type_t<T>();
175 }
176};
177
178// If type is a pointer (indicating an optional value) don't index with row if
179// the field is not set.
180template <typename T>
181struct each_field<T, if_t< is_pointer<T>::value &&
182 !is_empty<actual_type_t<T>>::value > >
184{
185 each_field(const flecs::iter_t*, _::field_ptr& field, size_t row)
186 : each_column_base(field, row) { }
187
188 actual_type_t<T> get_row() {
189 if (this->field_.ptr) {
190 return &static_cast<actual_type_t<T>>(this->field_.ptr)[this->row_];
191 } else {
192 // optional argument doesn't have a value
193 return nullptr;
194 }
195 }
196};
197
198// If the query contains component references to other entities, check if the
199// current argument is one.
200template <typename T, typename = int>
201struct each_ref_field : public each_field<T> {
203 : each_field<T>(iter, field, row) {
204
205 if (field.is_ref) {
206 // If this is a reference, set the row to 0 as a ref always is a
207 // single value, not an array. This prevents the application from
208 // having to do an if-check on whether the column is owned.
209 //
210 // This check only happens when the current table being iterated
211 // over caused the query to match a reference. The check is
212 // performed once per iterated table.
213 this->row_ = 0;
214 }
215
216 if (field.is_row) {
217 field.ptr = ecs_field_at_w_size(iter, sizeof(T), field.index,
218 static_cast<int8_t>(row));
219 }
220 }
221};
222
223// Type that handles passing components to each callbacks
224template <typename Func, typename ... Components>
225struct each_delegate : public delegate {
226 using Terms = typename field_ptrs<Components ...>::array;
227
228 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
229 explicit each_delegate(Func&& func) noexcept
230 : func_(FLECS_MOV(func)) { }
231
232 explicit each_delegate(const Func& func) noexcept
233 : func_(func) { }
234
235 // Invoke object directly. This operation is useful when the calling
236 // function has just constructed the delegate, such as what happens when
237 // iterating a query.
238 void invoke(ecs_iter_t *iter) const {
239 field_ptrs<Components...> terms;
240
241 iter->flags |= EcsIterCppEach;
242
243 if (iter->ref_fields | iter->up_fields) {
244 terms.populate(iter);
245 invoke_unpack< each_ref_field >(iter, func_, 0, terms.fields_);
246 } else {
247 terms.populate_self(iter);
248 invoke_unpack< each_field >(iter, func_, 0, terms.fields_);
249 }
250 }
251
252 // Static function that can be used as callback for systems/triggers
253 static void run(ecs_iter_t *iter) {
254 auto self = static_cast<const each_delegate*>(iter->callback_ctx);
255 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
256 self->invoke(iter);
257 }
258
259 // Create instance of delegate
260 static each_delegate* make(const Func& func) {
261 return FLECS_NEW(each_delegate)(func);
262 }
263
264 // Function that can be used as callback to free delegate
265 static void destruct(void *obj) {
266 _::free_obj<each_delegate>(static_cast<each_delegate*>(obj));
267 }
268
269 // Static function to call for component on_add hook
270 static void run_add(ecs_iter_t *iter) {
271 component_binding_ctx *ctx = reinterpret_cast<component_binding_ctx*>(
272 iter->callback_ctx);
273 iter->callback_ctx = ctx->on_add;
274 run(iter);
275 }
276
277 // Static function to call for component on_remove hook
278 static void run_remove(ecs_iter_t *iter) {
279 component_binding_ctx *ctx = reinterpret_cast<component_binding_ctx*>(
280 iter->callback_ctx);
281 iter->callback_ctx = ctx->on_remove;
282 run(iter);
283 }
284
285 // Static function to call for component on_set hook
286 static void run_set(ecs_iter_t *iter) {
287 component_binding_ctx *ctx = reinterpret_cast<component_binding_ctx*>(
288 iter->callback_ctx);
289 iter->callback_ctx = ctx->on_set;
290 run(iter);
291 }
292
293private:
294 // func(flecs::entity, Components...)
295 template <template<typename X, typename = int> class ColumnType,
296 typename... Args,
297 typename Fn = Func,
298 decltype(std::declval<const Fn&>()(
299 std::declval<flecs::entity>(),
300 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...), 0) = 0>
301 static void invoke_callback(
302 ecs_iter_t *iter, const Func& func, size_t i, Args... comps)
303 {
304 ecs_assert(iter->count > 0, ECS_INVALID_OPERATION,
305 "no entities returned, use each() without flecs::entity argument");
306
307 func(flecs::entity(iter->world, iter->entities[i]),
308 (ColumnType< remove_reference_t<Components> >(iter, comps, i)
309 .get_row())...);
310 }
311
312 // func(flecs::iter&, size_t row, Components...)
313 template <template<typename X, typename = int> class ColumnType,
314 typename... Args,
315 typename Fn = Func,
316 decltype(std::declval<const Fn&>()(
317 std::declval<flecs::iter&>(),
318 std::declval<size_t&>(),
319 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...), 0) = 0>
320 static void invoke_callback(
321 ecs_iter_t *iter, const Func& func, size_t i, Args... comps)
322 {
323 flecs::iter it(iter);
324 func(it, i, (ColumnType< remove_reference_t<Components> >(iter, comps, i)
325 .get_row())...);
326 }
327
328 // func(Components...)
329 template <template<typename X, typename = int> class ColumnType,
330 typename... Args,
331 typename Fn = Func,
332 decltype(std::declval<const Fn&>()(
333 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...), 0) = 0>
334 static void invoke_callback(
335 ecs_iter_t *iter, const Func& func, size_t i, Args... comps)
336 {
337 func((ColumnType< remove_reference_t<Components> >(iter, comps, i)
338 .get_row())...);
339 }
340
341 template <template<typename X, typename = int> class ColumnType,
342 typename... Args, if_t<
343 sizeof...(Components) == sizeof...(Args)> = 0>
344 static void invoke_unpack(
345 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
346 {
347 ECS_TABLE_LOCK(iter->world, iter->table);
348
349 size_t count = static_cast<size_t>(iter->count);
350 if (count == 0 && !iter->table) {
351 // If query has no This terms, count can be 0. Since each does not
352 // have an entity parameter, just pass through components
353 count = 1;
354 }
355
356 for (size_t i = 0; i < count; i ++) {
357 invoke_callback<ColumnType>(iter, func, i, comps...);
358 }
359
360 ECS_TABLE_UNLOCK(iter->world, iter->table);
361 }
362
363 template <template<typename X, typename = int> class ColumnType,
364 typename... Args, if_t< sizeof...(Components) != sizeof...(Args) > = 0>
365 static void invoke_unpack(ecs_iter_t *iter, const Func& func,
366 size_t index, Terms& columns, Args... comps)
367 {
368 invoke_unpack<ColumnType>(
369 iter, func, index + 1, columns, comps..., columns[index]);
370 }
371
372public:
373 Func func_;
374};
375
376template <typename Func, typename ... Components>
377struct find_delegate : public delegate {
378 using Terms = typename field_ptrs<Components ...>::array;
379
380 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
381 explicit find_delegate(Func&& func) noexcept
382 : func_(FLECS_MOV(func)) { }
383
384 explicit find_delegate(const Func& func) noexcept
385 : func_(func) { }
386
387 // Invoke object directly. This operation is useful when the calling
388 // function has just constructed the delegate, such as what happens when
389 // iterating a query.
390 flecs::entity invoke(ecs_iter_t *iter) const {
391 field_ptrs<Components...> terms;
392
393 iter->flags |= EcsIterCppEach;
394
395 if (iter->ref_fields | iter->up_fields) {
396 terms.populate(iter);
397 return invoke_callback< each_ref_field >(iter, func_, 0, terms.fields_);
398 } else {
399 terms.populate_self(iter);
400 return invoke_callback< each_field >(iter, func_, 0, terms.fields_);
401 }
402 }
403
404private:
405 // Number of function arguments is one more than number of components, pass
406 // entity as argument.
407 template <template<typename X, typename = int> class ColumnType,
408 typename... Args,
409 typename Fn = Func,
410 if_t<sizeof...(Components) == sizeof...(Args)> = 0,
411 decltype(bool(std::declval<const Fn&>()(
412 std::declval<flecs::entity>(),
413 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...))) = true>
414 static flecs::entity invoke_callback(
415 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
416 {
417 ECS_TABLE_LOCK(iter->world, iter->table);
418
419 ecs_world_t *world = iter->world;
420 size_t count = static_cast<size_t>(iter->count);
421 flecs::entity result;
422
423 ecs_assert(count > 0, ECS_INVALID_OPERATION,
424 "no entities returned, use find() without flecs::entity argument");
425
426 for (size_t i = 0; i < count; i ++) {
427 if (func(flecs::entity(world, iter->entities[i]),
428 (ColumnType< remove_reference_t<Components> >(iter, comps, i)
429 .get_row())...))
430 {
431 result = flecs::entity(world, iter->entities[i]);
432 break;
433 }
434 }
435
436 ECS_TABLE_UNLOCK(iter->world, iter->table);
437
438 return result;
439 }
440
441 // Number of function arguments is two more than number of components, pass
442 // iter + index as argument.
443 template <template<typename X, typename = int> class ColumnType,
444 typename... Args,
445 typename Fn = Func,
446 if_t<sizeof...(Components) == sizeof...(Args)> = 0,
447 decltype(bool(std::declval<const Fn&>()(
448 std::declval<flecs::iter&>(),
449 std::declval<size_t&>(),
450 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...))) = true>
451 static flecs::entity invoke_callback(
452 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
453 {
454 size_t count = static_cast<size_t>(iter->count);
455 if (count == 0) {
456 // If query has no This terms, count can be 0. Since each does not
457 // have an entity parameter, just pass through components
458 count = 1;
459 }
460
461 flecs::iter it(iter);
462 flecs::entity result;
463
464 ECS_TABLE_LOCK(iter->world, iter->table);
465
466 for (size_t i = 0; i < count; i ++) {
467 if (func(it, i,
468 (ColumnType< remove_reference_t<Components> >(iter, comps, i)
469 .get_row())...))
470 {
471 result = flecs::entity(iter->world, iter->entities[i]);
472 break;
473 }
474 }
475
476 ECS_TABLE_UNLOCK(iter->world, iter->table);
477
478 return result;
479 }
480
481 // Number of function arguments is equal to number of components, no entity
482 template <template<typename X, typename = int> class ColumnType,
483 typename... Args,
484 typename Fn = Func,
485 if_t<sizeof...(Components) == sizeof...(Args)> = 0,
486 decltype(bool(std::declval<const Fn&>()(
487 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...))) = true>
488 static flecs::entity invoke_callback(
489 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
490 {
491 size_t count = static_cast<size_t>(iter->count);
492 if (count == 0) {
493 // If query has no This terms, count can be 0. Since each does not
494 // have an entity parameter, just pass through components
495 count = 1;
496 }
497
498 flecs::iter it(iter);
499 flecs::entity result;
500
501 ECS_TABLE_LOCK(iter->world, iter->table);
502
503 for (size_t i = 0; i < count; i ++) {
504 if (func(
505 (ColumnType< remove_reference_t<Components> >(iter, comps, i)
506 .get_row())...))
507 {
508 result = flecs::entity(iter->world, iter->entities[i]);
509 break;
510 }
511 }
512
513 ECS_TABLE_UNLOCK(iter->world, iter->table);
514
515 return result;
516 }
517
518 template <template<typename X, typename = int> class ColumnType,
519 typename... Args, if_t< sizeof...(Components) != sizeof...(Args) > = 0>
520 static flecs::entity invoke_callback(ecs_iter_t *iter, const Func& func,
521 size_t index, Terms& columns, Args... comps)
522 {
523 return invoke_callback<ColumnType>(
524 iter, func, index + 1, columns, comps..., columns[index]);
525 }
526
527 Func func_;
528};
529
533
534template <typename Func>
536 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
537 explicit run_delegate(Func&& func) noexcept
538 : func_(FLECS_MOV(func)) { }
539
540 explicit run_delegate(const Func& func) noexcept
541 : func_(func) { }
542
543 // Invoke object directly. This operation is useful when the calling
544 // function has just constructed the delegate, such as what happens when
545 // iterating a query.
546 void invoke(ecs_iter_t *iter) const {
547 flecs::iter it(iter);
548 iter->flags &= ~EcsIterIsValid;
549 func_(it);
550 }
551
552 // Static function that can be used as callback for systems/triggers
553 static void run(ecs_iter_t *iter) {
554 auto self = static_cast<const run_delegate*>(iter->run_ctx);
555 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
556 self->invoke(iter);
557 }
558
559 Func func_;
560};
561
562
566
567template <typename Func>
569 explicit entity_observer_delegate(Func&& func) noexcept
570 : func_(FLECS_MOV(func)) { }
571
572 // Static function that can be used as callback for systems/triggers
573 static void run(ecs_iter_t *iter) {
574 invoke<Func>(iter);
575 }
576
577private:
578 template <typename F,
579 decltype(std::declval<const F&>()(std::declval<flecs::entity>()), 0) = 0>
580 static void invoke(ecs_iter_t *iter) {
581 auto self = static_cast<const entity_observer_delegate*>(iter->callback_ctx);
582 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
583 self->func_(flecs::entity(iter->world, ecs_field_src(iter, 0)));
584 }
585
586 template <typename F,
587 decltype(std::declval<const F&>()(), 0) = 0>
588 static void invoke(ecs_iter_t *iter) {
589 auto self = static_cast<const entity_observer_delegate*>(iter->callback_ctx);
590 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
591 self->func_();
592 }
593
594 Func func_;
595};
596
597template <typename Func, typename Event>
599 explicit entity_payload_observer_delegate(Func&& func) noexcept
600 : func_(FLECS_MOV(func)) { }
601
602 // Static function that can be used as callback for systems/triggers
603 static void run(ecs_iter_t *iter) {
604 invoke<Func>(iter);
605 }
606
607private:
608 template <typename F,
609 decltype(std::declval<const F&>()(
610 std::declval<Event&>()), 0) = 0>
611 static void invoke(ecs_iter_t *iter) {
612 auto self = static_cast<const entity_payload_observer_delegate*>(
613 iter->callback_ctx);
614 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
615 ecs_assert(iter->param != nullptr, ECS_INVALID_OPERATION,
616 "entity observer invoked without payload");
617
618 Event *data = static_cast<Event*>(iter->param);
619 self->func_(*data);
620 }
621
622 template <typename F,
623 decltype(std::declval<const F&>()(
624 std::declval<flecs::entity>(),
625 std::declval<Event&>()), 0) = 0>
626 static void invoke(ecs_iter_t *iter) {
627 auto self = static_cast<const entity_payload_observer_delegate*>(
628 iter->callback_ctx);
629 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
630 ecs_assert(iter->param != nullptr, ECS_INVALID_OPERATION,
631 "entity observer invoked without payload");
632
633 Event *data = static_cast<Event*>(iter->param);
634 self->func_(flecs::entity(iter->world, ecs_field_src(iter, 0)), *data);
635 }
636
637 Func func_;
638};
639
640
644
645template<typename ... Args>
647
648template<typename ... Args>
650 using ColumnArray = flecs::array<int32_t, sizeof...(Args)>;
651 using ArrayType = flecs::array<void*, sizeof...(Args)>;
652 using DummyArray = flecs::array<int, sizeof...(Args)>;
653 using IdArray = flecs::array<id_t, sizeof...(Args)>;
654
655 static bool const_args() {
656 static flecs::array<bool, sizeof...(Args)> is_const_args ({
657 flecs::is_const<flecs::remove_reference_t<Args>>::value...
658 });
659
660 for (auto is_const : is_const_args) {
661 if (!is_const) {
662 return false;
663 }
664 }
665 return true;
666 }
667
668 static
669 bool get_ptrs(world_t *world, flecs::entity_t e, const ecs_record_t *r, ecs_table_t *table,
670 ArrayType& ptrs)
671 {
672 ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL);
674 !ecs_table_has_flags(table, EcsTableHasSparse))
675 {
676 return false;
677 }
678
679 /* table_index_of needs real world */
680 const flecs::world_t *real_world = ecs_get_world(world);
681
682 IdArray ids ({
683 _::type<Args>().id(world)...
684 });
685
686 /* Get column indices for components */
687 ColumnArray columns ({
689 _::type<Args>().id(world))...
690 });
691
692 /* Get pointers for columns for entity */
693 size_t i = 0;
694 for (int32_t column : columns) {
695 if (column == -1) {
696 /* Component could be sparse */
697 void *ptr = ecs_get_mut_id(world, e, ids[i]);
698 if (!ptr) {
699 return false;
700 }
701
702 ptrs[i ++] = ptr;
703 continue;
704 }
705
706 ptrs[i ++] = ecs_record_get_by_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 _::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, e, 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, e, 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
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, id, 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 ensure
844 } else {
845 ensure_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:352
#define ecs_abort(error_code,...)
Abort.
Definition log.h:343
ecs_id_t ecs_entity_t
An entity identifier.
Definition flecs.h:347
struct ecs_world_t ecs_world_t
A world is the container for all ECS data and supporting features.
Definition flecs.h:391
struct ecs_table_t ecs_table_t
A table stores entities and components for a specific type.
Definition flecs.h:397
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:616
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, int8_t index)
Return field source.
void * ecs_field_at_w_size(const ecs_iter_t *it, size_t size, int8_t index, int32_t row)
Get data for field at specified row.
void * ecs_field_w_size(const ecs_iter_t *it, size_t size, int8_t index)
Get data for field.
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:1062
Record for entity index.
Definition flecs.h:494
ecs_table_t * table
Identifies a type (and table) in world.
Definition flecs.h:496
A type is a list of (component) ids.
Definition flecs.h:364
ecs_id_t * array
Array with ids.
Definition flecs.h:365
int32_t count
Number of elements in array.
Definition flecs.h:366
Entity.
Definition entity.hpp:30
Wrapper class around a field.
Definition field.hpp:61
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:142
flecs::field< const flecs::entity_t > entities() const
Get readonly access to entity ids.
Definition iter.hpp:314
The world.
Definition world.hpp:137
bool is_stage() const
Test if is a stage.
Definition world.hpp:421
bool is_deferred() const
Test whether deferring is enabled.
Definition world.hpp:370