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