From a20baf3a8ea0b4100df4a896731002c86c6d023c Mon Sep 17 00:00:00 2001 From: Sander Mertens Date: Wed, 8 Jan 2025 16:40:49 -0800 Subject: [PATCH] Implement flecs::ref::component() for getting ref's component id --- distr/flecs.h | 46 ++++++++++++++- include/flecs/addons/cpp/entity.hpp | 28 +++++++++ .../flecs/addons/cpp/mixins/entity/impl.hpp | 5 ++ include/flecs/addons/cpp/ref.hpp | 13 ++++- test/cpp/project.json | 5 +- test/cpp/src/Refs.cpp | 58 +++++++++++++++++++ test/cpp/src/main.cpp | 17 +++++- 7 files changed, 164 insertions(+), 8 deletions(-) diff --git a/distr/flecs.h b/distr/flecs.h index 867de1c59..cb06f4415 100644 --- a/distr/flecs.h +++ b/distr/flecs.h @@ -26205,6 +26205,34 @@ struct entity : entity_builder ecs_modified_id(world_, id_, comp); } + /** Get reference to component specified by id. + * A reference allows for quick and safe access to a component value, and is + * a faster alternative to repeatedly calling 'get' for the same component. + * + * The method accepts an optional component id argument, which can be used + * to create a ref to a component that is different from the provided type. + * This allows for creating a base type ref that points to a derived type: + * + * @code + * flecs::ref r = e.get_ref(world.id()); + * @endcode + * + * If the provided component id is not binary compatible with the specified + * type, the behavior is undefined. + * + * @tparam T component for which to get a reference. + * @return The reference. + */ + template ::value > = 0> + ref get_ref_w_id(flecs::id_t component) const { + if (component) { + _::type::id(world_); // ensure type is registered + return ref(world_, id_, component); + } else { + return ref(world_, id_, _::type::id(world_)); + } + } + /** Get reference to component. * A reference allows for quick and safe access to a component value, and is * a faster alternative to repeatedly calling 'get' for the same component. @@ -28120,9 +28148,12 @@ struct ref { id = _::type::id(world); } - ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, - "operation invalid for empty type"); - +#ifdef FLECS_DEBUG + flecs::entity_t type = ecs_get_typeid(world, id); + const flecs::type_info_t *ti = ecs_get_type_info(world, type); + ecs_assert(ti && ti->size != 0, ECS_INVALID_PARAMETER, + "cannot create ref to empty type"); +#endif ref_ = ecs_ref_init_id(world_, entity, id); } @@ -28161,8 +28192,12 @@ struct ref { return has(); } + /** Return entity associated with reference. */ flecs::entity entity() const; + /** Return component associated with reference. */ + flecs::id component() const; + private: world_t *world_; flecs::ref_t ref_; @@ -29106,6 +29141,11 @@ flecs::entity ref::entity() const { return flecs::entity(world_, ref_.entity); } +template +flecs::id ref::component() const { + return flecs::id(world_, ref_.id); +} + template template inline const Self& entity_builder::insert(const Func& func) const { diff --git a/include/flecs/addons/cpp/entity.hpp b/include/flecs/addons/cpp/entity.hpp index 258a82441..cc32e4770 100644 --- a/include/flecs/addons/cpp/entity.hpp +++ b/include/flecs/addons/cpp/entity.hpp @@ -236,6 +236,34 @@ struct entity : entity_builder ecs_modified_id(world_, id_, comp); } + /** Get reference to component specified by id. + * A reference allows for quick and safe access to a component value, and is + * a faster alternative to repeatedly calling 'get' for the same component. + * + * The method accepts an optional component id argument, which can be used + * to create a ref to a component that is different from the provided type. + * This allows for creating a base type ref that points to a derived type: + * + * @code + * flecs::ref r = e.get_ref(world.id()); + * @endcode + * + * If the provided component id is not binary compatible with the specified + * type, the behavior is undefined. + * + * @tparam T component for which to get a reference. + * @return The reference. + */ + template ::value > = 0> + ref get_ref_w_id(flecs::id_t component) const { + if (component) { + _::type::id(world_); // ensure type is registered + return ref(world_, id_, component); + } else { + return ref(world_, id_, _::type::id(world_)); + } + } + /** Get reference to component. * A reference allows for quick and safe access to a component value, and is * a faster alternative to repeatedly calling 'get' for the same component. diff --git a/include/flecs/addons/cpp/mixins/entity/impl.hpp b/include/flecs/addons/cpp/mixins/entity/impl.hpp index c1261b9e9..39ee5e2ad 100644 --- a/include/flecs/addons/cpp/mixins/entity/impl.hpp +++ b/include/flecs/addons/cpp/mixins/entity/impl.hpp @@ -12,6 +12,11 @@ flecs::entity ref::entity() const { return flecs::entity(world_, ref_.entity); } +template +flecs::id ref::component() const { + return flecs::id(world_, ref_.id); +} + template template inline const Self& entity_builder::insert(const Func& func) const { diff --git a/include/flecs/addons/cpp/ref.hpp b/include/flecs/addons/cpp/ref.hpp index 00eddf7d1..063768136 100644 --- a/include/flecs/addons/cpp/ref.hpp +++ b/include/flecs/addons/cpp/ref.hpp @@ -34,9 +34,12 @@ struct ref { id = _::type::id(world); } - ecs_assert(_::type::size() != 0, ECS_INVALID_PARAMETER, - "operation invalid for empty type"); - +#ifdef FLECS_DEBUG + flecs::entity_t type = ecs_get_typeid(world, id); + const flecs::type_info_t *ti = ecs_get_type_info(world, type); + ecs_assert(ti && ti->size != 0, ECS_INVALID_PARAMETER, + "cannot create ref to empty type"); +#endif ref_ = ecs_ref_init_id(world_, entity, id); } @@ -75,8 +78,12 @@ struct ref { return has(); } + /** Return entity associated with reference. */ flecs::entity entity() const; + /** Return component associated with reference. */ + flecs::id component() const; + private: world_t *world_; flecs::ref_t ref_; diff --git a/test/cpp/project.json b/test/cpp/project.json index 7e8b03cfa..6c0578b75 100644 --- a/test/cpp/project.json +++ b/test/cpp/project.json @@ -1095,7 +1095,10 @@ "implicit_operator_bool", "try_get", "has", - "bool_operator" + "bool_operator", + "base_type", + "empty_base_type", + "get_component" ] }, { "id": "Module", diff --git a/test/cpp/src/Refs.cpp b/test/cpp/src/Refs.cpp index 8d2913be8..e8ea2b921 100644 --- a/test/cpp/src/Refs.cpp +++ b/test/cpp/src/Refs.cpp @@ -226,3 +226,61 @@ void Refs_bool_operator(void) { test_assert(p); } } + +struct Base { + int x; +}; + +struct Derived : public Base { + Derived() { } + + Derived(int x_, int y_) { + x = x_; + y = y_; + } + + int y; +}; + +void Refs_base_type(void) { + flecs::world world; + + flecs::entity e = world.entity().set(Derived{10, 20}); + + flecs::ref r = e.get_ref_w_id(world.id()); + test_int(r->x, 10); +} + +struct BaseEmpty { }; + +struct DerivedFromEmpty : public BaseEmpty { + DerivedFromEmpty() { } + + DerivedFromEmpty(int y_) { + y = y_; + } + + int y; +}; + +void Refs_empty_base_type(void) { + flecs::world world; + + flecs::entity e = world.entity().set(DerivedFromEmpty{20}); + + flecs::ref r = e.get_ref_w_id(world.id()); + + BaseEmpty *b = r.get(); + DerivedFromEmpty *d = static_cast(b); + test_int(d->y, 20); +} + +void Refs_get_component(void) { + flecs::world world; + + auto e = flecs::entity(world) + .set({10, 20}); + + auto ref = e.get_ref(); + test_assert(ref.component() == world.id()); +} diff --git a/test/cpp/src/main.cpp b/test/cpp/src/main.cpp index 58add0cc9..35d291770 100644 --- a/test/cpp/src/main.cpp +++ b/test/cpp/src/main.cpp @@ -1059,6 +1059,9 @@ void Refs_implicit_operator_bool(void); void Refs_try_get(void); void Refs_has(void); void Refs_bool_operator(void); +void Refs_base_type(void); +void Refs_empty_base_type(void); +void Refs_get_component(void); // Testsuite 'Module' void Module_import(void); @@ -5554,6 +5557,18 @@ bake_test_case Refs_testcases[] = { { "bool_operator", Refs_bool_operator + }, + { + "base_type", + Refs_base_type + }, + { + "empty_base_type", + Refs_empty_base_type + }, + { + "get_component", + Refs_get_component } }; @@ -7090,7 +7105,7 @@ static bake_test_suite suites[] = { "Refs", NULL, NULL, - 17, + 20, Refs_testcases }, {