Skip to content

Commit

Permalink
Add support for type checked term<T> and term_at<T>(i)
Browse files Browse the repository at this point in the history
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:

BeanCheeseBurrito/Flecs.NET#52

Signed-off-by: Tomas Slusny <[email protected]>
  • Loading branch information
deathbeam committed Oct 14, 2024
1 parent 5150a24 commit 0a9e0af
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 1 deletion.
54 changes: 54 additions & 0 deletions distr/flecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -29730,6 +29730,8 @@ struct query_builder_i : term_builder_i<Base> {

/* 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_),
Expand All @@ -29748,6 +29750,37 @@ struct query_builder_i : term_builder_i<Base> {
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 <typename T>
Base& term() {
flecs::id_t term_id = _::type<T>::id(this->world_v());

for (int i = 0; i < term_index_; i ++) {
ecs_term_t term = desc_->terms[i];
flecs::id_t cur_term_id = term.id;
if (cur_term_id == 0) {
cur_term_id = term.second.id;
if (cur_term_id == 0) {
cur_term_id = 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_;
Expand All @@ -29759,6 +29792,27 @@ struct query_builder_i : term_builder_i<Base> {
return *this;
}

/** Sets the current term to the one at the provided index and asserts that the type matches.
*/
template <typename T>
Base& term_at(int32_t term_index) {
this->term_at(term_index);
flecs::id_t term_id = _::type<T>::id(this->world_v());
ecs_term_t term = this->term_;
flecs::id_t cur_term_id = term.id;
if (cur_term_id == 0) {
cur_term_id = term.second.id;
if (cur_term_id == 0) {
cur_term_id = term.first.id;
}
} else if ((cur_term_id & ECS_PAIR) != 0) {
cur_term_id = cur_term_id & 0xFFFFFFFF;
}
ecs_assert(cur_term_id == term_id,
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.
Expand Down
54 changes: 54 additions & 0 deletions include/flecs/addons/cpp/mixins/query/builder_i.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ struct query_builder_i : term_builder_i<Base> {

/* 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_),
Expand All @@ -235,6 +237,37 @@ struct query_builder_i : term_builder_i<Base> {
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 <typename T>
Base& term() {
flecs::id_t term_id = _::type<T>::id(this->world_v());

for (int i = 0; i < term_index_; i ++) {
ecs_term_t term = desc_->terms[i];
flecs::id_t cur_term_id = term.id;
if (cur_term_id == 0) {
cur_term_id = term.second.id;
if (cur_term_id == 0) {
cur_term_id = 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_;
Expand All @@ -246,6 +279,27 @@ struct query_builder_i : term_builder_i<Base> {
return *this;
}

/** Sets the current term to the one at the provided index and asserts that the type matches.
*/
template <typename T>
Base& term_at(int32_t term_index) {
this->term_at(term_index);
flecs::id_t term_id = _::type<T>::id(this->world_v());
ecs_term_t term = this->term_;
flecs::id_t cur_term_id = term.id;
if (cur_term_id == 0) {
cur_term_id = term.second.id;
if (cur_term_id == 0) {
cur_term_id = term.first.id;
}
} else if ((cur_term_id & ECS_PAIR) != 0) {
cur_term_id = cur_term_id & 0xFFFFFFFF;
}
ecs_assert(cur_term_id == term_id,
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.
Expand Down
2 changes: 2 additions & 0 deletions test/cpp/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,8 @@
"relation_w_predicate_wildcard",
"add_pair_w_rel_type",
"template_term",
"typed_term",
"typed_term_at",
"explicit_subject_w_id",
"explicit_subject_w_type",
"explicit_object_w_id",
Expand Down
48 changes: 48 additions & 0 deletions test/cpp/src/QueryBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1592,6 +1592,54 @@ void QueryBuilder_template_term(void) {
test_int(count, 1);
}

void QueryBuilder_typed_term(void) {
flecs::world ecs;

struct Rel { int foo; };

int32_t count = 0;

auto s = ecs.system<Rel, const Velocity>()
.term<Velocity>().singleton()
.term<Rel>().second(flecs::Wildcard)
.run([&](flecs::iter it){
while (it.next()) {
count += it.count();
}
});

ecs.entity().add<Rel, Tag>();
ecs.set<Velocity>({});

s.run();

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<Rel, const Velocity>()
.term_at<Velocity>(1).singleton()
.term_at<Rel>(0).second(flecs::Wildcard)
.run([&](flecs::iter it){
while (it.next()) {
count += it.count();
}
});

ecs.entity().add<Rel, Tag>();
ecs.set<Velocity>({});

s.run();

test_int(count, 1);
}

void QueryBuilder_explicit_subject_w_id(void) {
flecs::world ecs;

Expand Down
12 changes: 11 additions & 1 deletion test/cpp/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(void);
void QueryBuilder_typed_term_at(void);
void QueryBuilder_explicit_subject_w_id(void);
void QueryBuilder_explicit_subject_w_type(void);
void QueryBuilder_explicit_object_w_id(void);
Expand Down Expand Up @@ -4160,6 +4162,14 @@ bake_test_case QueryBuilder_testcases[] = {
"template_term",
QueryBuilder_template_term
},
{
"typed_term",
QueryBuilder_typed_term
},
{
"typed_term_at",
QueryBuilder_typed_term_at
},
{
"explicit_subject_w_id",
QueryBuilder_explicit_subject_w_id
Expand Down Expand Up @@ -6805,7 +6815,7 @@ static bake_test_suite suites[] = {
"QueryBuilder",
QueryBuilder_setup,
NULL,
166,
168,
QueryBuilder_testcases,
1,
QueryBuilder_params
Expand Down

0 comments on commit 0a9e0af

Please sign in to comment.