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 populate_self(iter, index + 1, comps ...);
103 }
104
105 template <typename T, typename... Targs,
106 typename A = remove_pointer_t<actual_type_t<T>>,
107 if_t< is_empty<A>::value > = 0>
108 void populate_self(const ecs_iter_t *iter, size_t index, T, Targs... comps) {
109 populate(iter, index + 1, comps ...);
110 }
111};
112
113struct delegate { };
114
115// Template that figures out from the template parameters of a query/system
116// how to pass the value to the each callback
117template <typename T, typename = int>
118struct each_field { };
119
120// Base class
122 each_column_base(const _::field_ptr& field, size_t row)
123 : field_(field), row_(row) { }
124
125protected:
126 const _::field_ptr& field_;
127 size_t row_;
128};
129
130// If type is not a pointer, return a reference to the type (default case)
131template <typename T>
132struct each_field<T, if_t< !is_pointer<T>::value &&
133 !is_empty<actual_type_t<T>>::value && is_actual<T>::value > >
135{
136 each_field(const flecs::iter_t*, _::field_ptr& field, size_t row)
137 : each_column_base(field, row) { }
138
139 T& get_row() {
140 return static_cast<T*>(this->field_.ptr)[this->row_];
141 }
142};
143
144// If argument type is not the same as actual component type, return by value.
145// This requires that the actual type can be converted to the type.
146// A typical scenario where this happens is when using flecs::pair types.
147template <typename T>
148struct each_field<T, if_t< !is_pointer<T>::value &&
149 !is_empty<actual_type_t<T>>::value && !is_actual<T>::value> >
151{
152 each_field(const flecs::iter_t*, _::field_ptr& field, size_t row)
153 : each_column_base(field, row) { }
154
155 T get_row() {
156 return static_cast<actual_type_t<T>*>(this->field_.ptr)[this->row_];
157 }
158};
159
160// If type is empty (indicating a tag) the query will pass a nullptr. To avoid
161// returning nullptr to reference arguments, return a temporary value.
162template <typename T>
163struct each_field<T, if_t< is_empty<actual_type_t<T>>::value &&
164 !is_pointer<T>::value > >
166{
167 each_field(const flecs::iter_t*, _::field_ptr& field, size_t row)
168 : each_column_base(field, row) { }
169
170 T get_row() {
171 return actual_type_t<T>();
172 }
173};
174
175// If type is a pointer (indicating an optional value) don't index with row if
176// the field is not set.
177template <typename T>
178struct each_field<T, if_t< is_pointer<T>::value &&
179 !is_empty<actual_type_t<T>>::value > >
181{
182 each_field(const flecs::iter_t*, _::field_ptr& field, size_t row)
183 : each_column_base(field, row) { }
184
185 actual_type_t<T> get_row() {
186 if (this->field_.ptr) {
187 return &static_cast<actual_type_t<T>>(this->field_.ptr)[this->row_];
188 } else {
189 // optional argument doesn't have a value
190 return nullptr;
191 }
192 }
193};
194
195// If the query contains component references to other entities, check if the
196// current argument is one.
197template <typename T, typename = int>
198struct each_ref_field : public each_field<T> {
200 : each_field<T>(iter, field, row) {
201
202 if (field.is_ref) {
203 // If this is a reference, set the row to 0 as a ref always is a
204 // single value, not an array. This prevents the application from
205 // having to do an if-check on whether the column is owned.
206 //
207 // This check only happens when the current table being iterated
208 // over caused the query to match a reference. The check is
209 // performed once per iterated table.
210 this->row_ = 0;
211 }
212
213 if (field.is_row) {
214 field.ptr = ecs_field_at_w_size(iter, sizeof(T), field.index,
215 static_cast<int8_t>(row));
216 }
217 }
218};
219
220
221// Type that handles passing components to each callbacks
222template <typename Func, typename ... Components>
223struct each_delegate : public delegate {
224 using Terms = typename field_ptrs<Components ...>::array;
225
226 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
227 explicit each_delegate(Func&& func) noexcept
228 : func_(FLECS_MOV(func)) { }
229
230 explicit each_delegate(const Func& func) noexcept
231 : func_(func) { }
232
233 // Invoke object directly. This operation is useful when the calling
234 // function has just constructed the delegate, such as what happens when
235 // iterating a query.
236 void invoke(ecs_iter_t *iter) const {
237 field_ptrs<Components...> terms;
238
239 iter->flags |= EcsIterCppEach;
240
241 if (iter->ref_fields | iter->up_fields) {
242 terms.populate(iter);
243 invoke_unpack< each_ref_field >(iter, func_, 0, terms.fields_);
244 } else {
245 terms.populate_self(iter);
246 invoke_unpack< each_field >(iter, func_, 0, terms.fields_);
247 }
248 }
249
250 // Static function that can be used as callback for systems/triggers
251 static void run(ecs_iter_t *iter) {
252 auto self = static_cast<const each_delegate*>(iter->callback_ctx);
253 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
254 self->invoke(iter);
255 }
256
257 // Create instance of delegate
258 static each_delegate* make(const Func& func) {
259 return FLECS_NEW(each_delegate)(func);
260 }
261
262 // Function that can be used as callback to free delegate
263 static void destruct(void *obj) {
264 _::free_obj<each_delegate>(static_cast<each_delegate*>(obj));
265 }
266
267 // Static function to call for component on_add hook
268 static void run_add(ecs_iter_t *iter) {
269 component_binding_ctx *ctx = reinterpret_cast<component_binding_ctx*>(
270 iter->callback_ctx);
271 iter->callback_ctx = ctx->on_add;
272 run(iter);
273 }
274
275 // Static function to call for component on_remove hook
276 static void run_remove(ecs_iter_t *iter) {
277 component_binding_ctx *ctx = reinterpret_cast<component_binding_ctx*>(
278 iter->callback_ctx);
279 iter->callback_ctx = ctx->on_remove;
280 run(iter);
281 }
282
283 // Static function to call for component on_set hook
284 static void run_set(ecs_iter_t *iter) {
285 component_binding_ctx *ctx = reinterpret_cast<component_binding_ctx*>(
286 iter->callback_ctx);
287 iter->callback_ctx = ctx->on_set;
288 run(iter);
289 }
290
291private:
292 // func(flecs::entity, Components...)
293 template <template<typename X, typename = int> class ColumnType,
294 typename... Args,
295 typename Fn = Func,
296 decltype(std::declval<const Fn&>()(
297 std::declval<flecs::entity>(),
298 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...), 0) = 0>
299 static void invoke_callback(
300 ecs_iter_t *iter, const Func& func, size_t i, Args... comps)
301 {
302 ecs_assert(iter->count > 0, ECS_INVALID_OPERATION,
303 "no entities returned, use each() without flecs::entity argument");
304
305 func(flecs::entity(iter->world, iter->entities[i]),
306 (ColumnType< remove_reference_t<Components> >(iter, comps, i)
307 .get_row())...);
308 }
309
310 // func(flecs::iter&, size_t row, Components...)
311 template <template<typename X, typename = int> class ColumnType,
312 typename... Args,
313 typename Fn = Func,
314 decltype(std::declval<const Fn&>()(
315 std::declval<flecs::iter&>(),
316 std::declval<size_t&>(),
317 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...), 0) = 0>
318 static void invoke_callback(
319 ecs_iter_t *iter, const Func& func, size_t i, Args... comps)
320 {
321 flecs::iter it(iter);
322 func(it, i, (ColumnType< remove_reference_t<Components> >(iter, comps, i)
323 .get_row())...);
324 }
325
326 // func(Components...)
327 template <template<typename X, typename = int> class ColumnType,
328 typename... Args,
329 typename Fn = Func,
330 decltype(std::declval<const Fn&>()(
331 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...), 0) = 0>
332 static void invoke_callback(
333 ecs_iter_t *iter, const Func& func, size_t i, Args... comps)
334 {
335 func((ColumnType< remove_reference_t<Components> >(iter, comps, i)
336 .get_row())...);
337 }
338
339 template <template<typename X, typename = int> class ColumnType,
340 typename... Args, if_t<
341 sizeof...(Components) == sizeof...(Args)> = 0>
342 static void invoke_unpack(
343 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
344 {
345 ECS_TABLE_LOCK(iter->world, iter->table);
346
347 size_t count = static_cast<size_t>(iter->count);
348 if (count == 0 && !iter->table) {
349 // If query has no This terms, count can be 0. Since each does not
350 // have an entity parameter, just pass through components
351 count = 1;
352 }
353
354 for (size_t i = 0; i < count; i ++) {
355 invoke_callback<ColumnType>(iter, func, i, comps...);
356 }
357
358 ECS_TABLE_UNLOCK(iter->world, iter->table);
359 }
360
361 template <template<typename X, typename = int> class ColumnType,
362 typename... Args, if_t< sizeof...(Components) != sizeof...(Args) > = 0>
363 static void invoke_unpack(ecs_iter_t *iter, const Func& func,
364 size_t index, Terms& columns, Args... comps)
365 {
366 invoke_unpack<ColumnType>(
367 iter, func, index + 1, columns, comps..., columns[index]);
368 }
369
370public:
371 Func func_;
372};
373
374template <typename Func, typename ... Components>
375struct find_delegate : public delegate {
376 using Terms = typename field_ptrs<Components ...>::array;
377
378 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
379 explicit find_delegate(Func&& func) noexcept
380 : func_(FLECS_MOV(func)) { }
381
382 explicit find_delegate(const Func& func) noexcept
383 : func_(func) { }
384
385 // Invoke object directly. This operation is useful when the calling
386 // function has just constructed the delegate, such as what happens when
387 // iterating a query.
388 flecs::entity invoke(ecs_iter_t *iter) const {
389 field_ptrs<Components...> terms;
390
391 iter->flags |= EcsIterCppEach;
392
393 if (iter->ref_fields | iter->up_fields) {
394 terms.populate(iter);
395 return invoke_callback< each_ref_field >(iter, func_, 0, terms.fields_);
396 } else {
397 terms.populate_self(iter);
398 return invoke_callback< each_field >(iter, func_, 0, terms.fields_);
399 }
400 }
401
402private:
403 // Number of function arguments is one more than number of components, pass
404 // entity as argument.
405 template <template<typename X, typename = int> class ColumnType,
406 typename... Args,
407 typename Fn = Func,
408 if_t<sizeof...(Components) == sizeof...(Args)> = 0,
409 decltype(bool(std::declval<const Fn&>()(
410 std::declval<flecs::entity>(),
411 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...))) = true>
412 static flecs::entity invoke_callback(
413 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
414 {
415 ECS_TABLE_LOCK(iter->world, iter->table);
416
417 ecs_world_t *world = iter->world;
418 size_t count = static_cast<size_t>(iter->count);
419 flecs::entity result;
420
421 ecs_assert(count > 0, ECS_INVALID_OPERATION,
422 "no entities returned, use find() without flecs::entity argument");
423
424 for (size_t i = 0; i < count; i ++) {
425 if (func(flecs::entity(world, iter->entities[i]),
426 (ColumnType< remove_reference_t<Components> >(iter, comps, i)
427 .get_row())...))
428 {
429 result = flecs::entity(world, iter->entities[i]);
430 break;
431 }
432 }
433
434 ECS_TABLE_UNLOCK(iter->world, iter->table);
435
436 return result;
437 }
438
439 // Number of function arguments is two more than number of components, pass
440 // iter + index as argument.
441 template <template<typename X, typename = int> class ColumnType,
442 typename... Args,
443 typename Fn = Func,
444 if_t<sizeof...(Components) == sizeof...(Args)> = 0,
445 decltype(bool(std::declval<const Fn&>()(
446 std::declval<flecs::iter&>(),
447 std::declval<size_t&>(),
448 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...))) = true>
449 static flecs::entity invoke_callback(
450 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
451 {
452 size_t count = static_cast<size_t>(iter->count);
453 if (count == 0) {
454 // If query has no This terms, count can be 0. Since each does not
455 // have an entity parameter, just pass through components
456 count = 1;
457 }
458
459 flecs::iter it(iter);
460 flecs::entity result;
461
462 ECS_TABLE_LOCK(iter->world, iter->table);
463
464 for (size_t i = 0; i < count; i ++) {
465 if (func(it, i,
466 (ColumnType< remove_reference_t<Components> >(iter, comps, i)
467 .get_row())...))
468 {
469 result = flecs::entity(iter->world, iter->entities[i]);
470 break;
471 }
472 }
473
474 ECS_TABLE_UNLOCK(iter->world, iter->table);
475
476 return result;
477 }
478
479 // Number of function arguments is equal to number of components, no entity
480 template <template<typename X, typename = int> class ColumnType,
481 typename... Args,
482 typename Fn = Func,
483 if_t<sizeof...(Components) == sizeof...(Args)> = 0,
484 decltype(bool(std::declval<const Fn&>()(
485 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...))) = true>
486 static flecs::entity invoke_callback(
487 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
488 {
489 size_t count = static_cast<size_t>(iter->count);
490 if (count == 0) {
491 // If query has no This terms, count can be 0. Since each does not
492 // have an entity parameter, just pass through components
493 count = 1;
494 }
495
496 flecs::iter it(iter);
497 flecs::entity result;
498
499 ECS_TABLE_LOCK(iter->world, iter->table);
500
501 for (size_t i = 0; i < count; i ++) {
502 if (func(
503 (ColumnType< remove_reference_t<Components> >(iter, comps, i)
504 .get_row())...))
505 {
506 result = flecs::entity(iter->world, iter->entities[i]);
507 break;
508 }
509 }
510
511 ECS_TABLE_UNLOCK(iter->world, iter->table);
512
513 return result;
514 }
515
516 template <template<typename X, typename = int> class ColumnType,
517 typename... Args, if_t< sizeof...(Components) != sizeof...(Args) > = 0>
518 static flecs::entity invoke_callback(ecs_iter_t *iter, const Func& func,
519 size_t index, Terms& columns, Args... comps)
520 {
521 return invoke_callback<ColumnType>(
522 iter, func, index + 1, columns, comps..., columns[index]);
523 }
524
525 Func func_;
526};
527
531
532template <typename Func>
534 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
535 explicit run_delegate(Func&& func) noexcept
536 : func_(FLECS_MOV(func)) { }
537
538 explicit run_delegate(const Func& func) noexcept
539 : func_(func) { }
540
541 // Invoke object directly. This operation is useful when the calling
542 // function has just constructed the delegate, such as what happens when
543 // iterating a query.
544 void invoke(ecs_iter_t *iter) const {
545 flecs::iter it(iter);
546 iter->flags &= ~EcsIterIsValid;
547 func_(it);
548 }
549
550 // Static function that can be used as callback for systems/triggers
551 static void run(ecs_iter_t *iter) {
552 auto self = static_cast<const run_delegate*>(iter->run_ctx);
553 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
554 self->invoke(iter);
555 }
556
557 Func func_;
558};
559
560
564
565template <typename Func>
567 explicit entity_observer_delegate(Func&& func) noexcept
568 : func_(FLECS_MOV(func)) { }
569
570 // Static function that can be used as callback for systems/triggers
571 static void run(ecs_iter_t *iter) {
572 invoke<Func>(iter);
573 }
574
575private:
576 template <typename F,
577 decltype(std::declval<const F&>()(std::declval<flecs::entity>()), 0) = 0>
578 static void invoke(ecs_iter_t *iter) {
579 auto self = static_cast<const entity_observer_delegate*>(iter->callback_ctx);
580 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
581 self->func_(flecs::entity(iter->world, ecs_field_src(iter, 0)));
582 }
583
584 template <typename F,
585 decltype(std::declval<const F&>()(), 0) = 0>
586 static void invoke(ecs_iter_t *iter) {
587 auto self = static_cast<const entity_observer_delegate*>(iter->callback_ctx);
588 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
589 self->func_();
590 }
591
592 Func func_;
593};
594
595template <typename Func, typename Event>
597 explicit entity_payload_observer_delegate(Func&& func) noexcept
598 : func_(FLECS_MOV(func)) { }
599
600 // Static function that can be used as callback for systems/triggers
601 static void run(ecs_iter_t *iter) {
602 invoke<Func>(iter);
603 }
604
605private:
606 template <typename F,
607 decltype(std::declval<const F&>()(
608 std::declval<Event&>()), 0) = 0>
609 static void invoke(ecs_iter_t *iter) {
610 auto self = static_cast<const entity_payload_observer_delegate*>(
611 iter->callback_ctx);
612 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
613 ecs_assert(iter->param != nullptr, ECS_INVALID_OPERATION,
614 "entity observer invoked without payload");
615
616 Event *data = static_cast<Event*>(iter->param);
617 self->func_(*data);
618 }
619
620 template <typename F,
621 decltype(std::declval<const F&>()(
622 std::declval<flecs::entity>(),
623 std::declval<Event&>()), 0) = 0>
624 static void invoke(ecs_iter_t *iter) {
625 auto self = static_cast<const entity_payload_observer_delegate*>(
626 iter->callback_ctx);
627 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL);
628 ecs_assert(iter->param != nullptr, ECS_INVALID_OPERATION,
629 "entity observer invoked without payload");
630
631 Event *data = static_cast<Event*>(iter->param);
632 self->func_(flecs::entity(iter->world, ecs_field_src(iter, 0)), *data);
633 }
634
635 Func func_;
636};
637
638
642
643template<typename ... Args>
645
646template<typename ... Args>
648 using ColumnArray = flecs::array<int32_t, sizeof...(Args)>;
649 using ArrayType = flecs::array<void*, sizeof...(Args)>;
650 using DummyArray = flecs::array<int, sizeof...(Args)>;
651 using IdArray = flecs::array<id_t, sizeof...(Args)>;
652
653 static bool const_args() {
654 static flecs::array<bool, sizeof...(Args)> is_const_args ({
655 flecs::is_const<flecs::remove_reference_t<Args>>::value...
656 });
657
658 for (auto is_const : is_const_args) {
659 if (!is_const) {
660 return false;
661 }
662 }
663 return true;
664 }
665
666 static
667 bool get_ptrs(world_t *world, flecs::entity_t e, const ecs_record_t *r, ecs_table_t *table,
668 ArrayType& ptrs)
669 {
670 ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL);
672 !ecs_table_has_flags(table, EcsTableHasSparse))
673 {
674 return false;
675 }
676
677 /* table_index_of needs real world */
678 const flecs::world_t *real_world = ecs_get_world(world);
679
680 IdArray ids ({
681 _::type<Args>().id(world)...
682 });
683
684 /* Get column indices for components */
685 ColumnArray columns ({
687 _::type<Args>().id(world))...
688 });
689
690 /* Get pointers for columns for entity */
691 size_t i = 0;
692 for (int32_t column : columns) {
693 if (column == -1) {
694 /* Component could be sparse */
695 void *ptr = ecs_get_mut_id(world, e, ids[i]);
696 if (!ptr) {
697 return false;
698 }
699
700 ptrs[i ++] = ptr;
701 continue;
702 }
703
704 ptrs[i ++] = ecs_record_get_by_column(r, column, 0);
705 }
706
707 return true;
708 }
709
710 static bool ensure_ptrs(world_t *world, ecs_entity_t e, ArrayType& ptrs) {
711 /* Get pointers w/ensure */
712 size_t i = 0;
713 DummyArray dummy ({
714 (ptrs[i ++] = ecs_ensure_id(world, e,
715 _::type<Args>().id(world)), 0)...
716 });
717
718 return true;
719 }
720
721 template <typename Func>
722 static bool invoke_read(world_t *world, entity_t e, const Func& func) {
723 const ecs_record_t *r = ecs_read_begin(world, e);
724 if (!r) {
725 return false;
726 }
727
728 ecs_table_t *table = r->table;
729 if (!table) {
730 return false;
731 }
732
733 ArrayType ptrs;
734 bool has_components = get_ptrs(world, e, r, table, ptrs);
735 if (has_components) {
736 invoke_callback(func, 0, ptrs);
737 }
738
739 ecs_read_end(r);
740
741 return has_components;
742 }
743
744 template <typename Func>
745 static bool invoke_write(world_t *world, entity_t e, const Func& func) {
747 if (!r) {
748 return false;
749 }
750
751 ecs_table_t *table = r->table;
752 if (!table) {
753 return false;
754 }
755
756 ArrayType ptrs;
757 bool has_components = get_ptrs(world, e, r, table, ptrs);
758 if (has_components) {
759 invoke_callback(func, 0, ptrs);
760 }
761
762 ecs_write_end(r);
763
764 return has_components;
765 }
766
767 template <typename Func>
768 static bool invoke_get(world_t *world, entity_t e, const Func& func) {
769 if (const_args()) {
770 return invoke_read(world, e, func);
771 } else {
772 return invoke_write(world, e, func);
773 }
774 }
775
776 // Utility for storing id in array in pack expansion
777 static size_t store_added(IdArray& added, size_t elem, ecs_table_t *prev,
778 ecs_table_t *next, id_t id)
779 {
780 // Array should only contain ids for components that are actually added,
781 // so check if the prev and next tables are different.
782 if (prev != next) {
783 added[elem] = id;
784 elem ++;
785 }
786 return elem;
787 }
788
789 template <typename Func>
790 static bool invoke_ensure(world_t *world, entity_t id, const Func& func) {
792
793 ArrayType ptrs;
794 ecs_table_t *table = NULL;
795
796 // When not deferred take the fast path.
797 if (!w.is_deferred()) {
798 // Bit of low level code so we only do at most one table move & one
799 // entity lookup for the entire operation.
800
801 // Make sure the object is not a stage. Operations on a stage are
802 // only allowed when the stage is in deferred mode, which is when
803 // the world is in readonly mode.
804 ecs_assert(!w.is_stage(), ECS_INVALID_PARAMETER, NULL);
805
806 // Find table for entity
808 if (r) {
809 table = r->table;
810 }
811
812 // Find destination table that has all components
813 ecs_table_t *prev = table, *next;
814 size_t elem = 0;
815 IdArray added;
816
817 // Iterate components, only store added component ids in added array
818 DummyArray dummy_before ({ (
819 next = ecs_table_add_id(world, prev, w.id<Args>()),
820 elem = store_added(added, elem, prev, next, w.id<Args>()),
821 prev = next, 0
822 )... });
823 (void)dummy_before;
824
825 // If table is different, move entity straight to it
826 if (table != next) {
827 ecs_type_t ids;
828 ids.array = added.ptr();
829 ids.count = static_cast<ecs_size_t>(elem);
830 ecs_commit(world, id, r, next, &ids, NULL);
831 table = next;
832 }
833
834 if (!get_ptrs(w, id, r, table, ptrs)) {
835 ecs_abort(ECS_INTERNAL_ERROR, NULL);
836 }
837
838 ECS_TABLE_LOCK(world, table);
839
840 // When deferred, obtain pointers with regular ensure
841 } else {
842 ensure_ptrs(world, id, ptrs);
843 }
844
845 invoke_callback(func, 0, ptrs);
846
847 if (!w.is_deferred()) {
848 ECS_TABLE_UNLOCK(world, table);
849 }
850
851 // Call modified on each component
852 DummyArray dummy_after ({
853 ( ecs_modified_id(world, id, w.id<Args>()), 0)...
854 });
855 (void)dummy_after;
856
857 return true;
858 }
859
860private:
861 template <typename Func, typename ... TArgs,
862 if_t<sizeof...(TArgs) == sizeof...(Args)> = 0>
863 static void invoke_callback(
864 const Func& f, size_t, ArrayType&, TArgs&& ... comps)
865 {
866 f(*static_cast<typename base_arg_type<Args>::type*>(comps)...);
867 }
868
869 template <typename Func, typename ... TArgs,
870 if_t<sizeof...(TArgs) != sizeof...(Args)> = 0>
871 static void invoke_callback(const Func& f, size_t arg, ArrayType& ptrs,
872 TArgs&& ... comps)
873 {
874 invoke_callback(f, arg + 1, ptrs, comps..., ptrs[arg]);
875 }
876};
877
878template <typename Func, typename U = int>
880 static_assert(function_traits<Func>::value, "type is not callable");
881};
882
883template <typename Func>
884struct entity_with_delegate<Func, if_t< is_callable<Func>::value > >
885 : entity_with_delegate_impl< arg_list_t<Func> >
886{
887 static_assert(function_traits<Func>::arity > 0,
888 "function must have at least one argument");
889};
890
891} // namespace _
892
893// Experimental: allows using the each delegate for use cases outside of flecs
894template <typename Func, typename ... Args>
896
897} // 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_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:608
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:1050
ecs_flags32_t ref_fields
Bitset with fields that aren't component arrays.
Definition flecs.h:1067
ecs_flags32_t flags
Iterator flags.
Definition flecs.h:1103
ecs_table_t * table
Current table.
Definition flecs.h:1058
ecs_world_t * world
The world.
Definition flecs.h:1052
void * callback_ctx
Callback language binding context.
Definition flecs.h:1090
ecs_flags32_t up_fields
Bitset with fields matched through up traversal.
Definition flecs.h:1069
int32_t count
Number of entities to iterate.
Definition flecs.h:1100
const ecs_entity_t * entities
Entity identifiers.
Definition flecs.h:1056
Record for entity index.
Definition flecs.h:486
ecs_table_t * table
Identifies a type (and table) in world.
Definition flecs.h:488
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
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:140
flecs::field< const flecs::entity_t > entities() const
Get readonly access to entity ids.
Definition iter.hpp:312
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