Skip to content
Flecs v4.1
builder_i.hpp
Go to the documentation of this file.
1/**
2 * @file addons/cpp/mixins/query/builder_i.hpp
3 * @brief Query builder interface.
4 */
5
6#pragma once
7
9
10namespace flecs
11{
12
13/** Query builder interface.
14 *
15 * @ingroup cpp_core_queries
16 */
17template<typename Base, typename ... Components>
19 /** Construct from a query descriptor. */
20 query_builder_i(ecs_query_desc_t *desc, int32_t term_index = 0)
21 : term_index_(term_index)
22 , expr_count_(0)
23 , desc_(desc) { }
24
25 /** Set the query flags. */
26 Base& query_flags(ecs_flags32_t flags) {
27 desc_->flags |= flags;
28 return *this;
29 }
30
31 /** Set the cache kind for the query. */
33 desc_->cache_kind = static_cast<ecs_query_cache_kind_t>(kind);
34 return *this;
35 }
36
37 /** Enable auto-caching for the query. */
38 Base& cached() {
40 }
41
42 /** Enable change detection for the query. */
44 desc_->flags |= EcsQueryDetectChanges;
45 return *this;
46 }
47
48 /** Set the query expression string. */
49 Base& expr(const char *expr) {
50 ecs_check(expr_count_ == 0, ECS_INVALID_OPERATION,
51 "query_builder::expr() called more than once");
52 desc_->expr = expr;
53 expr_count_ ++;
54
55 error:
56 return *this;
57 }
58
59 template <typename T>
60 Base& with() {
61 return with(_::type<T>::id(this->world_v())).inout(_::type_to_inout<T>());
62 }
63
64 template <typename First, typename Second>
65 Base& with() {
66 return with(_::type<First>::id(this->world_v()), _::type<Second>::id(this->world_v()));
67 }
68
69 template <typename First, typename Second>
70 Base& with(Second second) {
71 return with(_::type<First>::id(this->world_v()), second);
72 }
73
74 template <typename Arg>
75 Base& with(Arg&& arg) {
76 using T = decay_t<Arg>;
77 if constexpr (is_enum_v<T>) {
78 return with(_::type<T>::id(this->world_v()), _::entity_id(this->world_v(), arg));
79 } else {
80 this->term();
81 if constexpr (std::is_same_v<T, flecs::term>) {
82 *this->term_ = arg;
83 } else if constexpr (std::is_convertible_v<T, const char*>) {
84 *this->term_ = flecs::term().first(arg);
85 } else {
86 *this->term_ = flecs::term(arg);
87 }
88 return *this;
89 }
90 }
91
92 template <typename First, typename Second>
93 Base& with(First first, Second second) {
94 this->term();
95 *this->term_ = flecs::term().first(first).second(second);
96 return *this;
97 }
98
99 template <typename... T, typename... Args>
100 Base& without(Args&&... args) {
101 return this->template with<T...>(FLECS_FWD(args)...).not_();
102 }
103
104 Base& write() {
106 }
107
108 template <typename... T, typename... Args>
109 Base& write(Args&&... args) {
110 return this->template with<T...>(FLECS_FWD(args)...).write();
111 }
112
113 Base& read() {
115 }
116
117 template <typename... T, typename... Args>
118 Base& read(Args&&... args) {
119 return this->template with<T...>(FLECS_FWD(args)...).read();
120 }
121
122 /** Open a query scope. */
123 Base& scope_open() {
124 return this->with(flecs::ScopeOpen).entity(0);
125 }
126
127 /** Close a query scope. */
128 Base& scope_close() {
129 return this->with(flecs::ScopeClose).entity(0);
130 }
131
132 /** Set the current term to the next one in the term list. */
133 Base& term() {
134 if (this->term_) {
137 "query_builder::term() called without initializing term");
138 }
139
140 ecs_check(term_index_ < FLECS_TERM_COUNT_MAX,
141 ECS_INVALID_PARAMETER, "maximum number of terms exceeded");
142
143 this->set_term(&desc_->terms[term_index_]);
144
145 term_index_ ++;
146
147 error:
148 return *this;
149 }
150
151 /** Set the current term to the one with the provided type.
152 * This loops over all terms to find the one with the provided type.
153 * For performance-critical paths, use term_at(int32_t) instead.
154 */
155 template <typename T>
156 Base& term_at() {
157 flecs::id_t term_id = _::type<T>::id(this->world_v());
158 for (int i = 0; i < term_index_; i ++) {
159 ecs_term_t cur_term = desc_->terms[i];
160 ecs_id_t cur_term_id = cur_term.id;
161 ecs_id_t cur_term_pair = ecs_pair(cur_term.first.id, cur_term.second.id);
162
163 if ((term_id == cur_term_id || (cur_term_id != 0 && term_id == ecs_get_typeid(this->world_v(), cur_term_id))) ||
164 (term_id == cur_term_pair || (cur_term_pair != 0 && term_id == ecs_get_typeid(this->world_v(), cur_term_pair)))) {
165 return term_at(i);
166 }
167 }
168
169 ecs_err("term not found");
170 return *this;
171 }
172
173 /** Set the current term to the one at the provided index. */
174 Base& term_at(int32_t term_index) {
175 ecs_assert(term_index >= 0, ECS_INVALID_PARAMETER, nullptr);
176 int32_t prev_index = term_index_;
177 term_index_ = term_index;
178 this->term();
179 term_index_ = prev_index;
181 ECS_INVALID_PARAMETER, nullptr);
182 return *this;
183 }
184
185 /** Set the current term to the one at the provided index and assert
186 * that the type matches. */
187 template <typename T>
188 Base& term_at(int32_t term_index) {
189 this->term_at(term_index);
190#if !defined(FLECS_NDEBUG) || defined(FLECS_KEEP_ASSERT)
191 flecs::id_t term_id = _::type<T>::id(this->world_v());
192 ecs_term_t cur_term = *this->term_;
193 ecs_id_t cur_term_id = cur_term.id;
194 ecs_id_t cur_term_pair = ecs_pair(cur_term.first.id, cur_term.second.id);
195
196 ecs_assert((term_id == cur_term_id || (cur_term_id != 0 && term_id == ecs_get_typeid(this->world_v(), cur_term_id))) ||
197 (term_id == cur_term_pair || (cur_term_pair != 0 && term_id == ecs_get_typeid(this->world_v(), cur_term_pair))),
198 ECS_INVALID_PARAMETER, "term type mismatch");
199#endif
200 return *this;
201 }
202
203 /** Sort the output of a query.
204 * This enables sorting of entities across matched tables. As a result of this
205 * operation, the order of entities in the matched tables may be changed.
206 * Resorting happens when a query iterator is obtained, and only if the table
207 * data has changed.
208 *
209 * If multiple queries that match the same (down)set of tables specify different
210 * sorting functions, resorting is likely to happen every time an iterator is
211 * obtained, which can significantly slow down iterations.
212 *
213 * The sorting function will be applied to the specified component. Resorting
214 * only happens if that component has changed, or when the entity order in the
215 * table has changed. If no component is provided, resorting only happens when
216 * the entity order changes.
217 *
218 * @tparam T The component used to sort.
219 * @param compare The compare function used to sort the components.
220 */
221 template <typename T>
222 Base& order_by(int(*compare)(flecs::entity_t, const T*, flecs::entity_t, const T*)) {
223 ecs_order_by_action_t cmp = reinterpret_cast<ecs_order_by_action_t>(compare);
224 return this->order_by(_::type<T>::id(this->world_v()), cmp);
225 }
226
227 /** Sort the output of a query.
228 * Same as order_by<T>(), but with a component identifier.
229 *
230 * @param component The component used to sort.
231 * @param compare The compare function used to sort the components.
232 */
233 Base& order_by(flecs::entity_t component, int(*compare)(flecs::entity_t, const void*, flecs::entity_t, const void*)) {
234 desc_->order_by_callback = reinterpret_cast<ecs_order_by_action_t>(compare);
235 desc_->order_by = component;
236 return *this;
237 }
238
239 /** Group and sort matched tables.
240 * Similar to ecs_query_order_by(), but instead of sorting individual entities, this
241 * operation only sorts matched tables. This can be useful if a query needs to
242 * enforce a certain iteration order upon the tables it is iterating, for
243 * example by giving a certain component or tag a higher priority.
244 *
245 * The sorting function assigns a "rank" to each type, which is then used to
246 * sort the tables. Tables with higher ranks will appear later in the iteration.
247 *
248 * Resorting happens when a query iterator is obtained, and only if the set of
249 * matched tables for a query has changed. If table sorting is enabled together
250 * with entity sorting, table sorting takes precedence, and entities will be
251 * sorted within each set of tables that are assigned the same rank.
252 *
253 * @tparam T The component used to determine the group rank.
254 * @param group_by_action Callback that determines the group ID for a table.
255 */
256 template <typename T>
257 Base& group_by(uint64_t(*group_by_action)(flecs::world_t*, flecs::table_t *table, flecs::id_t id, void* ctx)) {
258 ecs_group_by_action_t action = reinterpret_cast<ecs_group_by_action_t>(group_by_action);
259 return this->group_by(_::type<T>::id(this->world_v()), action);
260 }
261
262 /** Group and sort matched tables.
263 * Same as group_by<T>(), but with a component identifier.
264 *
265 * @param component The component used to determine the group rank.
266 * @param group_by_action Callback that determines the group ID for a table.
267 */
268 Base& group_by(flecs::entity_t component, uint64_t(*group_by_action)(flecs::world_t*, flecs::table_t *table, flecs::id_t id, void* ctx)) {
269 desc_->group_by_callback = reinterpret_cast<ecs_group_by_action_t>(group_by_action);
270 desc_->group_by = component;
271 return *this;
272 }
273
274 /** Group and sort matched tables.
275 * Same as group_by<T>(), but with the default group_by() action.
276 *
277 * @tparam T The component used to determine the group rank.
278 */
279 template <typename T>
280 Base& group_by() {
281 return this->group_by(_::type<T>::id(this->world_v()), nullptr);
282 }
283
284 /** Group and sort matched tables.
285 * Same as group_by(), but with the default group_by() action.
286 *
287 * @param component The component used to determine the group rank.
288 */
290 return this->group_by(component, nullptr);
291 }
292
293 /** Specify context to be passed to the group_by() function.
294 *
295 * @param ctx Context to pass to the group_by() function.
296 * @param ctx_free Function to clean up context (called when the query is deleted).
297 */
298 Base& group_by_ctx(void *ctx, ecs_ctx_free_t ctx_free = nullptr) {
299 desc_->group_by_ctx = ctx;
300 desc_->group_by_ctx_free = ctx_free;
301 return *this;
302 }
303
304 /** Specify the on_group_create() action. */
306 desc_->on_group_create = action;
307 return *this;
308 }
309
310 /** Specify the on_group_delete() action. */
312 desc_->on_group_delete = action;
313 return *this;
314 }
315
316protected:
317 virtual flecs::world_t* world_v() override = 0;
318 int32_t term_index_;
319 int32_t expr_count_;
320
321private:
322 operator Base&() {
323 return *static_cast<Base*>(this);
324 }
325
326 ecs_query_desc_t *desc_;
327};
328
329}
#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_err(...)
Error macro.
Definition log.h:319
#define ECS_INVALID_PARAMETER
Invalid parameter error code.
Definition log.h:671
#define ecs_check(condition, error_code,...)
Check.
Definition log.h:527
uint64_t ecs_id_t
IDs are the things that can be added to an entity.
Definition flecs.h:388
ecs_table_t table_t
Table type.
Definition c_types.hpp:23
ecs_id_t id_t
ID type.
Definition c_types.hpp:20
ecs_entity_t entity_t
Entity type.
Definition c_types.hpp:21
query_cache_kind_t
Query cache kind.
Definition c_types.hpp:60
ecs_world_t world_t
World type.
Definition c_types.hpp:18
@ QueryCacheAuto
Auto query cache.
Definition c_types.hpp:62
void(*) ecs_ctx_free_t(void *ctx)
Function to clean up context data.
Definition flecs.h:660
uint64_t(*) ecs_group_by_action_t(ecs_world_t *world, ecs_table_t *table, ecs_id_t group_id, void *ctx)
Callback used for grouping tables in a query.
Definition flecs.h:631
void(*) ecs_group_delete_action_t(ecs_world_t *world, uint64_t group_id, void *group_ctx, void *group_by_ctx)
Callback invoked when a query deletes an existing group.
Definition flecs.h:644
void *(*) ecs_group_create_action_t(ecs_world_t *world, uint64_t group_id, void *group_by_ctx)
Callback invoked when a query creates a new group.
Definition flecs.h:638
int(*) ecs_order_by_action_t(ecs_entity_t e1, const void *ptr1, ecs_entity_t e2, const void *ptr2)
Callback used for comparing components.
Definition flecs.h:613
ecs_entity_t ecs_get_typeid(const ecs_world_t *world, ecs_id_t component)
Get the type for a component.
#define FLECS_TERM_COUNT_MAX
Maximum number of terms in queries.
Definition flecs.h:338
#define EcsQueryDetectChanges
Enable change detection for a query.
Definition flecs.h:1297
bool ecs_term_is_initialized(const ecs_term_t *term)
Test whether a term is set.
ecs_query_cache_kind_t
Specify cache policy for query.
Definition flecs.h:747
Used with ecs_query_init().
Definition flecs.h:1325
ecs_entity_t id
Entity ID.
Definition flecs.h:820
Type that describes a term (single element in a query).
Definition flecs.h:834
ecs_id_t id
Component ID to be matched by term.
Definition flecs.h:835
ecs_term_ref_t second
Second element of pair.
Definition flecs.h:842
ecs_term_ref_t first
Component or first element of pair.
Definition flecs.h:841
Component class.
Base & term_at(int32_t term_index)
Set the current term to the one at the provided index and assert that the type matches.
Base & group_by()
Group and sort matched tables.
Base & scope_close()
Close a query scope.
Base & on_group_delete(ecs_group_delete_action_t action)
Specify the on_group_delete() action.
Base & scope_open()
Open a query scope.
Base & group_by(flecs::entity_t component, uint64_t(*group_by_action)(flecs::world_t *, flecs::table_t *table, flecs::id_t id, void *ctx))
Group and sort matched tables.
Base & cache_kind(query_cache_kind_t kind)
Set the cache kind for the query.
Definition builder_i.hpp:32
Base & expr(const char *expr)
Set the query expression string.
Definition builder_i.hpp:49
Base & on_group_create(ecs_group_create_action_t action)
Specify the on_group_create() action.
Base & detect_changes()
Enable change detection for the query.
Definition builder_i.hpp:43
Base & query_flags(ecs_flags32_t flags)
Set the query flags.
Definition builder_i.hpp:26
Base & group_by(flecs::entity_t component)
Group and sort matched tables.
Base & group_by(uint64_t(*group_by_action)(flecs::world_t *, flecs::table_t *table, flecs::id_t id, void *ctx))
Group and sort matched tables.
Base & term_at(int32_t term_index)
Set the current term to the one at the provided index.
Base & term()
Set the current term to the next one in the term list.
Base & order_by(int(*compare)(flecs::entity_t, const T *, flecs::entity_t, const T *))
Sort the output of a query.
Base & group_by_ctx(void *ctx, ecs_ctx_free_t ctx_free=nullptr)
Specify context to be passed to the group_by() function.
query_builder_i(ecs_query_desc_t *desc, int32_t term_index=0)
Construct from a query descriptor.
Definition builder_i.hpp:20
Base & cached()
Enable auto-caching for the query.
Definition builder_i.hpp:38
Base & term_at()
Set the current term to the one with the provided type.
Base & order_by(flecs::entity_t component, int(*compare)(flecs::entity_t, const void *, flecs::entity_t, const void *))
Sort the output of a query.
Table.
Definition table.hpp:23
void set_term(ecs_term_t *term)
Set the current term pointer.
Base & desc()
Use with cascade() to iterate results in descending (bottom-to-top) order.
ecs_term_t * term_
Pointer to the current term.
Base & read()
Short for inout_stage(flecs::In).
term_builder_i()
Default constructor.
Base & not_()
Short for oper(flecs::Not).
Base & write()
Short for inout_stage(flecs::Out).
Base & flags(flecs::flags64_t flags)
Override the term ID flags.
Definition builder_i.hpp:74
flecs::term term(Args &&... args) const
Create a term.
Term builder interface.