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