Skip to content
Flecs v4.1
iterable.hpp
Go to the documentation of this file.
1/**
2 * @file addons/cpp/utils/iterable.hpp
3 * @brief Base class for iterable objects, like queries.
4 */
5
6namespace flecs {
7
8/** Forward declaration of iter_iterable. */
9template <typename ... Components>
10struct iter_iterable;
11
12namespace _ {
13 template <auto Create, ecs_iter_next_action_t Next, typename... Components>
14 struct chained_iterable;
15}
16
17template <typename... Components>
18using page_iterable = _::chained_iterable<ecs_page_iter, ecs_page_next, Components...>;
19
20template <typename... Components>
21using worker_iterable = _::chained_iterable<ecs_worker_iter, ecs_worker_next, Components...>;
22
23/** Base class for iterable query objects. */
24template <typename ... Components>
25struct iterable {
26
27 /** Each iterator.
28 * The "each" iterator accepts a function that is invoked for each matching
29 * entity. The following function signatures are valid:
30 * - func(flecs::entity e, Components& ...)
31 * - func(flecs::iter& it, size_t index, Components& ...)
32 * - func(Components& ...)
33 *
34 * @param func The callback function.
35 */
36 template <typename Func>
37 void each(Func&& func) const {
38 ecs_iter_t it = this->get_iter(nullptr);
39 ecs_iter_next_action_t next = this->next_action();
40 while (next(&it)) {
41 _::each_delegate<Func, Components...>(func).invoke(&it);
42 }
43 }
44
45 /** Run the iterator.
46 * The "run" callback accepts a function that is invoked once for a query
47 * with a valid iterator. The following signature is valid:
48 * - func(flecs::iter&)
49 *
50 * @param func The callback function.
51 */
52 template <typename Func>
53 void run(Func&& func) const {
54 ecs_iter_t it = this->get_iter(nullptr);
55 _::run_delegate<Func>(func).invoke(&it);
56 }
57
58 /** Find the first entity matching a condition.
59 * Return the first entity for which the provided function returns true.
60 *
61 * @param func The predicate function.
62 * @return The first matching entity, or an empty entity if none found.
63 */
64 template <typename Func>
65 flecs::entity find(Func&& func) const {
66 ecs_iter_t it = this->get_iter(nullptr);
67 ecs_iter_next_action_t next = this->next_action();
68
69 flecs::entity result;
70 while (!result && next(&it)) {
71 result = _::find_delegate<Func, Components...>(func).invoke(&it);
72 }
73
74 if (result) {
75 ecs_iter_fini(&it);
76 }
77
78 return result;
79 }
80
81 /** Create an iterator.
82 * Create an iterator object that can be modified before iterating.
83 */
84 iter_iterable<Components...> iter(flecs::world_t *world = nullptr) const;
85
86 /** Create an iterator.
87 * Create an iterator object that can be modified before iterating.
88 */
89 iter_iterable<Components...> iter(flecs::iter& iter) const;
90
91 /** Create an iterator.
92 * Create an iterator object that can be modified before iterating.
93 */
94 iter_iterable<Components...> iter(flecs::entity e) const;
95
96 /** Page iterator.
97 * Create an iterator that limits the returned entities with offset/limit.
98 *
99 * @param offset How many entities to skip.
100 * @param limit The maximum number of entities to return.
101 * @return Iterable that can be iterated with each/iter.
102 */
103 page_iterable<Components...> page(int32_t offset, int32_t limit);
104
105 /** Worker iterator.
106 * Create an iterator that divides the number of matched entities across
107 * a number of resources.
108 *
109 * @param index The index of the current resource.
110 * @param count The total number of resources to divide entities between.
111 * @return Iterable that can be iterated with each/iter.
112 */
113 worker_iterable<Components...> worker(int32_t index, int32_t count);
114
115 /** Return the number of entities matched by the iterable. */
116 int32_t count() const {
117 return this->iter().count();
118 }
119
120 /** Return whether the iterable has any matches. */
121 bool is_true() const {
122 return this->iter().is_true();
123 }
124
125 /** Return the first entity matched by the iterable. */
127 return this->iter().first();
128 }
129
130 template <typename Var, typename Value>
131 iter_iterable<Components...> set_var(Var var, Value value) const {
132 return this->iter().set_var(var, value);
133 }
134
135#ifdef FLECS_CACHED_QUERIES
136 /** Limit results to tables with the specified group ID (grouped queries only). */
137 iter_iterable<Components...> set_group(uint64_t group_id) const {
138 return this->iter().set_group(group_id);
139 }
140
141 /** Limit results to tables with the specified group type (grouped queries only). */
142 template <typename Group>
143 iter_iterable<Components...> set_group() const {
144 return this->iter().template set_group<Group>();
145 }
146#endif
147
148 /** Virtual destructor. */
149 virtual ~iterable() { }
150protected:
151 friend iter_iterable<Components...>;
152 friend page_iterable<Components...>;
153 friend worker_iterable<Components...>;
154
155 virtual ecs_iter_t get_iter(flecs::world_t *stage) const = 0;
156 virtual ecs_iter_next_action_t next_action() const = 0;
157};
158
159/** Iterable adapter for iterating with iter/each/run. */
160template <typename ... Components>
161struct iter_iterable final : iterable<Components...> {
162 /** Construct iter_iterable from an iterable and a world. */
163 template <typename Iterable>
165 {
166 it_ = it->get_iter(world);
167 next_ = it->next_action();
168 ecs_assert(next_ != nullptr, ECS_INTERNAL_ERROR, nullptr);
169 }
170
171 template <typename Var, typename Value>
172 iter_iterable& set_var(Var var, Value value) {
173 int var_id;
174#ifdef FLECS_QUERY_PLANS
175 if constexpr (std::is_convertible_v<Var, const char*>) {
176 var_id = ecs_query_find_var(it_.query, var);
177 } else
178#endif
179 {
180 var_id = var;
181 }
182 ecs_assert(var_id != -1, ECS_INVALID_PARAMETER, "query variable was not found");
183 if constexpr (std::is_convertible_v<Value, flecs::entity_t>) {
184 ecs_iter_set_var(&it_, var_id, value);
185 } else if constexpr (std::is_convertible_v<Value, flecs::table_t*> &&
186 !std::is_base_of_v<flecs::table_range, Value>)
187 {
188 ecs_iter_set_var_as_table(&it_, var_id, value);
189 } else {
190 ecs_table_range_t range;
191 if constexpr (std::is_same_v<Value, ecs_table_range_t>) {
192 range = value;
193 } else {
194 range = {value.get_table(), value.offset(), value.count()};
195 }
196 ecs_iter_set_var_as_range(&it_, var_id, &range);
197 }
198 return *this;
199 }
200
201# ifdef FLECS_JSON
203# endif
204
205 /** Return the total number of entities in the result. */
206 int32_t count() {
207 int32_t result = 0;
208 while (next_(&it_)) {
209 result += it_.count;
210 }
211 return result;
212 }
213
214 /** Return whether the iterator yields at least one result. */
215 bool is_true() {
216 bool result = next_(&it_);
217 if (result) {
218 ecs_iter_fini(&it_);
219 }
220 return result;
221 }
222
223 /** Return the first matching entity. */
225 while (next_(&it_)) {
226 if (it_.count) {
227 flecs::entity result(it_.stage, it_.entities[0]);
228 ecs_iter_fini(&it_);
229 return result;
230 }
231 }
232 return flecs::entity();
233 }
234
235#ifdef FLECS_CACHED_QUERIES
236 /** Limit results to tables with the specified group ID (grouped queries only). */
237 iter_iterable<Components...>& set_group(uint64_t group_id) {
238 ecs_iter_set_group(&it_, group_id);
239 return *this;
240 }
241
242 /** Limit results to tables with the specified group type (grouped queries only). */
243 template <typename Group>
244 iter_iterable<Components...>& set_group() {
245 ecs_iter_set_group(&it_, _::type<Group>().id(it_.world));
246 return *this;
247 }
248#endif
249
250protected:
251 ecs_iter_t get_iter(flecs::world_t *world) const override {
252 if (world) {
253 ecs_iter_t result = it_;
254 result.stage = world;
255 return result;
256 }
257 return it_;
258 }
259
260 ecs_iter_next_action_t next_action() const override {
261 return next_;
262 }
263
264private:
265 ecs_iter_t it_;
267};
268
269template <typename ... Components>
271{
272 return iter_iterable<Components...>(this, world);
273}
274
275template <typename ... Components>
277{
278 return iter_iterable<Components...>(this, it.stage());
279}
280
281template <typename ... Components>
283{
284 return iter_iterable<Components...>(this, e.world());
285}
286
287namespace _ {
288
289template <auto Create, ecs_iter_next_action_t Next, typename... Components>
290struct chained_iterable final : iterable<Components...> {
291 chained_iterable(int32_t first, int32_t second, const iterable<Components...> *source)
292 : chain_it_(source->get_iter(nullptr)), args_{first, second} { }
293
294protected:
295 ecs_iter_t get_iter(flecs::world_t*) const override {
296 return Create(&chain_it_, args_[0], args_[1]);
297 }
298
299 ecs_iter_next_action_t next_action() const override {
300 return Next;
301 }
302
303private:
304 ecs_iter_t chain_it_;
305 int32_t args_[2];
306};
307
308}
309
310template <typename... Components>
311page_iterable<Components...> iterable<Components...>::page(int32_t offset, int32_t limit) {
312 return page_iterable<Components...>(offset, limit, this);
313}
314
315template <typename... Components>
316worker_iterable<Components...> iterable<Components...>::worker(int32_t index, int32_t count) {
317 return worker_iterable<Components...>(index, count, this);
318}
319
320}
#define ecs_assert(condition, error_code,...)
Assert.
Definition log.h:473
#define ECS_INVALID_PARAMETER
Invalid parameter error code.
Definition log.h:671
#define ECS_INTERNAL_ERROR
Internal error code.
Definition log.h:681
struct ecs_iter_t ecs_iter_t
Type used for iterating iterable objects.
Definition flecs.h:481
ecs_world_t world_t
World type.
Definition c_types.hpp:18
bool(*) ecs_iter_next_action_t(ecs_iter_t *it)
Function prototype for iterating an iterator.
Definition flecs.h:601
ecs_iter_t ecs_worker_iter(const ecs_iter_t *it, int32_t index, int32_t count)
Create a worker iterator.
void ecs_iter_fini(ecs_iter_t *it)
Clean up iterator resources.
ecs_iter_t ecs_page_iter(const ecs_iter_t *it, int32_t offset, int32_t limit)
Create a paged iterator.
void ecs_iter_set_var(ecs_iter_t *it, int32_t var_id, ecs_entity_t entity)
Set the value for an iterator variable.
bool ecs_worker_next(ecs_iter_t *it)
Progress a worker iterator.
void ecs_iter_set_var_as_table(ecs_iter_t *it, int32_t var_id, const ecs_table_t *table)
Same as ecs_iter_set_var(), but for a table.
bool ecs_page_next(ecs_iter_t *it)
Progress a paged iterator.
void ecs_iter_set_var_as_range(ecs_iter_t *it, int32_t var_id, const ecs_table_range_t *range)
Same as ecs_iter_set_var(), but for a range of entities.
void ecs_iter_set_group(ecs_iter_t *it, uint64_t group_id)
Set the group to iterate for a query iterator.
int32_t ecs_query_find_var(const ecs_query_t *query, const char *name)
Find a variable index.
JSON iterable mixin.
Int to enum.
Definition component.hpp:18
Iterator.
Definition flecs.h:1183
const ecs_query_t * query
Query being evaluated.
Definition flecs.h:1218
Entity.
Definition entity.hpp:30
flecs::world world() const
Get the world.
Definition impl.hpp:60
flecs::entity first() const
Get first element from a pair.
Definition impl.hpp:20
Forward declaration of iter_iterable.
Definition iterable.hpp:161
iter_iterable< Components... > & set_group()
Limit results to tables with the specified group type (grouped queries only).
Definition iterable.hpp:244
iter_iterable< Components... > & set_group(uint64_t group_id)
Limit results to tables with the specified group ID (grouped queries only).
Definition iterable.hpp:237
flecs::entity first()
Return the first matching entity.
Definition iterable.hpp:224
iter_iterable(Iterable *it, flecs::world_t *world)
Construct iter_iterable from an iterable and a world.
Definition iterable.hpp:164
bool is_true()
Return whether the iterator yields at least one result.
Definition iterable.hpp:215
int32_t count()
Return the total number of entities in the result.
Definition iterable.hpp:206
Class for iterating over query results.
Definition iter.hpp:68
iter(ecs_iter_t *it)
Construct iterator from C iterator object.
Definition iter.hpp:78
Base class for iterable query objects.
Definition iterable.hpp:25
bool is_true() const
Return whether the iterable has any matches.
Definition iterable.hpp:121
iter_iterable< Components... > set_group() const
Limit results to tables with the specified group type (grouped queries only).
Definition iterable.hpp:143
void run(Func &&func) const
Run the iterator.
Definition iterable.hpp:53
flecs::entity first() const
Return the first entity matched by the iterable.
Definition iterable.hpp:126
void each(Func &&func) const
Each iterator.
Definition iterable.hpp:37
virtual ~iterable()
Virtual destructor.
Definition iterable.hpp:149
page_iterable< Components... > page(int32_t offset, int32_t limit)
Page iterator.
Definition iterable.hpp:311
flecs::entity find(Func &&func) const
Find the first entity matching a condition.
Definition iterable.hpp:65
worker_iterable< Components... > worker(int32_t index, int32_t count)
Worker iterator.
Definition iterable.hpp:316
iter_iterable< Components... > iter(flecs::world_t *world=nullptr) const
Create an iterator.
Definition iterable.hpp:270
iter_iterable< Components... > set_group(uint64_t group_id) const
Limit results to tables with the specified group ID (grouped queries only).
Definition iterable.hpp:137
The world.
Definition world.hpp:129