diff --git a/distr/flecs.h b/distr/flecs.h
index d36f391ee..822d07f16 100644
--- a/distr/flecs.h
+++ b/distr/flecs.h
@@ -29730,6 +29730,8 @@ struct query_builder_i : term_builder_i {
/* Term notation for more complex query features */
+ /** Sets the current term to next one in term list.
+ */
Base& term() {
if (this->term_) {
ecs_check(ecs_term_is_initialized(this->term_),
@@ -29748,6 +29750,34 @@ struct query_builder_i : term_builder_i {
return *this;
}
+ /** Sets the current term to the one with the provided type.
+ * This loops over all terms to find the one with the provided type.
+ * For performance-critical paths, use term_at(int32_t) instead.
+ */
+ template
+ Base& term_at() {
+ flecs::id_t term_id = _::type::id(this->world_v());
+ for (int i = 0; i < term_index_; i ++) {
+ ecs_term_t cur_term = desc_->terms[i];
+ flecs::id_t cur_term_id = cur_term.id;
+ if (cur_term_id == 0) {
+ cur_term_id = cur_term.second.id;
+ if (cur_term_id == 0) {
+ cur_term_id = cur_term.first.id;
+ }
+ } else if ((cur_term_id & ECS_PAIR) != 0) {
+ cur_term_id = cur_term_id & 0xFFFFFFFF;
+ }
+ if (cur_term_id == term_id) {
+ return term_at(i);
+ }
+ }
+ ecs_err("term not found");
+ return *this;
+ }
+
+ /** Sets the current term to the one at the provided index.
+ */
Base& term_at(int32_t term_index) {
ecs_assert(term_index >= 0, ECS_INVALID_PARAMETER, NULL);
int32_t prev_index = term_index_;
@@ -29759,6 +29789,26 @@ struct query_builder_i : term_builder_i {
return *this;
}
+ /** Sets the current term to the one at the provided index and asserts that the type matches.
+ */
+ template
+ Base& term_at(int32_t term_index) {
+ this->term_at(term_index);
+ ecs_term_t cur_term = *this->term_;
+ flecs::id_t cur_term_id = cur_term.id;
+ if (cur_term_id == 0) {
+ cur_term_id = cur_term.second.id;
+ if (cur_term_id == 0) {
+ cur_term_id = cur_term.first.id;
+ }
+ } else if ((cur_term_id & ECS_PAIR) != 0) {
+ cur_term_id = cur_term_id & 0xFFFFFFFF;
+ }
+ ecs_assert(cur_term_id == _::type::id(this->world_v()),
+ ECS_INVALID_PARAMETER, "term type mismatch");
+ return *this;
+ }
+
/** Sort the output of a query.
* This enables sorting of entities across matched tables. As a result of this
* operation, the order of entities in the matched tables may be changed.
diff --git a/include/flecs/addons/cpp/mixins/query/builder_i.hpp b/include/flecs/addons/cpp/mixins/query/builder_i.hpp
index c5e7258e2..053fd03e4 100644
--- a/include/flecs/addons/cpp/mixins/query/builder_i.hpp
+++ b/include/flecs/addons/cpp/mixins/query/builder_i.hpp
@@ -217,6 +217,8 @@ struct query_builder_i : term_builder_i {
/* Term notation for more complex query features */
+ /** Sets the current term to next one in term list.
+ */
Base& term() {
if (this->term_) {
ecs_check(ecs_term_is_initialized(this->term_),
@@ -235,6 +237,34 @@ struct query_builder_i : term_builder_i {
return *this;
}
+ /** Sets the current term to the one with the provided type.
+ * This loops over all terms to find the one with the provided type.
+ * For performance-critical paths, use term_at(int32_t) instead.
+ */
+ template
+ Base& term_at() {
+ flecs::id_t term_id = _::type::id(this->world_v());
+ for (int i = 0; i < term_index_; i ++) {
+ ecs_term_t cur_term = desc_->terms[i];
+ flecs::id_t cur_term_id = cur_term.id;
+ if (cur_term_id == 0) {
+ cur_term_id = cur_term.second.id;
+ if (cur_term_id == 0) {
+ cur_term_id = cur_term.first.id;
+ }
+ } else if ((cur_term_id & ECS_PAIR) != 0) {
+ cur_term_id = cur_term_id & 0xFFFFFFFF;
+ }
+ if (cur_term_id == term_id) {
+ return term_at(i);
+ }
+ }
+ ecs_err("term not found");
+ return *this;
+ }
+
+ /** Sets the current term to the one at the provided index.
+ */
Base& term_at(int32_t term_index) {
ecs_assert(term_index >= 0, ECS_INVALID_PARAMETER, NULL);
int32_t prev_index = term_index_;
@@ -246,6 +276,26 @@ struct query_builder_i : term_builder_i {
return *this;
}
+ /** Sets the current term to the one at the provided index and asserts that the type matches.
+ */
+ template
+ Base& term_at(int32_t term_index) {
+ this->term_at(term_index);
+ ecs_term_t cur_term = *this->term_;
+ flecs::id_t cur_term_id = cur_term.id;
+ if (cur_term_id == 0) {
+ cur_term_id = cur_term.second.id;
+ if (cur_term_id == 0) {
+ cur_term_id = cur_term.first.id;
+ }
+ } else if ((cur_term_id & ECS_PAIR) != 0) {
+ cur_term_id = cur_term_id & 0xFFFFFFFF;
+ }
+ ecs_assert(cur_term_id == _::type::id(this->world_v()),
+ ECS_INVALID_PARAMETER, "term type mismatch");
+ return *this;
+ }
+
/** Sort the output of a query.
* This enables sorting of entities across matched tables. As a result of this
* operation, the order of entities in the matched tables may be changed.
diff --git a/test/cpp/project.json b/test/cpp/project.json
index 7d35c391a..807502e95 100644
--- a/test/cpp/project.json
+++ b/test/cpp/project.json
@@ -747,6 +747,8 @@
"relation_w_predicate_wildcard",
"add_pair_w_rel_type",
"template_term",
+ "typed_term_at",
+ "typed_term_at_indexed",
"explicit_subject_w_id",
"explicit_subject_w_type",
"explicit_object_w_id",
diff --git a/test/cpp/src/QueryBuilder.cpp b/test/cpp/src/QueryBuilder.cpp
index c540008f3..c68bdf19e 100644
--- a/test/cpp/src/QueryBuilder.cpp
+++ b/test/cpp/src/QueryBuilder.cpp
@@ -1592,6 +1592,54 @@ void QueryBuilder_template_term(void) {
test_int(count, 1);
}
+void QueryBuilder_typed_term_at(void) {
+ flecs::world ecs;
+
+ struct Rel { int foo; };
+
+ int32_t count = 0;
+
+ auto s = ecs.system()
+ .term_at().singleton()
+ .term_at().second(flecs::Wildcard)
+ .run([&](flecs::iter it){
+ while (it.next()) {
+ count += it.count();
+ }
+ });
+
+ ecs.entity().add();
+ ecs.set({});
+
+ s.run();
+
+ test_int(count, 1);
+}
+
+void QueryBuilder_typed_term_at_indexed(void) {
+ flecs::world ecs;
+
+ struct Rel { int foo; };
+
+ int32_t count = 0;
+
+ auto s = ecs.system()
+ .term_at(1).singleton()
+ .term_at(0).second(flecs::Wildcard)
+ .run([&](flecs::iter it){
+ while (it.next()) {
+ count += it.count();
+ }
+ });
+
+ ecs.entity().add();
+ ecs.set({});
+
+ s.run();
+
+ test_int(count, 1);
+}
+
void QueryBuilder_explicit_subject_w_id(void) {
flecs::world ecs;
diff --git a/test/cpp/src/main.cpp b/test/cpp/src/main.cpp
index d1fdaf7e5..5de66b591 100644
--- a/test/cpp/src/main.cpp
+++ b/test/cpp/src/main.cpp
@@ -718,6 +718,8 @@ void QueryBuilder_relation_w_object_wildcard(void);
void QueryBuilder_relation_w_predicate_wildcard(void);
void QueryBuilder_add_pair_w_rel_type(void);
void QueryBuilder_template_term(void);
+void QueryBuilder_typed_term_at(void);
+void QueryBuilder_typed_term_at_indexed(void);
void QueryBuilder_explicit_subject_w_id(void);
void QueryBuilder_explicit_subject_w_type(void);
void QueryBuilder_explicit_object_w_id(void);
@@ -4160,6 +4162,14 @@ bake_test_case QueryBuilder_testcases[] = {
"template_term",
QueryBuilder_template_term
},
+ {
+ "typed_term_at",
+ QueryBuilder_typed_term_at
+ },
+ {
+ "typed_term_at_indexed",
+ QueryBuilder_typed_term_at_indexed
+ },
{
"explicit_subject_w_id",
QueryBuilder_explicit_subject_w_id
@@ -6805,7 +6815,7 @@ static bake_test_suite suites[] = {
"QueryBuilder",
QueryBuilder_setup,
NULL,
- 166,
+ 168,
QueryBuilder_testcases,
1,
QueryBuilder_params