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 void *on_validate = nullptr;
23 ecs_ctx_free_t free_on_add = nullptr;
24 ecs_ctx_free_t free_on_remove = nullptr;
25 ecs_ctx_free_t free_on_set = nullptr;
26 ecs_ctx_free_t free_on_replace = nullptr;
27 ecs_ctx_free_t free_on_validate = nullptr;
28
30 if (on_add && free_on_add) {
31 free_on_add(on_add);
32 }
33 if (on_remove && free_on_remove) {
34 free_on_remove(on_remove);
35 }
36 if (on_set && free_on_set) {
37 free_on_set(on_set);
38 }
39 if (on_replace && free_on_replace) {
40 free_on_replace(on_replace);
41 }
42 if (on_validate && free_on_validate) {
43 free_on_validate(on_validate);
44 }
45 }
46};
47
48// Utility to convert a template argument pack to an array of term pointers.
49struct field_ptr {
50 void *ptr = nullptr;
51 int8_t index = 0;
52 bool is_ref = false;
53 bool is_row = false;
54};
55
56template <typename ... Components>
57struct field_ptrs {
58 using array = flecs::array<_::field_ptr, sizeof...(Components)>;
59
60 void populate(const ecs_iter_t *iter) {
61 populate_impl(iter, std::index_sequence_for<Components...>{});
62 }
63
64 void populate_self(const ecs_iter_t *iter) {
65 populate_self_impl(iter, std::index_sequence_for<Components...>{});
66 }
67
68 array fields_;
69
70private:
71 template <typename T>
72 void populate_field(const ecs_iter_t *iter, size_t index) {
73 using A = remove_pointer_t<actual_type_t<T>>;
74 if constexpr (!is_empty_v<A>) {
75 if (iter->row_fields & (1llu << index)) {
76 /* Need to fetch the value with ecs_field_at() */
77 fields_[index].is_row = true;
78 fields_[index].is_ref = true;
79 fields_[index].index = static_cast<int8_t>(index);
80 } else {
81 fields_[index].ptr = ecs_field_w_size(iter, sizeof(A),
82 static_cast<int8_t>(index));
83 fields_[index].is_ref = iter->sources[index] != 0;
84 }
85 }
86 }
87
88 template <typename T>
89 void populate_self_field(const ecs_iter_t *iter, size_t index) {
90 (void)iter; (void)index;
91
92 using A = remove_pointer_t<actual_type_t<T>>;
93 if constexpr (!is_empty_v<A>) {
94 fields_[index].ptr = ecs_field_w_size(iter, sizeof(A),
95 static_cast<int8_t>(index));
96 fields_[index].is_ref = false;
97 }
98 }
99
100 template <size_t... Is>
101 void populate_impl(const ecs_iter_t *iter, std::index_sequence<Is...>) {
102 (void)iter;
103 (populate_field<Components>(iter, Is), ...);
104 }
105
106 template <size_t... Is>
107 void populate_self_impl(const ecs_iter_t *iter, std::index_sequence<Is...>) {
108 (void)iter;
109 (populate_self_field<Components>(iter, Is), ...);
110 }
111};
112
113struct delegate { };
114
115// Template that figures out from the template parameters of a query/system
116// how to pass the value to the each callback.
117template <typename T, typename = int>
118struct each_field { };
119
120// Base class.
122 each_column_base(const _::field_ptr& field, size_t row)
123 : field_(field), row_(row) {
124 }
125
126protected:
127 const _::field_ptr& field_;
128 size_t row_;
129};
130
131// If the type is not a pointer, return a reference to the type (default case).
132template <typename T>
133struct each_field<T, if_t< !is_pointer<T>::value &&
134 !is_empty<actual_type_t<T>>::value && is_actual<T>::value > >
136{
137 each_field(const flecs::iter_t*, _::field_ptr& field, size_t row)
138 : each_column_base(field, row) { }
139
140 T& get_row() {
141 return static_cast<T*>(this->field_.ptr)[this->row_];
142 }
143};
144
145// If the argument type is not the same as the actual component type, return by value.
146// This requires that the actual type can be converted to the type.
147// A typical scenario where this happens is when using flecs::pair types.
148template <typename T>
149struct each_field<T, if_t< !is_pointer<T>::value &&
150 !is_empty<actual_type_t<T>>::value && !is_actual<T>::value> >
152{
153 each_field(const flecs::iter_t*, _::field_ptr& field, size_t row)
154 : each_column_base(field, row) { }
155
156 T get_row() {
157 return static_cast<actual_type_t<T>*>(this->field_.ptr)[this->row_];
158 }
159};
160
161// If the type is empty (indicating a tag), the query will pass a nullptr. To avoid
162// returning nullptr to reference arguments, return a temporary value.
163template <typename T>
164struct each_field<T, if_t< is_empty<actual_type_t<T>>::value &&
165 !is_pointer<T>::value > >
167{
168 each_field(const flecs::iter_t*, _::field_ptr& field, size_t row)
169 : each_column_base(field, row) { }
170
171 T get_row() {
172 return actual_type_t<T>();
173 }
174};
175
176// If the type is a pointer (indicating an optional value), don't index with row if
177// the field is not set.
178template <typename T>
179struct each_field<T, if_t< is_pointer<T>::value &&
180 !is_empty<actual_type_t<T>>::value > >
182{
183 each_field(const flecs::iter_t*, _::field_ptr& field, size_t row)
184 : each_column_base(field, row) { }
185
186 actual_type_t<T> get_row() {
187 if (this->field_.ptr) {
188 return &static_cast<actual_type_t<T>>(this->field_.ptr)[this->row_];
189 } else {
190 // Optional argument doesn't have a value.
191 return nullptr;
192 }
193 }
194};
195
196// If the query contains component references to other entities, check if the
197// current argument is one.
198template <typename T, typename = int>
199struct each_ref_field : public each_field<T> {
200 using A = remove_pointer_t<actual_type_t<T>>;
201
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 is always a
207 // single value, not an array. This prevents the application from
208 // having to do an if-check on whether the field 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(A), 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/observers.
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, nullptr);
256 self->invoke(iter);
257 }
258
259 // Static function that can be used as callback for systems/observers.
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, nullptr);
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
303 // Static function to call for component on_replace hook.
304 static void run_replace(ecs_iter_t *iter) {
305 component_binding_ctx *ctx = reinterpret_cast<component_binding_ctx*>(
306 iter->callback_ctx);
307 iter->callback_ctx = ctx->on_replace;
308 run(iter);
309 }
310
311private:
312 // func(flecs::entity, Components...)
313 template <template<typename X, typename = int> class ColumnType,
314 typename... Args,
315 typename Fn = Func,
316 decltype(std::declval<const Fn&>()(
317 std::declval<flecs::entity>(),
318 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...), 0) = 0>
319 static void invoke_callback(
320 ecs_iter_t *iter, const Func& func, size_t i, Args... comps)
321 {
323 "query does not return entities ($this variable is not populated)");
325 (ColumnType< remove_reference_t<Components> >(iter, comps, i)
326 .get_row())...);
327 }
328
329 // func(flecs::iter&, size_t row, Components...)
330 template <template<typename X, typename = int> class ColumnType,
331 typename... Args,
332 typename Fn = Func,
333 decltype(std::declval<const Fn&>()(
334 std::declval<flecs::iter&>(),
335 std::declval<size_t&>(),
336 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...), 0) = 0>
337 static void invoke_callback(
338 ecs_iter_t *iter, const Func& func, size_t i, Args... comps)
339 {
340 flecs::iter it(iter);
341 func(it, i, (ColumnType< remove_reference_t<Components> >(iter, comps, i)
342 .get_row())...);
343 }
344
345 // func(Components...)
346 template <template<typename X, typename = int> class ColumnType,
347 typename... Args,
348 typename Fn = Func,
349 decltype(std::declval<const Fn&>()(
350 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...), 0) = 0>
351 static void invoke_callback(
352 ecs_iter_t *iter, const Func& func, size_t i, Args... comps)
353 {
354 func((ColumnType< remove_reference_t<Components> >(iter, comps, i)
355 .get_row())...);
356 }
357
358 template <template<typename X, typename = int> class ColumnType,
359 typename... Args, if_t<
360 sizeof...(Components) == sizeof...(Args)> = 0>
361 static void invoke_unpack(
362 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
363 {
364 ECS_TABLE_LOCK(iter->world, iter->table);
365
366 size_t count = static_cast<size_t>(iter->count);
367 if (count == 0 && !iter->table) {
368 // If the query has no This terms, count can be 0. Since each does not
369 // have an entity parameter, just pass through components.
370 count = 1;
371 }
372
373 for (size_t i = 0; i < count; i ++) {
374 invoke_callback<ColumnType>(iter, func, i, comps...);
375 }
376
377 ECS_TABLE_UNLOCK(iter->world, iter->table);
378 }
379
380 template <template<typename X, typename = int> class ColumnType,
381 typename... Args, if_t< sizeof...(Components) != sizeof...(Args) > = 0>
382 static void invoke_unpack(ecs_iter_t *iter, const Func& func,
383 size_t index, Terms& columns, Args... comps)
384 {
385 invoke_unpack<ColumnType>(
386 iter, func, index + 1, columns, comps..., columns[index]);
387 }
388
389public:
390 Func func_;
391};
392
393template <typename Func, typename T>
395 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
396 explicit validate_delegate(Func&& func) noexcept
397 : func_(FLECS_MOV(func)) { }
398
399 explicit validate_delegate(const Func& func) noexcept
400 : func_(func) { }
401
402 static bool run(ecs_world_t *world, ecs_entity_t entity, void *ptr) {
405 ecs_assert(h != nullptr, ECS_INTERNAL_ERROR, nullptr);
406 auto ctx = static_cast<component_binding_ctx*>(h->binding_ctx);
407 ecs_assert(ctx != nullptr, ECS_INTERNAL_ERROR, nullptr);
408 auto self = static_cast<const validate_delegate*>(ctx->on_validate);
409 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, nullptr);
410 return self->func_(
411 flecs::entity(world, entity), *static_cast<T*>(ptr));
412 }
413
414 Func func_;
415};
416
417template <typename Func, typename ... Components>
418struct find_delegate : public delegate {
419 using Terms = typename field_ptrs<Components ...>::array;
420
421 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
422 explicit find_delegate(Func&& func) noexcept
423 : func_(FLECS_MOV(func)) { }
424
425 explicit find_delegate(const Func& func) noexcept
426 : func_(func) { }
427
428 // Invoke object directly. This operation is useful when the calling
429 // function has just constructed the delegate, such as what happens when
430 // iterating a query.
431 flecs::entity invoke(ecs_iter_t *iter) const {
432 field_ptrs<Components...> terms;
433
434 iter->flags |= EcsIterCppEach;
435
436 if (iter->ref_fields | iter->up_fields) {
437 terms.populate(iter);
438 return invoke_callback< each_ref_field >(iter, func_, 0, terms.fields_);
439 } else {
440 terms.populate_self(iter);
441 return invoke_callback< each_field >(iter, func_, 0, terms.fields_);
442 }
443 }
444
445private:
446 // The number of function arguments is one more than the number of components, pass
447 // entity as argument.
448 template <template<typename X, typename = int> class ColumnType,
449 typename... Args,
450 typename Fn = Func,
451 if_t<sizeof...(Components) == sizeof...(Args)> = 0,
452 decltype(bool(std::declval<const Fn&>()(
453 std::declval<flecs::entity>(),
454 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...))) = true>
455 static flecs::entity invoke_callback(
456 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
457 {
458 ECS_TABLE_LOCK(iter->world, iter->table);
459
461 size_t count = static_cast<size_t>(iter->count);
462 flecs::entity result;
463
464 for (size_t i = 0; i < count; i ++) {
465 if (func(flecs::entity(world, iter->entities[i]),
466 (ColumnType< remove_reference_t<Components> >(iter, comps, i)
467 .get_row())...))
468 {
469 result = flecs::entity(world, iter->entities[i]);
470 break;
471 }
472 }
473
474 ECS_TABLE_UNLOCK(iter->world, iter->table);
475
476 return result;
477 }
478
479 // The number of function arguments is two more than the number of components, pass
480 // iter + index as argument.
481 template <template<typename X, typename = int> class ColumnType,
482 typename... Args,
483 typename Fn = Func,
484 if_t<sizeof...(Components) == sizeof...(Args)> = 0,
485 decltype(bool(std::declval<const Fn&>()(
486 std::declval<flecs::iter&>(),
487 std::declval<size_t&>(),
488 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...))) = true>
489 static flecs::entity invoke_callback(
490 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
491 {
492 size_t count = static_cast<size_t>(iter->count);
493 if (count == 0) {
494 // If the query has no This terms, count can be 0. Since each does not
495 // have an entity parameter, just pass through components.
496 count = 1;
497 }
498
499 flecs::iter it(iter);
500 flecs::entity result;
501
502 ECS_TABLE_LOCK(iter->world, iter->table);
503
504 for (size_t i = 0; i < count; i ++) {
505 if (func(it, i,
506 (ColumnType< remove_reference_t<Components> >(iter, comps, i)
507 .get_row())...))
508 {
509 result = flecs::entity(iter->world, iter->entities[i]);
510 break;
511 }
512 }
513
514 ECS_TABLE_UNLOCK(iter->world, iter->table);
515
516 return result;
517 }
518
519 // The number of function arguments is equal to the number of components, no entity.
520 template <template<typename X, typename = int> class ColumnType,
521 typename... Args,
522 typename Fn = Func,
523 if_t<sizeof...(Components) == sizeof...(Args)> = 0,
524 decltype(bool(std::declval<const Fn&>()(
525 std::declval<ColumnType< remove_reference_t<Components> > >().get_row()...))) = true>
526 static flecs::entity invoke_callback(
527 ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps)
528 {
529 size_t count = static_cast<size_t>(iter->count);
530 if (count == 0) {
531 // If the query has no This terms, count can be 0. Since each does not
532 // have an entity parameter, just pass through components.
533 count = 1;
534 }
535
536 flecs::iter it(iter);
537 flecs::entity result;
538
539 ECS_TABLE_LOCK(iter->world, iter->table);
540
541 for (size_t i = 0; i < count; i ++) {
542 if (func(
543 (ColumnType< remove_reference_t<Components> >(iter, comps, i)
544 .get_row())...))
545 {
546 result = flecs::entity(iter->world, iter->entities[i]);
547 break;
548 }
549 }
550
551 ECS_TABLE_UNLOCK(iter->world, iter->table);
552
553 return result;
554 }
555
556 template <template<typename X, typename = int> class ColumnType,
557 typename... Args, if_t< sizeof...(Components) != sizeof...(Args) > = 0>
558 static flecs::entity invoke_callback(ecs_iter_t *iter, const Func& func,
559 size_t index, Terms& columns, Args... comps)
560 {
561 return invoke_callback<ColumnType>(
562 iter, func, index + 1, columns, comps..., columns[index]);
563 }
564
565 Func func_;
566};
567
571
572template <typename Func>
574 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
575 explicit run_delegate(Func&& func) noexcept
576 : func_(FLECS_MOV(func)) { }
577
578 explicit run_delegate(const Func& func) noexcept
579 : func_(func) { }
580
581 // Invoke object directly. This operation is useful when the calling
582 // function has just constructed the delegate, such as what happens when
583 // iterating a query.
584 void invoke(ecs_iter_t *iter) const {
585 flecs::iter it(iter);
586 iter->flags &= ~EcsIterIsValid;
587 func_(it);
588 }
589
590 // Static function that can be used as callback for systems/observers.
591 static void run(ecs_iter_t *iter) {
592 auto self = static_cast<const run_delegate*>(iter->run_ctx);
593 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, nullptr);
594 self->invoke(iter);
595 }
596
597 Func func_;
598};
599
600
604
605template <typename Func>
607 explicit entity_observer_delegate(Func&& func) noexcept
608 : func_(FLECS_MOV(func)) { }
609
610 // Static function that can be used as callback for systems/observers.
611 static void run(ecs_iter_t *iter) {
612 invoke<Func>(iter);
613 }
614
615private:
616 template <typename F,
617 decltype(std::declval<const F&>()(std::declval<flecs::entity>()), 0) = 0>
618 static void invoke(ecs_iter_t *iter) {
619 auto self = static_cast<const entity_observer_delegate*>(iter->callback_ctx);
620 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, nullptr);
621 self->func_(flecs::entity(iter->world, ecs_field_src(iter, 0)));
622 }
623
624 template <typename F,
625 decltype(std::declval<const F&>()(), 0) = 0>
626 static void invoke(ecs_iter_t *iter) {
627 auto self = static_cast<const entity_observer_delegate*>(iter->callback_ctx);
628 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, nullptr);
629 self->func_();
630 }
631
632 Func func_;
633};
634
635template <typename Func, typename Event>
637 explicit entity_payload_observer_delegate(Func&& func) noexcept
638 : func_(FLECS_MOV(func)) { }
639
640 // Static function that can be used as callback for systems/observers.
641 static void run(ecs_iter_t *iter) {
642 invoke<Func>(iter);
643 }
644
645private:
646 template <typename F,
647 decltype(std::declval<const F&>()(
648 std::declval<Event&>()), 0) = 0>
649 static void invoke(ecs_iter_t *iter) {
650 auto self = static_cast<const entity_payload_observer_delegate*>(
651 iter->callback_ctx);
652 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, nullptr);
654 "entity observer invoked without payload");
655
656 Event *data = static_cast<Event*>(iter->param);
657 self->func_(*data);
658 }
659
660 template <typename F,
661 decltype(std::declval<const F&>()(
662 std::declval<flecs::entity>(),
663 std::declval<Event&>()), 0) = 0>
664 static void invoke(ecs_iter_t *iter) {
665 auto self = static_cast<const entity_payload_observer_delegate*>(
666 iter->callback_ctx);
667 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, nullptr);
669 "entity observer invoked without payload");
670
671 Event *data = static_cast<Event*>(iter->param);
672 self->func_(flecs::entity(iter->world, ecs_field_src(iter, 0)), *data);
673 }
674
675 Func func_;
676};
677
678
682
683template<typename ... Args>
685
686template<typename ... Args>
687struct entity_with_delegate_impl<arg_list<Args ...>> {
688 using ColumnArray = flecs::array<int32_t, sizeof...(Args)>;
689 using ArrayType = flecs::array<void*, sizeof...(Args)>;
690 using DummyArray = flecs::array<int, sizeof...(Args)>;
691 using IdArray = flecs::array<id_t, sizeof...(Args)>;
692
693 static constexpr bool const_args() {
694 return (is_const_v<remove_reference_t<Args>> && ...);
695 }
696
697 static
698 bool get_ptrs(world_t *world, flecs::entity_t e, const ecs_record_t *r, ecs_table_t *table,
699 ArrayType& ptrs)
700 {
701 ecs_assert(table != nullptr, ECS_INTERNAL_ERROR, nullptr);
702
703 /* table_index_of needs the real world. */
704 const flecs::world_t *real_world = ecs_get_world(world);
705
706 IdArray ids ({
707 _::type<Args>().id(world)...
708 });
709
710 /* Get column indices for components. */
711 ColumnArray columns ({
713 _::type<Args>().id(world))...
714 });
715
716 /* Get pointers for columns for the entity. */
717 size_t i = 0;
718 for (int32_t column : columns) {
719 if (column == -1) {
720 /* Component could be sparse. */
721 void *ptr = ecs_get_mut_id(world, e, ids[i]);
722 if (!ptr) {
723 return false;
724 }
725
726 ptrs[i ++] = ptr;
727 continue;
728 }
729
730 ptrs[i ++] = ecs_record_get_by_column(r, column, 0);
731 }
732
733 return true;
734 }
735
736 static bool ensure_ptrs(world_t *world, ecs_entity_t e, ArrayType& ptrs) {
737 /* Get pointers w/ensure. */
738 size_t i = 0;
739 DummyArray dummy ({
740 (ptrs[i ++] = ecs_ensure_id(world, e,
741 _::type<Args>().id(world), sizeof(Args)), 0)...
742 });
743
744 return true;
745 }
746
747 template <typename Func>
748 static bool invoke_read(world_t *world, entity_t e, const Func& func) {
749 const ecs_record_t *r = ecs_read_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_read_end(r);
766
767 return has_components;
768 }
769
770 template <typename Func>
771 static bool invoke_write(world_t *world, entity_t e, const Func& func) {
772 ecs_record_t *r = ecs_write_begin(world, e);
773 if (!r) {
774 return false;
775 }
776
777 ecs_table_t *table = r->table;
778 if (!table) {
779 return false;
780 }
781
782 ArrayType ptrs;
783 bool has_components = get_ptrs(world, e, r, table, ptrs);
784 if (has_components) {
785 invoke_callback(func, 0, ptrs);
786 }
787
788 ecs_write_end(r);
789
790 return has_components;
791 }
792
793 template <typename Func>
794 static bool invoke_get(world_t *world, entity_t e, const Func& func) {
795 if constexpr (const_args()) {
796 return invoke_read(world, e, func);
797 } else {
798 return invoke_write(world, e, func);
799 }
800 }
801
802 // Utility for storing an ID in an array in pack expansion.
803 static size_t store_added(IdArray& added, size_t elem, ecs_table_t *prev,
804 ecs_table_t *next, id_t id)
805 {
806 // Array should only contain IDs for components that are actually added,
807 // so check if the prev and next tables are different.
808 if (prev != next) {
809 added[elem] = id;
810 elem ++;
811 }
812 return elem;
813 }
814
815 struct InvokeCtx {
816 InvokeCtx(flecs::table_t *table_arg) : table(table_arg) { }
818 size_t component_count = 0;
819 IdArray added = {};
820 };
821
822 static int invoke_add(
823 flecs::world& w,
825 flecs::id_t component_id,
826 InvokeCtx& ctx)
827 {
828 ecs_table_diff_t diff;
829 flecs::table_t *next = flecs_table_traverse_add(
830 w, ctx.table, &component_id, &diff);
831 if (next != ctx.table) {
832 ctx.added[ctx.component_count] = component_id;
833 ctx.component_count ++;
834 } else {
835 if (diff.added_flags & EcsTableHasDontFragment) {
836 w.entity(entity).add(component_id);
837
838 ctx.added[ctx.component_count] = component_id;
839 ctx.component_count ++;
840 }
841 }
842
843 ctx.table = next;
844
845 return 0;
846 }
847
848 template <typename Func>
849 static bool invoke_ensure(
850 world_t *world,
851 entity_t id,
852 const Func& func)
853 {
854 flecs::world w(world);
855
856 ArrayType ptrs;
857 ecs_table_t *table = nullptr;
858
859 // When not deferred, take the fast path.
860 if (!w.is_deferred()) {
861 // A bit of low-level code so we only do at most one table move and one
862 // entity lookup for the entire operation.
863
864 // Make sure the object is not a stage. Operations on a stage are
865 // only allowed when the stage is in deferred mode, which is when
866 // the world is in readonly mode.
868
869 // Find the table for the entity.
870 ecs_record_t *r = ecs_record_find(world, id);
871 if (r) {
872 table = r->table;
873 }
874
875 // Iterate components, only store added component IDs in the added array.
876 InvokeCtx ctx(table);
877 DummyArray dummy_before ({ (
878 invoke_add(w, id, w.id<Args>(), ctx)
879 )... });
880
881 (void)dummy_before;
882
883 // If the table is different, move the entity straight to it.
884 if (table != ctx.table) {
885 ecs_type_t ids;
886 ids.array = ctx.added.ptr();
887 ids.count = static_cast<ecs_size_t>(ctx.component_count);
888 ecs_commit(world, id, r, ctx.table, &ids, nullptr);
889 table = ctx.table;
890 }
891
892 if (!get_ptrs(w, id, r, table, ptrs)) {
894 }
895
896 ECS_TABLE_LOCK(world, table);
897
898 // When deferred, obtain pointers with regular ensure.
899 } else {
900 ensure_ptrs(world, id, ptrs);
901 }
902
903 invoke_callback(func, 0, ptrs);
904
905 if (!w.is_deferred()) {
906 ECS_TABLE_UNLOCK(world, table);
907 }
908
909 // Call modified on each component.
910 DummyArray dummy_after ({
911 ( ecs_modified_id(world, id, w.id<Args>()), 0)...
912 });
913 (void)dummy_after;
914
915 return true;
916 }
917
918private:
919 template <typename Func, typename ... TArgs,
920 if_t<sizeof...(TArgs) == sizeof...(Args)> = 0>
921 static void invoke_callback(
922 const Func& f, size_t, ArrayType&, TArgs&& ... comps)
923 {
924 f(*static_cast<typename base_arg_type<Args>::type*>(comps)...);
925 }
926
927 template <typename Func, typename ... TArgs,
928 if_t<sizeof...(TArgs) != sizeof...(Args)> = 0>
929 static void invoke_callback(const Func& f, size_t arg, ArrayType& ptrs,
930 TArgs&& ... comps)
931 {
932 invoke_callback(f, arg + 1, ptrs, comps..., ptrs[arg]);
933 }
934};
935
936template <typename Func, typename U = int>
938 static_assert(function_traits<Func>::value, "type is not callable");
939};
940
941template <typename Func>
942struct entity_with_delegate<Func, if_t< is_callable<Func>::value > >
943 : entity_with_delegate_impl< arg_list_t<Func> >
944{
945 static_assert(function_traits<Func>::arity > 0,
946 "function must have at least one argument");
947};
948
950template <typename ArgList>
952
953template <typename ... Args>
954struct each_normalize_args<arg_list<Args...>> {
955 using type = arg_list<remove_reference_t<Args>...>;
956};
957
960template <typename ArgList, typename = int>
962 using type = typename each_normalize_args<ArgList>::type;
963};
964
965template <typename First, typename ... Args>
966struct each_callback_args<arg_list<First, Args...>,
967 if_t<is_same<decay_t<First>, flecs::entity>::value>> {
968 using type = typename each_normalize_args<arg_list<Args...>>::type;
969};
970
971template <typename First, typename Second, typename ... Args>
972struct each_callback_args<arg_list<First, Second, Args...>,
973 if_t<is_same<decay_t<First>, flecs::iter>::value>> {
974 using type = typename each_normalize_args<arg_list<Args...>>::type;
975};
976
977} // namespace _
978
985template <typename Func, typename ... Args>
987
988} // namespace flecs
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:473
#define ECS_INVALID_OPERATION
Invalid operation error code.
Definition log.h:669
#define ECS_INVALID_PARAMETER
Invalid parameter error code.
Definition log.h:671
#define ECS_INTERNAL_ERROR
Internal error code.
Definition log.h:681
#define ecs_abort(error_code,...)
Abort.
Definition log.h:464
const ecs_type_hooks_t * ecs_get_hooks_id(const ecs_world_t *world, ecs_entity_t component)
Get hooks for a component.
ecs_id_t ecs_entity_t
An entity identifier.
Definition flecs.h:386
struct ecs_world_t ecs_world_t
A world is the container for all ECS data and supporting features.
Definition flecs.h:430
struct ecs_record_t ecs_record_t
Information about an entity, like its table and row.
Definition flecs.h:495
struct ecs_table_t ecs_table_t
A table stores entities and components for a specific type.
Definition flecs.h:436
flecs::entity entity(Args &&... args) const
Create an entity.
flecs::id id(E value) const
Convert an enum constant to an entity.
ecs_table_t table_t
Table type.
Definition c_types.hpp:23
ecs_id_t id_t
ID type.
Definition c_types.hpp:20
ecs_entity_t entity_t
Entity type.
Definition c_types.hpp:21
ecs_world_t world_t
World type.
Definition c_types.hpp:18
void(* ecs_ctx_free_t)(void *ctx)
Function to clean up context data.
Definition flecs.h:651
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 an entity has a component and return a pointer.
ecs_entity_t ecs_field_src(const ecs_iter_t *it, int8_t index)
Return the 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 a field at a specified row.
void * ecs_field_w_size(const ecs_iter_t *it, size_t size, int8_t index)
Get data for a field.
int32_t ecs_table_get_column_index(const ecs_world_t *world, const ecs_table_t *table, ecs_id_t component)
Get the column index for a 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) an entity to a table.
const ecs_world_t * ecs_get_world(const ecs_poly_t *poly)
Get the world from a poly.
Iterator.
Definition flecs.h:1185
void * binding_ctx
Language binding context.
Definition flecs.h:1025
A type is a list of (component) IDs.
Definition flecs.h:403
ecs_id_t * array
Array with IDs.
Definition flecs.h:404
int32_t count
Number of elements in array.
Definition flecs.h:405
Extract the component argument list from an each-callback signature.
Definition delegate.hpp:961
Strip references from each-callback argument types.
Definition delegate.hpp:951
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
Test if a type is the same as its actual type.
Definition pair.hpp:160
Trait to check if a type is callable.
Class for iterating over query results.
Definition iter.hpp:68
flecs::table table() const
Get the table for the current iterator result.
Definition iter.hpp:63
void * param()
Access param.
Definition iter.hpp:168
size_t count() const
Get the number of entities to iterate over.
Definition iter.hpp:111
flecs::field< const flecs::entity_t > entities() const
Get read-only access to entity IDs.
Definition iter.hpp:382
bool next()
Progress iterator.
Definition iter.hpp:439
flecs::world world() const
Get the world associated with the iterator.
Definition iter.hpp:27
Table.
Definition table.hpp:23
table()
Default constructor.
Definition table.hpp:25
The world.
Definition world.hpp:244
bool is_stage() const
Test if this is a stage.
Definition world.hpp:553
bool is_deferred() const
Test whether deferring is enabled.
Definition world.hpp:486
enable_if_t< V, int > if_t
Convenience enable_if alias using int as default type.
Definition utils.hpp:168