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