Skip to content
Flecs v4.1
iter.hpp
Go to the documentation of this file.
1/**
2 * @file addons/cpp/iter.hpp
3 * @brief Wrapper classes for ecs_iter_t and component arrays.
4 */
5
6#pragma once
7
8/**
9 * @defgroup cpp_iterator Iterators
10 * @ingroup cpp_core
11 * Iterator operations.
12 *
13 * @{
14 */
15
16namespace flecs
17{
18
19////////////////////////////////////////////////////////////////////////////////
20
21namespace _ {
22
23////////////////////////////////////////////////////////////////////////////////
24
25/** Iterate over an integer range (used to iterate over entity range).
26 *
27 * @tparam T The value type of the iterator.
28 */
29template <typename T>
30struct range_iterator
31{
32 explicit range_iterator(T value)
33 : value_(value){}
34
35 bool operator!=(range_iterator const& other) const
36 {
37 return value_ != other.value_;
38 }
39
40 T const& operator*() const
41 {
42 return value_;
43 }
44
45 range_iterator& operator++()
46 {
47 ++value_;
48 return *this;
49 }
50
51private:
52 T value_;
53};
54
55} // namespace _
56
57} // namespace flecs
58
59namespace flecs
60{
61
62////////////////////////////////////////////////////////////////////////////////
63
64/** Class for iterating over query results.
65 *
66 * @ingroup cpp_iterator
67 */
68struct iter {
69private:
70 using row_iterator = _::range_iterator<size_t>;
71
72public:
73 /** Construct iterator from C iterator object.
74 * This operation is typically not invoked directly by the user.
75 *
76 * @param it Pointer to C iterator.
77 */
78 iter(ecs_iter_t *it) : iter_(it) { }
79
80 /** Get an iterator to the beginning of the entity range. */
81 row_iterator begin() const {
82 return row_iterator(0);
83 }
84
85 /** Get an iterator to the end of the entity range. */
86 row_iterator end() const {
87 return row_iterator(static_cast<size_t>(iter_->count));
88 }
89
90 /** Get the system entity associated with the iterator. */
91 flecs::entity system() const;
92
93 /** Get the event entity associated with the iterator. */
94 flecs::entity event() const;
95
96 /** Get the event ID associated with the iterator. */
97 flecs::id event_id() const;
98
99 /** Get the world associated with the iterator. */
100 flecs::world world() const;
101
102 /** Get a pointer to the underlying C iterator object. */
103 const flecs::iter_t* c_ptr() const {
104 return iter_;
105 }
106
107 /** Get the number of entities to iterate over.
108 *
109 * @return The number of entities in the current result.
110 */
111 size_t count() const {
112 ecs_check(iter_->flags & EcsIterIsValid, ECS_INVALID_PARAMETER,
113 "operation invalid before calling next()");
114 return static_cast<size_t>(iter_->count);
115 error:
116 return 0;
117 }
118
119 /** Get the time elapsed since the last frame.
120 *
121 * @return The delta time.
122 */
124 return iter_->delta_time;
125 }
126
127 /** Get the time elapsed since the last system invocation.
128 *
129 * @return The delta system time.
130 */
132 return iter_->delta_system_time;
133 }
134
135 /** Get the type of the iterated table. */
136 flecs::type type() const;
137
138 /** Get the table for the current iterator result. */
139 flecs::table table() const;
140
141 /** Get the other table for the current iterator result.
142 * This is used for move operations where data is moved from one table
143 * to another.
144 */
146
147 /** Get the table range for the current iterator result. */
148 flecs::table_range range() const;
149
150 /** Access ctx.
151 * ctx contains the context pointer assigned to a system.
152 */
153 void* ctx() {
154 return iter_->ctx;
155 }
156
157 /** Access ctx.
158 * ctx contains the context pointer assigned to a system.
159 */
160 template <typename T>
161 T* ctx() {
162 return static_cast<T*>(iter_->ctx);
163 }
164
165 /** Access param.
166 * param contains the pointer passed to the param argument of system::run().
167 */
168 void* param() {
169 return iter_->param;
170 }
171
172 /** Access param.
173 * param contains the pointer passed to the param argument of system::run().
174 */
175 template <typename T>
176 T* param() {
177 /* TODO: type check */
178 return static_cast<T*>(iter_->param);
179 }
180
181 /** Obtain a mutable handle to the entity being iterated over.
182 *
183 * @param row Row being iterated over.
184 * @return The entity at the specified row.
185 */
186 flecs::entity entity(size_t row) const;
187
188 /** Return whether the field is matched on self.
189 *
190 * @param index The field index.
191 * @return True if the field is matched on self, false if not.
192 */
193 bool is_self(int8_t index) const {
194 return ecs_field_is_self(iter_, index);
195 }
196
197 /** Return whether the field is set.
198 *
199 * @param index The field index.
200 * @return True if the field is set, false if not.
201 */
202 bool is_set(int8_t index) const {
203 return ecs_field_is_set(iter_, index);
204 }
205
206 /** Return whether the field is readonly.
207 *
208 * @param index The field index.
209 * @return True if the field is readonly, false if not.
210 */
211 bool is_readonly(int8_t index) const {
212 return ecs_field_is_readonly(iter_, index);
213 }
214
215 /** Number of fields in the iterator.
216 *
217 * @return The number of fields.
218 */
219 int32_t field_count() const {
220 return iter_->field_count;
221 }
222
223 /** Size of the field data type.
224 *
225 * @param index The field index.
226 * @return The size of the field data type.
227 */
228 size_t size(int8_t index) const {
229 return ecs_field_size(iter_, index);
230 }
231
232 /** Obtain the field source (0 if This).
233 *
234 * @param index The field index.
235 * @return The source entity for the field.
236 */
237 flecs::entity src(int8_t index) const;
238
239 /** Obtain the ID matched for the field.
240 *
241 * @param index The field index.
242 * @return The ID matched for the field.
243 */
244 flecs::id id(int8_t index) const;
245
246 /** Obtain the pair ID matched for the field.
247 * This operation will fail if the ID is not a pair.
248 *
249 * @param index The field index.
250 * @return The pair ID matched for the field.
251 */
252 flecs::id pair(int8_t index) const;
253
254 /** Obtain the column index for the field.
255 *
256 * @param index The field index.
257 * @return The column index for the field.
258 */
259 int32_t column_index(int8_t index) const {
260 return ecs_field_column(iter_, index);
261 }
262
263 /** Obtain the term that triggered an observer.
264 *
265 * @return The index of the term that triggered the observer.
266 */
267 int8_t term_index() const {
268 return iter_->term_index;
269 }
270
271 /** Get read-only access to field data.
272 * If the specified field index does not match with the provided type, the
273 * function will assert.
274 *
275 * This function should not be used in each() callbacks, unless it is to
276 * access a shared field. For access to non-shared fields in each(), use
277 * field_at().
278 *
279 * @tparam T Type of the field.
280 * @param index The field index.
281 * @return The field data.
282 */
283 template <typename T, typename A = actual_type_t<T>, if_t<is_const_v<T>> = 0>
284 flecs::field<A> field(int8_t index) const;
285
286 /** Get read/write access to field data.
287 * If the matched ID for the specified field does not match with the provided
288 * type or if the field is readonly, the function will assert.
289 *
290 * This function should not be used in each() callbacks, unless it is to
291 * access a shared field. For access to non-shared fields in each(), use
292 * field_at().
293 *
294 * @tparam T Type of the field.
295 * @param index The field index.
296 * @return The field data.
297 */
298 template <typename T, typename A = actual_type_t<T>, if_not_t<is_const_v<T>> = 0>
299 flecs::field<A> field(int8_t index) const;
300
301 /** Get unchecked access to field data.
302 * Unchecked access is required when a system does not know the type of a
303 * field at compile time.
304 *
305 * This function should not be used in each() callbacks, unless it is to
306 * access a shared field. For access to non-shared fields in each(), use
307 * field_at().
308 *
309 * @param index The field index.
310 */
311 flecs::untyped_field field(int8_t index) const {
312 ecs_assert(!(iter_->flags & EcsIterCppEach) ||
313 ecs_field_src(iter_, index) != 0, ECS_INVALID_OPERATION,
314 "cannot .field from .each, use .field_at(%d, row) instead", index);
315 return get_unchecked_field(index);
316 }
317
318 /** Get pointer to field at row.
319 * This function may be used to access shared fields when row is set to 0.
320 *
321 * @param index The field index.
322 * @param row The row index.
323 * @return Pointer to the field value at the specified row.
324 */
325 void* field_at(int8_t index, size_t row) const {
326 if (iter_->row_fields & (1llu << index)) {
327 return get_unchecked_field_at(index, row)[0];
328 } else {
329 return get_unchecked_field(index)[row];
330 }
331 }
332
333 /** Get const reference to field at row.
334 * This function may be used to access shared fields when row is set to 0.
335 *
336 * @tparam T Type of the field (must be const-qualified).
337 * @param index The field index.
338 * @param row The row index.
339 * @return Const reference to the field value at the specified row.
340 */
341 template <typename T, typename A = actual_type_t<T>, if_t< is_const_v<T> > = 0>
342 const A& field_at(int8_t index, size_t row) const {
343 if (iter_->row_fields & (1llu << index)) {
344 return get_field_at<A>(index, row)[0];
345 } else {
346 return get_field<A>(index)[row];
347 }
348 }
349
350 /** Get mutable reference to field at row.
351 * This function may be used to access shared fields when row is set to 0.
352 *
353 * @tparam T Type of the field (must not be const-qualified).
354 * @param index The field index.
355 * @param row The row index.
356 * @return Mutable reference to the field value at the specified row.
357 */
358 template <typename T, typename A = actual_type_t<T>, if_not_t< is_const_v<T> > = 0>
359 A& field_at(int8_t index, size_t row) const {
360 ecs_assert(!ecs_field_is_readonly(iter_, index),
361 ECS_ACCESS_VIOLATION, nullptr);
362 if (iter_->row_fields & (1llu << index)) {
363 return get_field_at<A>(index, row)[0];
364 } else {
365 return get_field<A>(index)[row];
366 }
367 }
368
369 /** Get read-only access to entity IDs.
370 *
371 * @return The entity IDs.
372 */
375 iter_->entities, static_cast<size_t>(iter_->count), false);
376 }
377
378#ifdef FLECS_CACHED_QUERIES
379 /** Check if the current table has changed since the last iteration.
380 * Can only be used when iterating queries and/or systems.
381 *
382 * @return True if the table has changed.
383 */
384 bool changed() {
385 return ecs_iter_changed(iter_);
386 }
387
388 /** Skip current table.
389 * This indicates to the query that the data in the current table is not
390 * modified. By default, iterating a table with a query will mark the
391 * iterated components as dirty if they are annotated with InOut or Out.
392 *
393 * When this operation is invoked, the components of the current table will
394 * not be marked dirty. */
395 void skip() {
396 ecs_iter_skip(iter_);
397 }
398
399 /** Return the group ID for the current table (grouped queries only).
400 *
401 * @return The group ID.
402 */
403 uint64_t group_id() const {
404 return ecs_iter_get_group(iter_);
405 }
406#endif
407
408#ifdef FLECS_QUERY_PLANS
409 /** Get value of variable by ID.
410 * Get value of a query variable for current result.
411 *
412 * @param var_id The variable ID.
413 * @return The variable value.
414 */
415 flecs::entity get_var(int var_id) const;
416
417 /** Get value of variable by name.
418 * Get value of a query variable for current result.
419 *
420 * @param name The variable name.
421 * @return The variable value.
422 */
423 flecs::entity get_var(const char *name) const;
424#endif
425
426 /** Progress iterator.
427 * This operation should only be called from a context where the iterator is
428 * not being progressed automatically. An example of a valid context is
429 * inside of a run() callback. An example of an invalid context is inside of
430 * an each() callback.
431 *
432 * @return True if there is more data to iterate, false if not.
433 */
434 bool next() {
435 if (iter_->flags & EcsIterIsValid && iter_->table) {
436 ECS_TABLE_UNLOCK(iter_->world, iter_->table);
437 }
438 bool result = iter_->next(iter_);
439 iter_->flags |= EcsIterIsValid;
440 if (result && iter_->table) {
441 ECS_TABLE_LOCK(iter_->world, iter_->table);
442 }
443 return result;
444 }
445
446 /** Forward to each().
447 * If a system has an each() callback registered, this operation will forward
448 * the current iterator to the each() callback.
449 */
450 void each() {
451 iter_->callback(iter_);
452 }
453
454 /** Iterate targets for pair field.
455 *
456 * @param index The field index.
457 * @param func Callback invoked for each target.
458 */
459 template <typename Func>
460 void targets(int8_t index, const Func& func);
461
462 /** Free iterator resources.
463 * This operation only needs to be called when the iterator is not iterated
464 * until completion (e.g., the last call to next() did not return false).
465 *
466 * Failing to call this operation on an unfinished iterator will throw a
467 * fatal LEAK_DETECTED error.
468 *
469 * @see ecs_iter_fini()
470 */
471 void fini() {
472 if (iter_->flags & EcsIterIsValid && iter_->table) {
473 ECS_TABLE_UNLOCK(iter_->world, iter_->table);
474 }
475 ecs_iter_fini(iter_);
476 }
477
478private:
479 /* Get field, check if correct type is used. */
480 template <typename T, typename A = actual_type_t<T>>
481 flecs::field<T> get_field(int8_t index) const {
482
483#ifndef FLECS_NDEBUG
484 ecs_entity_t term_id = ecs_field_id(iter_, index);
485 ecs_assert(ECS_HAS_ID_FLAG(term_id, PAIR) ||
486 term_id == _::type<T>::id(iter_->world),
487 ECS_COLUMN_TYPE_MISMATCH, nullptr);
488#endif
489
490 size_t count;
491 bool is_shared = !ecs_field_is_self(iter_, index);
492
493 /* If a shared field is retrieved with field(), there will only be a
494 * single value. Ensure that the application does not accidentally read
495 * out of bounds. */
496 if (is_shared) {
497 count = 1;
498 } else {
499 /* If field is owned, there will be as many values as there are
500 * entities. */
501 count = static_cast<size_t>(iter_->count);
502 }
503
504 return flecs::field<A>(
505 static_cast<T*>(ecs_field_w_size(iter_, sizeof(A), index)),
506 count, is_shared);
507 }
508
509 /* Get field, check if correct type is used. */
510 template <typename T, typename A = actual_type_t<T>>
511 flecs::field<T> get_field_at(int8_t index, int32_t row) const {
512
513#ifndef FLECS_NDEBUG
514 ecs_entity_t term_id = ecs_field_id(iter_, index);
515 ecs_assert(ECS_HAS_ID_FLAG(term_id, PAIR) ||
516 term_id == _::type<T>::id(iter_->world),
517 ECS_COLUMN_TYPE_MISMATCH, nullptr);
518#endif
519
520 return flecs::field<A>(
521 static_cast<T*>(ecs_field_at_w_size(iter_, sizeof(A), index, row)),
522 1, false);
523 }
524
525 flecs::untyped_field get_unchecked_field(int8_t index) const {
526 size_t count;
527 size_t size = ecs_field_size(iter_, index);
528 bool is_shared = !ecs_field_is_self(iter_, index);
529
530 /* If a shared field is retrieved with field(), there will only be a
531 * single value. Ensure that the application does not accidentally read
532 * out of bounds. */
533 if (is_shared) {
534 count = 1;
535 } else {
536 /* If field is owned, there will be as many values as there are
537 * entities. */
538 count = static_cast<size_t>(iter_->count);
539 }
540
541 return flecs::untyped_field(
542 ecs_field_w_size(iter_, size, index), size, count, is_shared);
543 }
544
545 flecs::untyped_field get_unchecked_field_at(int8_t index, size_t row) const {
546 size_t size = ecs_field_size(iter_, index);
547 return flecs::untyped_field(
548 ecs_field_at_w_size(iter_, size, index, static_cast<int32_t>(row)),
549 size, 1, false);
550 }
551
552 flecs::iter_t *iter_;
553};
554
555} // namespace flecs
556
557/** @} */
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:473
#define ECS_ACCESS_VIOLATION
Access violation error code.
Definition log.h:719
#define ECS_INVALID_OPERATION
Invalid operation error code.
Definition log.h:669
#define ECS_COLUMN_TYPE_MISMATCH
Column type mismatch error code.
Definition log.h:727
#define ECS_INVALID_PARAMETER
Invalid parameter error code.
Definition log.h:671
#define ecs_check(condition, error_code,...)
Check.
Definition log.h:527
ecs_id_t ecs_entity_t
An entity identifier.
Definition flecs.h:395
ecs_iter_t iter_t
Iterator type.
Definition c_types.hpp:28
ecs_entity_t ecs_field_src(const ecs_iter_t *it, int8_t index)
Return the field source.
bool ecs_iter_changed(ecs_iter_t *it)
Return whether the current iterator result has changed.
bool ecs_field_is_readonly(const ecs_iter_t *it, int8_t index)
Test whether the field is read-only.
void ecs_iter_fini(ecs_iter_t *it)
Clean up iterator resources.
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.
ecs_id_t ecs_field_id(const ecs_iter_t *it, int8_t index)
Return the ID matched for a field.
bool ecs_field_is_set(const ecs_iter_t *it, int8_t index)
Test whether a field is set.
bool ecs_field_is_self(const ecs_iter_t *it, int8_t index)
Test whether the field is matched on self.
int32_t ecs_field_column(const ecs_iter_t *it, int8_t index)
Return the index of a matched table column.
uint64_t ecs_iter_get_group(const ecs_iter_t *it)
Return the group ID for the currently iterated result.
void * ecs_field_w_size(const ecs_iter_t *it, size_t size, int8_t index)
Get data for a field.
size_t ecs_field_size(const ecs_iter_t *it, int8_t index)
Return the field type size.
#define ecs_ftime_t
Customizable precision for scalar time values.
Definition flecs.h:59
void ecs_iter_skip(ecs_iter_t *it)
Skip a table while iterating.
Int to enum.
Definition component.hpp:18
Iterator.
Definition flecs.h:1192
ecs_world_t * world
The world.
Definition flecs.h:1194
int32_t count
Number of entities to iterate.
Definition flecs.h:1199
Iterate over an integer range (used to iterate over entity range).
Definition iter.hpp:31
Entity.
Definition entity.hpp:30
entity()
Default constructor.
Definition entity.hpp:32
Wrapper class around a field.
Definition field.hpp:61
field(T *array, size_t count, bool is_shared=false)
Create a field from a component array.
Definition field.hpp:71
Class that wraps around a flecs::id_t.
Definition decl.hpp:27
size_t size(int8_t index) const
Size of the field data type.
Definition iter.hpp:228
flecs::field< A > field(int8_t index) const
Get read/write access to field data.
const A & field_at(int8_t index, size_t row) const
Get const reference to field at row.
Definition iter.hpp:342
bool is_self(int8_t index) const
Return whether the field is matched on self.
Definition iter.hpp:193
int32_t field_count() const
Number of fields in the iterator.
Definition iter.hpp:219
ecs_ftime_t delta_system_time() const
Get the time elapsed since the last system invocation.
Definition iter.hpp:131
int32_t column_index(int8_t index) const
Obtain the column index for the field.
Definition iter.hpp:259
void * param()
Access param.
Definition iter.hpp:168
row_iterator begin() const
Get an iterator to the beginning of the entity range.
Definition iter.hpp:81
row_iterator end() const
Get an iterator to the end of the entity range.
Definition iter.hpp:86
void * field_at(int8_t index, size_t row) const
Get pointer to field at row.
Definition iter.hpp:325
ecs_ftime_t delta_time() const
Get the time elapsed since the last frame.
Definition iter.hpp:123
bool is_readonly(int8_t index) const
Return whether the field is readonly.
Definition iter.hpp:211
flecs::table other_table() const
Get the other table for the current iterator result.
Definition iter.hpp:68
const flecs::iter_t * c_ptr() const
Get a pointer to the underlying C iterator object.
Definition iter.hpp:103
bool changed()
Check if the current table has changed since the last iteration.
Definition iter.hpp:384
int8_t term_index() const
Obtain the term that triggered an observer.
Definition iter.hpp:267
T * ctx()
Access ctx.
Definition iter.hpp:161
void fini()
Free iterator resources.
Definition iter.hpp:471
bool is_set(int8_t index) const
Return whether the field is set.
Definition iter.hpp:202
A & field_at(int8_t index, size_t row) const
Get mutable reference to field at row.
Definition iter.hpp:359
void * ctx()
Access ctx.
Definition iter.hpp:153
void each()
Forward to each().
Definition iter.hpp:450
flecs::untyped_field field(int8_t index) const
Get unchecked access to field data.
Definition iter.hpp:311
flecs::id event_id() const
Get the event ID associated with the iterator.
Definition iter.hpp:22
flecs::entity get_var(int var_id) const
Get value of variable by ID.
Definition iter.hpp:102
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
uint64_t group_id() const
Return the group ID for the current table (grouped queries only).
Definition iter.hpp:403
iter(ecs_iter_t *it)
Construct iterator from C iterator object.
Definition iter.hpp:78
T * param()
Access param.
Definition iter.hpp:176
void targets(int8_t index, const Func &func)
Iterate targets for pair field.
Definition iter.hpp:121
flecs::entity src(int8_t index) const
Obtain the field source (0 if This).
Definition iter.hpp:39
void skip()
Skip current table.
Definition iter.hpp:395
bool next()
Progress iterator.
Definition iter.hpp:434
pair(type &v)
Construct pair from a mutable reference to the storage type.
Definition pair.hpp:45
system()
Default constructor.
Definition impl.hpp:82
Table range.
Definition table.hpp:205
Table.
Definition table.hpp:23
table()
Default constructor.
Definition table.hpp:25
Type class.
Definition type.hpp:21
type()
Default constructor.
Definition type.hpp:23
Unsafe wrapper class around a field.
Definition field.hpp:26
The world.
Definition world.hpp:129
world()
Create a world.
Definition world.hpp:132
flecs::event_builder event(flecs::entity_t evt) const
Create a new event.