Skip to content
Flecs v4.1
delegate.hpp
Go to the documentation of this file.
1/**
2 * @file addons/cpp/delegate.hpp
3 * @brief Wrappers around C++ functions that provide callbacks for C APIs.
4 */
5
6#pragma once
7
8#include <utility> // std::declval
9
10namespace flecs
11{
12
13namespace _
14{
15
17 enum Hook { OnAdd, OnRemove, OnSet, OnReplace, OnValidate, Count };
18 void *callbacks[Count] = {};
19 ecs_ctx_free_t free[Count] = {};
20
22 for (size_t i = 0; i < Count; i ++) {
23 if (callbacks[i] && free[i]) {
24 free[i](callbacks[i]);
25 }
26 }
27 }
28};
29
30// Utility to convert a template argument pack to an array of term pointers.
31struct field_ptr {
32 void *ptr = nullptr;
33 int32_t stride = 0;
34 int8_t index = 0;
35 bool is_ref = false;
36 bool is_row = false;
37};
38
39template <typename ... Components>
40struct field_ptrs {
41 using array = flecs::array<_::field_ptr, sizeof...(Components)>;
42
43 void populate(const ecs_iter_t *iter) {
44 populate_impl(iter, std::index_sequence_for<Components...>{});
45 }
46
47 void populate_self(const ecs_iter_t *iter) {
48 populate_self_impl(iter, std::index_sequence_for<Components...>{});
49 }
50
51 array fields_;
52
53private:
54 template <typename T>
55 void populate_field(const ecs_iter_t *iter, size_t index) {
56 using A = remove_pointer_t<actual_type_t<T>>;
57 if constexpr (!is_empty_v<A>) {
58 if (iter->row_fields & (1llu << index)) {
59 /* Need to fetch the value with ecs_field_at() */
60 fields_[index].is_row = true;
61 fields_[index].is_ref = true;
62 fields_[index].index = static_cast<int8_t>(index);
63 } else {
64 /* Use base field, as the matched (derived) component can be
65 * larger than A when matched through component inheritance. */
66 fields_[index].ptr = ecs_base_field_w_size(iter, sizeof(A),
67 static_cast<int8_t>(index));
68 fields_[index].is_ref = iter->sources[index] != 0;
69 }
70
71 fields_[index].stride = static_cast<int32_t>(
72 ecs_field_stride(iter, static_cast<int8_t>(index)));
73 }
74 }
75
76 template <typename T>
77 void populate_self_field(const ecs_iter_t *iter, size_t index) {
78 (void)iter; (void)index;
79
80 using A = remove_pointer_t<actual_type_t<T>>;
81 if constexpr (!is_empty_v<A>) {
82 fields_[index].ptr = ecs_field_w_size(iter, sizeof(A),
83 static_cast<int8_t>(index));
84 fields_[index].is_ref = false;
85 }
86 }
87
88 template <size_t... Is>
89 void populate_impl(const ecs_iter_t *iter, std::index_sequence<Is...>) {
90 (void)iter;
91 (populate_field<Components>(iter, Is), ...);
92 }
93
94 template <size_t... Is>
95 void populate_self_impl(const ecs_iter_t *iter, std::index_sequence<Is...>) {
96 (void)iter;
97 (populate_self_field<Components>(iter, Is), ...);
98 }
99};
100
101struct delegate { };
102
103template <typename T, bool Ref = false>
104struct each_field {
105 const flecs::iter_t *iter_;
106 _::field_ptr& field_;
107 size_t row_;
108
109 each_field(const flecs::iter_t *iter, _::field_ptr& field, size_t row)
110 : iter_(iter), field_(field), row_(row) { }
111
112 decltype(auto) get_row() {
113 using A = actual_type_t<T>;
114 if constexpr (is_empty<A>::value && !is_pointer<T>::value) {
115 return T(A());
116 } else {
117 using E = remove_pointer_t<A>;
118 size_t row = row_;
119 if constexpr (Ref) {
120 if (field_.is_ref) {
121 row = 0;
122 }
123 if (field_.is_row) {
124 field_.ptr = ecs_field_at_w_size(iter_, sizeof(E),
125 field_.index, static_cast<int32_t>(row_));
126 }
127 }
128
129 if constexpr (is_pointer<T>::value) {
130 if (!field_.ptr) {
131 return static_cast<A>(nullptr);
132 }
133 }
134
135 E *elem;
136 if constexpr (Ref) {
137 /* Fields matched through component inheritance store a derived
138 * component, which has its own (larger) stride. */
139 elem = static_cast<E*>(ECS_OFFSET(field_.ptr,
140 row * static_cast<size_t>(field_.stride)));
141 } else {
142 elem = &static_cast<E*>(field_.ptr)[row];
143 }
144
145 if constexpr (is_pointer<T>::value) {
146 return elem;
147 } else if constexpr (is_actual<T>::value) {
148 return *elem;
149 } else {
150 return T(*elem);
151 }
152 }
153 }
154};
155
156// Type that handles passing components to each callbacks.
157template <typename Func, typename ... Components>
158struct each_delegate : public delegate {
159 using Terms = typename field_ptrs<Components ...>::array;
160
161 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
162 explicit each_delegate(Func&& func) noexcept
163 : func_(FLECS_MOV(func)) { }
164
165 explicit each_delegate(const Func& func) noexcept
166 : func_(func) { }
167
168 // Invoke object directly. This operation is useful when the calling
169 // function has just constructed the delegate, such as what happens when
170 // iterating a query.
171 void invoke(ecs_iter_t *iter) const {
172 invoke_until<false>(iter);
173 }
174
175 // Static function that can be used as callback for systems/observers.
176 static void run(ecs_iter_t *iter) {
177 auto self = static_cast<const each_delegate*>(iter->callback_ctx);
178 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, nullptr);
179 self->invoke(iter);
180 }
181
182 // Static function that can be used as callback for systems/observers.
183 // Different from run() in that it loops the iterator.
184 static void run_each(ecs_iter_t *iter) {
185 auto self = static_cast<const each_delegate*>(iter->run_ctx);
186 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, nullptr);
187 while (iter->next(iter)) {
188 self->invoke(iter);
189 }
190 }
191
192 // Create instance of delegate.
193 static each_delegate* make(const Func& func) {
194 return FLECS_NEW(each_delegate)(func);
195 }
196
197 // Function that can be used as callback to free delegate.
198 static void destruct(void *obj) {
199 _::free_obj<each_delegate>(obj);
200 }
201
202 template <component_binding_ctx::Hook Hook>
203 static void run_hook(ecs_iter_t *iter) {
204 auto ctx = static_cast<component_binding_ctx*>(iter->callback_ctx);
205 auto self = static_cast<const each_delegate*>(ctx->callbacks[Hook]);
206 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, nullptr);
207 iter->callback_ctx = ctx->callbacks[Hook];
208 self->template invoke_until<false,
209 Hook != component_binding_ctx::OnReplace>(iter);
210 }
211
212protected:
213 template <bool Find, bool Shared = true>
214 flecs::entity invoke_until(ecs_iter_t *iter) const {
215 field_ptrs<Components...> terms;
216 iter->flags |= EcsIterCppEach;
217 if (Shared && (iter->ref_fields | iter->up_fields |
218 (iter->flags & EcsIterComponentInheritance)))
219 {
220 terms.populate(iter);
221 return invoke_rows<Find, true>(iter, terms.fields_,
222 std::index_sequence_for<Components...>{});
223 } else {
224 terms.populate_self(iter);
225 return invoke_rows<Find, false>(iter, terms.fields_,
226 std::index_sequence_for<Components...>{});
227 }
228 }
229
230private:
231 template <typename... Args>
232 static decltype(auto) invoke_callback(
233 ecs_iter_t *iter, const Func& func, size_t i, Args&&... args)
234 {
235 if constexpr (std::is_invocable_v<const Func&, flecs::entity, Args...>) {
237 "query does not return entities ($this variable is not populated)");
238 return func(flecs::entity(iter->stage, iter->entities[i]),
239 FLECS_FWD(args)...);
240 } else if constexpr (std::is_invocable_v<
241 const Func&, flecs::iter&, size_t&, Args...>)
242 {
243 flecs::iter it(iter);
244 return func(it, i, FLECS_FWD(args)...);
245 } else {
246 return func(FLECS_FWD(args)...);
247 }
248 }
249
250 template <bool Find, bool Ref, size_t... I>
251 flecs::entity invoke_rows(ecs_iter_t *iter, Terms& terms,
252 std::index_sequence<I...>) const
253 {
254 ECS_TABLE_LOCK(iter->stage, iter->table);
255 size_t count = static_cast<size_t>(iter->count);
256 if constexpr (Find) {
257 if constexpr (!std::is_invocable_v<const Func&, flecs::entity,
258 decltype(std::declval<each_field<
259 remove_reference_t<Components>, Ref>>().get_row())...>)
260 {
261 if (!count) {
262 count = 1;
263 }
264 }
265 } else if (!count && !iter->table) {
266 count = 1;
267 }
268
269 flecs::entity result;
270 for (size_t i = 0; i < count; i ++) {
271 if constexpr (Find) {
272 if (invoke_callback(iter, func_, i,
273 each_field<remove_reference_t<Components>, Ref>(
274 iter, terms[I], i).get_row()...))
275 {
276 result = flecs::entity(iter->stage, iter->entities[i]);
277 break;
278 }
279 } else {
280 invoke_callback(iter, func_, i,
281 each_field<remove_reference_t<Components>, Ref>(
282 iter, terms[I], i).get_row()...);
283 }
284 }
285 ECS_TABLE_UNLOCK(iter->stage, iter->table);
286 return result;
287 }
288
289public:
290 Func func_;
291};
292
293template <typename Func, typename T>
294struct validate_delegate : public delegate {
295 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
296 explicit validate_delegate(Func&& func) noexcept
297 : func_(FLECS_MOV(func)) { }
298
299 explicit validate_delegate(const Func& func) noexcept
300 : func_(func) { }
301
302 static bool run(ecs_world_t *world, ecs_entity_t entity, void *ptr) {
305 ecs_assert(h != nullptr, ECS_INTERNAL_ERROR, nullptr);
306 auto ctx = static_cast<component_binding_ctx*>(h->binding_ctx);
307 ecs_assert(ctx != nullptr, ECS_INTERNAL_ERROR, nullptr);
308 auto self = static_cast<const validate_delegate*>(ctx->callbacks[component_binding_ctx::OnValidate]);
309 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, nullptr);
310 return self->func_(
311 flecs::entity(world, entity), *static_cast<T*>(ptr));
312 }
313
314 Func func_;
315};
316
317template <typename Func, typename ... Components>
318struct find_delegate : each_delegate<Func, Components...> {
319 using each_delegate<Func, Components...>::each_delegate;
320
321 flecs::entity invoke(ecs_iter_t *iter) const {
322 return this->template invoke_until<true>(iter);
323 }
324};
325
326////////////////////////////////////////////////////////////////////////////////
327//// Utility class to invoke a system iterate action
328////////////////////////////////////////////////////////////////////////////////
329
330template <typename Func>
331struct run_delegate : delegate {
332 template < if_not_t< is_same< decay_t<Func>, decay_t<Func>& >::value > = 0>
333 explicit run_delegate(Func&& func) noexcept
334 : func_(FLECS_MOV(func)) { }
335
336 explicit run_delegate(const Func& func) noexcept
337 : func_(func) { }
338
339 // Invoke object directly. This operation is useful when the calling
340 // function has just constructed the delegate, such as what happens when
341 // iterating a query.
342 void invoke(ecs_iter_t *iter) const {
343 flecs::iter it(iter);
344 iter->flags &= ~EcsIterIsValid;
345 func_(it);
346 }
347
348 // Static function that can be used as callback for systems/observers.
349 static void run(ecs_iter_t *iter) {
350 auto self = static_cast<const run_delegate*>(iter->run_ctx);
351 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, nullptr);
352 self->invoke(iter);
353 }
354
355 Func func_;
356};
357
358
359////////////////////////////////////////////////////////////////////////////////
360//// Utility class to invoke an entity observer delegate
361////////////////////////////////////////////////////////////////////////////////
362
363template <typename Func, typename Event = void>
364struct entity_observer_delegate : delegate {
365 template <typename F>
366 explicit entity_observer_delegate(F&& func) noexcept
367 : func_(FLECS_FWD(func)) { }
368
369 static void run(ecs_iter_t *iter) {
370 auto self = static_cast<const entity_observer_delegate*>(iter->callback_ctx);
371 ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, nullptr);
372 if constexpr (std::is_void_v<Event> || is_empty_v<Event>) {
373 self->invoke(iter);
374 } else {
376 "entity observer invoked without payload");
377 self->invoke(iter, *static_cast<Event*>(iter->param));
378 }
379 }
380
381private:
382 template <typename... Args>
383 void invoke(ecs_iter_t *iter, Args&... args) const {
384 if constexpr (std::is_invocable_v<const Func&, flecs::entity, Args&...>) {
385 func_(flecs::entity(iter->stage, ecs_field_src(iter, 0)), args...);
386 } else {
387 func_(args...);
388 }
389 }
390
391 Func func_;
392};
393
394////////////////////////////////////////////////////////////////////////////////
395//// Utility to invoke callback on entity if it has components in signature
396////////////////////////////////////////////////////////////////////////////////
397
398template <typename ArgList>
400
401template <typename... Args>
402struct entity_with_delegate_impl<arg_list<Args...>> {
403 using ArrayType = flecs::array<void*, sizeof...(Args)>;
404 using IdArray = flecs::array<id_t, sizeof...(Args)>;
405
406 static bool get_ptrs(world_t *world, entity_t entity, const ecs_record_t *record,
407 ecs_table_t *table, IdArray& ids, ArrayType& ptrs)
408 {
409 ecs_assert(table != nullptr, ECS_INTERNAL_ERROR, nullptr);
410 const world_t *real_world = ecs_get_world(world);
411 size_t i = 0;
412 for (auto id : ids) {
413 int32_t column = ecs_table_get_column_index(real_world, table, id);
414 void *ptr = column == -1 ? ecs_get_mut_id(world, entity, id) :
415 flecs_entity_get_column(record, column, 0);
416 if (!ptr) {
417 return false;
418 }
419 ptrs[i ++] = ptr;
420 }
421 return true;
422 }
423
424 template <typename Func>
425 static bool invoke_get(world_t *world, entity_t entity, const Func& func) {
426 ecs_record_t *record = flecs_entities_get(ecs_get_world(world), entity);
427 if (!record || !record->table) {
428 return false;
429 }
430 auto table = record->table;
431 ECS_TABLE_LOCK(world, table);
432 IdArray ids ({ _::type<Args>::id(world)... });
433 ArrayType ptrs;
434 bool found = get_ptrs(world, entity, record, table, ids, ptrs);
435 if (found) {
436 invoke_callback(func, ptrs, std::index_sequence_for<Args...>{});
437 }
438 ECS_TABLE_UNLOCK(world, table);
439 return found;
440 }
441
442 template <typename Func>
443 static bool invoke_ensure(world_t *world, entity_t entity, const Func& func) {
444 IdArray ids ({ _::type<Args>::id(world)... });
445 ArrayType ptrs;
446 ecs_table_t *table = nullptr;
447 if (!ecs_is_stage(world)) {
449 ecs_record_t *record = flecs_entities_get(world, entity);
450 ecs_assert(record != nullptr, ECS_INVALID_PARAMETER, nullptr);
451 flecs_add_ids(world, entity, ids.ptr(), static_cast<int32_t>(sizeof...(Args)));
452 table = record->table;
453 if (!get_ptrs(world, entity, record, table, ids, ptrs)) {
455 }
456 ECS_TABLE_LOCK(world, table);
457 } else {
458 size_t i = 0;
459 ((ptrs[i] = ecs_ensure_id(world, entity, ids[i], sizeof(Args)), i ++), ...);
460 }
461 invoke_callback(func, ptrs, std::index_sequence_for<Args...>{});
462 if (table) {
463 ECS_TABLE_UNLOCK(world, table);
464 }
465 for (auto id : ids) {
467 }
468 return true;
469 }
470
471private:
472 template <typename Func, size_t... I>
473 static void invoke_callback(const Func& func, ArrayType& ptrs, std::index_sequence<I...>) {
474 func(*static_cast<base_arg_type_t<Args>*>(ptrs[I])...);
475 }
476};
477
478template <typename Func, typename = int>
480 static_assert(arity<Func>::value > 0, "function must have at least one argument");
481};
482
483/** Strip references from each-callback argument types. */
484template <typename ArgList, bool Normalize>
486
487template <bool Normalize, typename ... Args>
488struct each_normalize_args<arg_list<Args...>, Normalize> {
489 using type = arg_list<conditional_t<Normalize, remove_reference_t<Args>, Args>...>;
490};
491
492/** Extract the component argument list from an each-callback signature.
493 * Skips a leading flecs::entity or flecs::iter argument when present. */
494template <typename ArgList, typename = int, bool Normalize = true>
497};
498
499template <bool Normalize, typename First, typename ... Args>
500struct each_callback_args<arg_list<First, Args...>,
501 if_t<is_same<decay_t<First>, flecs::entity>::value>, Normalize> {
502 using type = typename each_normalize_args<arg_list<Args...>, Normalize>::type;
503};
504
505template <bool Normalize, typename First, typename Second, typename ... Args>
506struct each_callback_args<arg_list<First, Second, Args...>,
507 if_t<is_same<decay_t<First>, flecs::iter>::value>, Normalize> {
508 using type = typename each_normalize_args<arg_list<Args...>, Normalize>::type;
509};
510
511template <typename Delegate, ecs_iter_action_t Action, bool Run,
512 typename Desc, typename Func>
513void set_callback(Desc& desc, Func&& func) {
514 (Run ? desc.run : desc.callback) = Action;
515 (Run ? desc.run_ctx : desc.callback_ctx) = FLECS_NEW(Delegate)(FLECS_FWD(func));
516 (Run ? desc.run_ctx_free : desc.callback_ctx_free) = free_obj<Delegate>;
517}
518
519template <bool Run, typename Desc, typename Func, typename... Components>
520void set_each_callback(Desc& desc, Func&& func, arg_list<Components...>) {
521 using Delegate = each_delegate<decay_t<Func>, Components...>;
522 set_callback<Delegate, Run ? Delegate::run_each : Delegate::run, Run>(
523 desc, FLECS_FWD(func));
524}
525
526} // namespace _
527
528/** Delegate type for each callbacks.
529 * Experimental: allows using the each delegate for use cases outside of Flecs.
530 *
531 * @tparam Func The callback function type.
532 * @tparam Args The component argument types.
533 */
534template <typename Func, typename ... Args>
536
537} // namespace flecs
_::each_delegate< typename std::decay< Func >::type, Args... > delegate
Delegate type for each callbacks.
Definition delegate.hpp:535
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:473
#define ECS_INVALID_OPERATION
Invalid operation error code.
Definition log.h:696
#define ECS_INVALID_PARAMETER
Invalid parameter error code.
Definition log.h:698
#define ECS_INTERNAL_ERROR
Internal error code.
Definition log.h:708
#define ecs_abort(error_code,...)
Abort.
Definition log.h:464
bool ecs_is_stage(const ecs_world_t *world)
Test whether a world object is a stage.
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:396
struct ecs_world_t ecs_world_t
A world is the container for all ECS data and supporting features.
Definition flecs.h:440
struct ecs_record_t ecs_record_t
Information about an entity, like its table and row.
Definition flecs.h:505
struct ecs_table_t ecs_table_t
A table stores entities and components for a specific type.
Definition flecs.h:446
ecs_iter_t iter_t
Iterator type.
Definition c_types.hpp:28
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
typename base_arg_type< T >::type base_arg_type_t
Convenience alias for base_arg_type.
Definition pair.hpp:155
void(*) ecs_ctx_free_t(void *ctx)
Function to clean up context data.
Definition flecs.h:661
void(*) ecs_iter_action_t(ecs_iter_t *it)
Function prototype for iterables.
Definition flecs.h:592
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.
size_t ecs_field_stride(const ecs_iter_t *it, int8_t index)
Return the field storage stride.
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_base_field_w_size(const ecs_iter_t *it, size_t size, int8_t index)
Get data for a field matched through component inheritance.
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.
const ecs_world_t * ecs_get_world(const ecs_poly_t *poly)
Get the world from a poly.
#define flecs_poly_is(object, type)
Test if a pointer is of the specified type.
Definition flecs.h:2512
Int to enum.
Definition component.hpp:18
Iterator.
Definition flecs.h:1184
void * binding_ctx
Language binding context.
Definition flecs.h:1041
Extract the component argument list from an each-callback signature.
Definition delegate.hpp:495
Strip references from each-callback argument types.
Definition delegate.hpp:485
Array class (primary template, disabled).
Definition array.hpp:47
Entity.
Definition entity.hpp:30
Wrapper class around a field.
Definition field.hpp:61
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:67
void * param()
Access param.
Definition iter.hpp:177
size_t count() const
Get the number of entities to iterate over.
Definition iter.hpp:112
flecs::field< const flecs::entity_t > entities() const
Get read-only access to entity IDs.
Definition iter.hpp:404
bool next()
Progress iterator.
Definition iter.hpp:465
Table.
Definition table.hpp:23
table()
Default constructor.
Definition table.hpp:25
The world.
Definition world.hpp:129
enable_if_t< V, int > if_t
Convenience enable_if alias using int as default type.
Definition utils.hpp:168