Flecs: include/flecs/addons/cpp/delegate.hpp Source File
Description: A fast entity component system (ECS) for C & C++

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