From 5859a18b3a5fa59f0f21185de2caa1fee0218ba5 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Sat, 27 Oct 2018 13:45:51 +0200 Subject: [PATCH] added get_if where possible --- TODO | 1 - src/entt/entity/actor.hpp | 44 ++++++++++++++++++++++++---------- src/entt/entity/prototype.hpp | 37 +++++++++++++++++++++++----- src/entt/entity/registry.hpp | 44 ++++++++++++++++++++++++++++++++++ src/entt/entity/sparse_set.hpp | 30 ++++++++++++++++++----- test/entt/entity/actor.cpp | 40 +++++++++++++++++++------------ test/entt/entity/prototype.cpp | 8 +++++++ test/entt/entity/registry.cpp | 15 ++++++++++++ 8 files changed, 179 insertions(+), 40 deletions(-) diff --git a/TODO b/TODO index 3a8ba4714..93af4103d 100644 --- a/TODO +++ b/TODO @@ -15,7 +15,6 @@ * composable looper so as to pack erased systems, compose runners at different rates and run them at once in the loop * meta: sort of meta view based on meta stuff to iterate entities, void * and meta info objects * meta: move-to-head optimization when searching by name on parts (data, func, etc) -* registry::probe(entt) (returns a component * if entt has the component, nullptr otherwise) * hashed string: add implicit check on construction for uniqueness (optional) * add a note about multithreading support to the README file * signals on entity creation/destruction diff --git a/src/entt/entity/actor.hpp b/src/entt/entity/actor.hpp index 784a0dcc1..eaa049a48 100644 --- a/src/entt/entity/actor.hpp +++ b/src/entt/entity/actor.hpp @@ -120,23 +120,43 @@ struct actor { } /** - * @brief Returns a reference to the given component for an actor. - * @tparam Component Type of the component to get. - * @return A reference to the instance of the component owned by the actor. + * @brief Returns references to the given components for an actor. + * @tparam Component Types of components to get. + * @return References to the components owned by the actor. */ - template - const Component & get() const ENTT_NOEXCEPT { - return reg->template get(entt); + template + decltype(auto) get() const ENTT_NOEXCEPT { + return reg->template get(entt); } /** - * @brief Returns a reference to the given component for an actor. - * @tparam Component Type of the component to get. - * @return A reference to the instance of the component owned by the actor. + * @brief Returns references to the given components for an actor. + * @tparam Component Types of components to get. + * @return References to the components owned by the actor. */ - template - inline Component & get() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).template get()); + template + decltype(auto) get() ENTT_NOEXCEPT { + return reg->template get(entt); + } + + /** + * @brief Returns pointers to the given components for an actor. + * @tparam Component Types of components to get. + * @return Pointers to the components owned by the actor. + */ + template + auto get_if() const ENTT_NOEXCEPT { + return reg->template get_if(entt); + } + + /** + * @brief Returns pointers to the given components for an actor. + * @tparam Component Types of components to get. + * @return Pointers to the components owned by the actor. + */ + template + auto get_if() ENTT_NOEXCEPT { + return reg->template get_if(entt); } /** diff --git a/src/entt/entity/prototype.hpp b/src/entt/entity/prototype.hpp index 7d21eca6c..b9f95e424 100644 --- a/src/entt/entity/prototype.hpp +++ b/src/entt/entity/prototype.hpp @@ -49,11 +49,6 @@ class prototype final { basic_fn_type *assign; }; - template - inline const Component & component() const ENTT_NOEXCEPT { - return reg->template get>(entity).component; - } - void release() { if(reg->valid(entity)) { reg->destroy(entity); @@ -189,7 +184,8 @@ public: template decltype(auto) get() const ENTT_NOEXCEPT { if constexpr(sizeof...(Component) == 1) { - return component(); + const auto &component = reg->template get>(entity).component; + return component; } else { return std::tuple{get()...}; } @@ -216,6 +212,35 @@ public: } } + /** + * @brief Returns pointers to the given components. + * @tparam Component Types of components to get. + * @return Pointers to the components owned by the prototype. + */ + template + auto get_if() const ENTT_NOEXCEPT { + if constexpr(sizeof...(Component) == 1) { + const auto *wrapper = reg->template get_if>(entity); + return wrapper ? &wrapper->component : nullptr; + } else { + return std::tuple{get_if()...}; + } + } + + /** + * @brief Returns pointers to the given components. + * @tparam Component Types of components to get. + * @return Pointers to the components owned by the prototype. + */ + template + inline auto get_if() ENTT_NOEXCEPT { + if constexpr(sizeof...(Component) == 1) { + return (const_cast(std::as_const(*this).template get_if()), ...); + } else { + return std::tuple{get_if()...}; + } + } + /** * @brief Creates a new entity using a given prototype. * diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index 32c6725fc..c1f760335 100644 --- a/src/entt/entity/registry.hpp +++ b/src/entt/entity/registry.hpp @@ -624,6 +624,50 @@ public: } } + /** + * @brief Returns pointers to the given components for an entity. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Types of components to get. + * @param entity A valid entity identifier. + * @return Pointers to the components owned by the entity. + */ + template + auto get_if([[maybe_unused]] const entity_type entity) const ENTT_NOEXCEPT { + assert(valid(entity)); + + if constexpr(sizeof...(Component) == 1) { + return managed() ? pool().get_if(entity) : nullptr; + } else { + return std::tuple{get_if(entity)...}; + } + } + + /** + * @brief Returns pointers to the given components for an entity. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Types of components to get. + * @param entity A valid entity identifier. + * @return Pointers to the components owned by the entity. + */ + template + inline auto get_if([[maybe_unused]] const entity_type entity) ENTT_NOEXCEPT { + if constexpr(sizeof...(Component) == 1) { + return (const_cast(std::as_const(*this).template get_if(entity)), ...); + } else { + return std::tuple{get_if(entity)...}; + } + } + /** * @brief Replaces the given component for an entity. * diff --git a/src/entt/entity/sparse_set.hpp b/src/entt/entity/sparse_set.hpp index 7f336c417..3ec2fb75e 100644 --- a/src/entt/entity/sparse_set.hpp +++ b/src/entt/entity/sparse_set.hpp @@ -683,7 +683,7 @@ class sparse_set: public sparse_set { }; public: - /*! @brief Type of the objects associated to the entities. */ + /*! @brief Type of the objects associated with the entities. */ using object_type = Type; /*! @brief Underlying entity identifier. */ using entity_type = typename underlying_type::entity_type; @@ -881,7 +881,7 @@ public: } /** - * @brief Returns the object associated to an entity. + * @brief Returns the object associated with an entity. * * @warning * Attempting to use an entity that doesn't belong to the sparse set results @@ -890,14 +890,14 @@ public: * sparse set doesn't contain the given entity. * * @param entity A valid entity identifier. - * @return The object associated to the entity. + * @return The object associated with the entity. */ const object_type & get(const entity_type entity) const ENTT_NOEXCEPT { return instances[underlying_type::get(entity)]; } /** - * @brief Returns the object associated to an entity. + * @brief Returns the object associated with an entity. * * @warning * Attempting to use an entity that doesn't belong to the sparse set results @@ -906,12 +906,30 @@ public: * sparse set doesn't contain the given entity. * * @param entity A valid entity identifier. - * @return The object associated to the entity. + * @return The object associated with the entity. */ inline object_type & get(const entity_type entity) ENTT_NOEXCEPT { return const_cast(std::as_const(*this).get(entity)); } + /** + * @brief Returns a pointer to the object associated with an entity, if any. + * @param entity A valid entity identifier. + * @return The object associated with the entity, if any. + */ + const object_type * get_if(const entity_type entity) const ENTT_NOEXCEPT { + return underlying_type::has(entity) ? (instances.data() + underlying_type::get(entity)) : nullptr; + } + + /** + * @brief Returns a pointer to the object associated with an entity, if any. + * @param entity A valid entity identifier. + * @return The object associated with the entity, if any. + */ + inline object_type * get_if(const entity_type entity) ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).get_if(entity)); + } + /** * @brief Assigns an entity to a sparse set and constructs its object. * @@ -929,7 +947,7 @@ public: * @tparam Args Types of arguments to use to construct the object. * @param entity A valid entity identifier. * @param args Parameters to use to construct an object for the entity. - * @return The object associated to the entity. + * @return The object associated with the entity. */ template object_type & construct(const entity_type entity, Args &&... args) { diff --git a/test/entt/entity/actor.cpp b/test/entt/entity/actor.cpp index 9f4c1a53d..14100757a 100644 --- a/test/entt/entity/actor.cpp +++ b/test/entt/entity/actor.cpp @@ -3,8 +3,6 @@ #include #include -struct actor_component final {}; - TEST(Actor, Component) { entt::registry<> registry; entt::actor actor{registry}; @@ -12,31 +10,43 @@ TEST(Actor, Component) { ASSERT_EQ(®istry, &actor.backend()); ASSERT_EQ(®istry, &cactor.backend()); - ASSERT_TRUE(registry.empty()); + ASSERT_TRUE(registry.empty()); ASSERT_FALSE(registry.empty()); - ASSERT_FALSE(actor.has()); + ASSERT_FALSE(actor.has()); - const auto &component = actor.assign(); + const auto &cint = actor.assign(); + const auto &cchar = actor.assign(); - ASSERT_EQ(&component, &actor.get()); - ASSERT_EQ(&component, &cactor.get()); - ASSERT_FALSE(registry.empty()); + ASSERT_EQ(&cint, &actor.get()); + ASSERT_EQ(&cchar, &cactor.get()); + ASSERT_EQ(&cint, &std::get<0>(actor.get())); + ASSERT_EQ(&cchar, &std::get<1>(actor.get())); + ASSERT_EQ(&cint, std::get<0>(actor.get_if())); + ASSERT_EQ(&cchar, std::get<1>(actor.get_if())); + ASSERT_EQ(nullptr, std::get<2>(actor.get_if())); + ASSERT_EQ(nullptr, actor.get_if()); + ASSERT_EQ(&cchar, actor.get_if()); + ASSERT_EQ(&cint, actor.get_if()); + + ASSERT_FALSE(registry.empty()); ASSERT_FALSE(registry.empty()); - ASSERT_TRUE(actor.has()); + ASSERT_TRUE(actor.has()); + ASSERT_TRUE(actor.has()); + ASSERT_FALSE(actor.has()); - actor.remove(); + actor.remove(); - ASSERT_TRUE(registry.empty()); + ASSERT_TRUE(registry.empty()); ASSERT_FALSE(registry.empty()); - ASSERT_FALSE(actor.has()); + ASSERT_FALSE(actor.has()); } TEST(Actor, EntityLifetime) { entt::registry<> registry; auto *actor = new entt::actor{registry}; - actor->assign(); + actor->assign(); - ASSERT_FALSE(registry.empty()); + ASSERT_FALSE(registry.empty()); ASSERT_FALSE(registry.empty()); registry.each([actor](const auto entity) { @@ -45,6 +55,6 @@ TEST(Actor, EntityLifetime) { delete actor; - ASSERT_TRUE(registry.empty()); + ASSERT_TRUE(registry.empty()); ASSERT_TRUE(registry.empty()); } diff --git a/test/entt/entity/prototype.cpp b/test/entt/entity/prototype.cpp index 8c59c61d9..d21a95205 100644 --- a/test/entt/entity/prototype.cpp +++ b/test/entt/entity/prototype.cpp @@ -21,6 +21,14 @@ TEST(Prototype, SameRegistry) { ASSERT_EQ(std::get<0>(prototype.get()), 3); ASSERT_EQ(std::get<1>(cprototype.get()), 'c'); + ASSERT_NE(prototype.get_if(), nullptr); + ASSERT_NE(prototype.get_if(), nullptr); + ASSERT_EQ(prototype.get_if(), nullptr); + ASSERT_EQ(*prototype.get_if(), 3); + ASSERT_EQ(*cprototype.get_if(), 'c'); + ASSERT_EQ(*std::get<0>(prototype.get_if()), 3); + ASSERT_EQ(*std::get<1>(cprototype.get_if()), 'c'); + const auto e0 = prototype.create(); ASSERT_TRUE((prototype.has())); diff --git a/test/entt/entity/registry.cpp b/test/entt/entity/registry.cpp index 54a752d48..12018a9fd 100644 --- a/test/entt/entity/registry.cpp +++ b/test/entt/entity/registry.cpp @@ -74,6 +74,13 @@ TEST(Registry, Functionalities) { ASSERT_FALSE((registry.has(e0))); ASSERT_TRUE((registry.has(e1))); + ASSERT_EQ(registry.get_if(e0), nullptr); + ASSERT_NE(registry.get_if(e1), nullptr); + ASSERT_EQ(registry.get_if(e0), nullptr); + ASSERT_NE(registry.get_if(e1), nullptr); + ASSERT_EQ(registry.get_if(e0), nullptr); + ASSERT_EQ(registry.get_if(e1), nullptr); + ASSERT_EQ(registry.assign(e0, 42), 42); ASSERT_EQ(registry.assign(e0, 'c'), 'c'); ASSERT_NO_THROW(registry.remove(e1)); @@ -96,8 +103,16 @@ TEST(Registry, Functionalities) { ASSERT_EQ(registry.get(e0), 42); ASSERT_EQ(registry.get(e0), 'c'); + ASSERT_NE(registry.get_if(e0), nullptr); + ASSERT_NE(registry.get_if(e0), nullptr); + ASSERT_EQ(registry.get_if(e0), nullptr); + ASSERT_EQ(*registry.get_if(e0), 42); + ASSERT_EQ(*registry.get_if(e0), 'c'); + ASSERT_EQ(std::get<0>(registry.get(e0)), 42); + ASSERT_EQ(*std::get<0>(registry.get_if(e0)), 42); ASSERT_EQ(std::get<1>(static_cast &>(registry).get(e0)), 'c'); + ASSERT_EQ(*std::get<1>(static_cast &>(registry).get_if(e0)), 'c'); ASSERT_EQ(registry.get(e0), registry.get(e2)); ASSERT_EQ(registry.get(e0), registry.get(e2));