From ad60d47b2bd56dff3e43cec620b0a35d1082dfd1 Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Sun, 13 Oct 2024 07:23:44 +0200 Subject: [PATCH] Add support for type checked term_at and term_at(i) First method is simple loop over terms to find term for T. Its slower than using index directly, but sometimes type safety is more preferred and when the cost is only on initialization. Second method is same as term_at(i) but with type checked assert. For middle ground to still keep the safety but also performance. This is mostly to mirror the C# binding PR where this is slightly more useful but maybe cpp would benefit too: https://github.com/BeanCheeseBurrito/Flecs.NET/pull/52 Signed-off-by: Tomas Slusny --- distr/flecs.h | 44 +++++++++++++++++ .../addons/cpp/mixins/query/builder_i.hpp | 44 +++++++++++++++++ test/cpp/project.json | 2 + test/cpp/src/QueryBuilder.cpp | 48 +++++++++++++++++++ test/cpp/src/main.cpp | 12 ++++- 5 files changed, 149 insertions(+), 1 deletion(-) diff --git a/distr/flecs.h b/distr/flecs.h index d36f391ee..038a770b5 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,30 @@ 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]; + ecs_id_t cur_term_id = cur_term.id; + ecs_id_t cur_term_pair = ecs_pair(cur_term.first.id, cur_term.second.id); + + if ((term_id == cur_term_id || (cur_term_id != 0 && term_id == ecs_get_typeid(this->world_v(), cur_term_id))) || + (term_id == cur_term_pair || (cur_term_pair != 0 && term_id == ecs_get_typeid(this->world_v(), cur_term_pair)))) { + 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 +29785,24 @@ 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); +#if !defined(FLECS_NDEBUG) || defined(FLECS_KEEP_ASSERT) + flecs::id_t term_id = _::type::id(this->world_v()); + ecs_term_t cur_term = *this->term_; + ecs_id_t cur_term_id = cur_term.id; + ecs_id_t cur_term_pair = ecs_pair(cur_term.first.id, cur_term.second.id); + + ecs_assert((term_id == cur_term_id || (cur_term_id != 0 && term_id == ecs_get_typeid(this->world_v(), cur_term_id))) || + (term_id == cur_term_pair || (cur_term_pair != 0 && term_id == ecs_get_typeid(this->world_v(), cur_term_pair))), + ECS_INVALID_PARAMETER, "term type mismatch"); +#endif + 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..da1129460 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,30 @@ 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]; + ecs_id_t cur_term_id = cur_term.id; + ecs_id_t cur_term_pair = ecs_pair(cur_term.first.id, cur_term.second.id); + + if ((term_id == cur_term_id || (cur_term_id != 0 && term_id == ecs_get_typeid(this->world_v(), cur_term_id))) || + (term_id == cur_term_pair || (cur_term_pair != 0 && term_id == ecs_get_typeid(this->world_v(), cur_term_pair)))) { + 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 +272,24 @@ 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); +#if !defined(FLECS_NDEBUG) || defined(FLECS_KEEP_ASSERT) + flecs::id_t term_id = _::type::id(this->world_v()); + ecs_term_t cur_term = *this->term_; + ecs_id_t cur_term_id = cur_term.id; + ecs_id_t cur_term_pair = ecs_pair(cur_term.first.id, cur_term.second.id); + + ecs_assert((term_id == cur_term_id || (cur_term_id != 0 && term_id == ecs_get_typeid(this->world_v(), cur_term_id))) || + (term_id == cur_term_pair || (cur_term_pair != 0 && term_id == ecs_get_typeid(this->world_v(), cur_term_pair))), + ECS_INVALID_PARAMETER, "term type mismatch"); +#endif + 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