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