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

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