Forráskód Böngészése

Truly invalid entt::handle (#551)

Vennor 5 éve
szülő
commit
897850c3eb
3 módosított fájl, 167 hozzáadás és 26 törlés
  1. 7 2
      docs/md/entity.md
  2. 32 19
      src/entt/entity/handle.hpp
  3. 128 5
      test/entt/entity/handle.cpp

+ 7 - 2
docs/md/entity.md

@@ -674,10 +674,15 @@ the requested method, passing on the arguments if necessary.
 
 
 ### Handle
 ### Handle
 
 
-A handle is a thin wrapper around an entity and a registry. It provides the same
-functions that the registry offers for working with components, such as
+A handle is a thin wrapper around an entity and a registry. It provides the
+same functions that the registry offers for working with components, such as
 `emplace`, `get`, `patch`, `remove` and so on. The difference being that the
 `emplace`, `get`, `patch`, `remove` and so on. The difference being that the
 entity is implicitly passed to the registry.<br/>
 entity is implicitly passed to the registry.<br/>
+It's default constructible as an invalid handle that contains a null registry
+and a null entity. When it contains a null registry calling functions that
+delegate execution to the registry will cause a crash, so it's recommended to
+check the validity of the handle with implicit cast to `bool` when in doubt.
+<br/>
 A handle is also non-owning, meaning that it can be freely copied and moved
 A handle is also non-owning, meaning that it can be freely copied and moved
 around without affecting its entity (in fact, handles happen to be trivially
 around without affecting its entity (in fact, handles happen to be trivially
 copyable). An implication of this is that mutability becomes part of the
 copyable). An implication of this is that mutability becomes part of the

+ 32 - 19
src/entt/entity/handle.hpp

@@ -27,31 +27,36 @@ struct basic_handle {
         basic_registry<entity_type>
         basic_registry<entity_type>
     >;
     >;
 
 
+    /**
+     * @brief Constructs a default invalid handle.
+     */
+    basic_handle() ENTT_NOEXCEPT
+        : reg{nullptr}, entt{null}
+    {}
+
     /**
     /**
      * @brief Constructs a handle from a given registry and entity.
      * @brief Constructs a handle from a given registry and entity.
      * @param ref An instance of the registry class.
      * @param ref An instance of the registry class.
      * @param value An entity identifier.
      * @param value An entity identifier.
      */
      */
-    basic_handle(registry_type &ref, entity_type value = null) ENTT_NOEXCEPT
+    basic_handle(registry_type &ref, entity_type value) ENTT_NOEXCEPT
         : reg{&ref}, entt{value}
         : reg{&ref}, entt{value}
     {}
     {}
 
 
     /**
     /**
-     * @brief Assigns an entity to a handle.
-     * @param value An entity identifier.
-     * @return This handle.
+     * @brief Compares two handles.
+     * @return True if both handles refer to the same registry and the same entity, false otherwise.
      */
      */
-    basic_handle & operator=(const entity_type value) ENTT_NOEXCEPT {
-        entt = value;
-        return *this;
+    bool operator==(const basic_handle<entity_type> &other) const ENTT_NOEXCEPT {
+        return reg == other.registry() && entt == other.entity();
     }
     }
 
 
     /**
     /**
-     * @brief Assigns the null object to a handle.
-     * @return This handle.
+     * @brief Compares two handles.
+     * @return False if both handles refer to the same registry and the same entity, true otherwise.
      */
      */
-    basic_handle & operator=(null_t) ENTT_NOEXCEPT {
-        return (*this = static_cast<entity_type>(null));
+    bool operator!=(const basic_handle<entity_type> &other) const ENTT_NOEXCEPT {
+        return !( *this == other);
     }
     }
 
 
     /**
     /**
@@ -59,7 +64,7 @@ struct basic_handle {
      * @return A const handle referring to the same entity.
      * @return A const handle referring to the same entity.
      */
      */
     [[nodiscard]] operator basic_handle<const entity_type>() const ENTT_NOEXCEPT {
     [[nodiscard]] operator basic_handle<const entity_type>() const ENTT_NOEXCEPT {
-        return {*reg, entt};
+        return reg ? basic_handle<const entity_type>{ *reg, entt} : basic_handle<const entity_type>{};
     }
     }
 
 
     /**
     /**
@@ -70,25 +75,33 @@ struct basic_handle {
         return entity();
         return entity();
     }
     }
 
 
+    /**
+     * @brief Checks if a handle refers to non-null registry pointer and entity.
+     * @return True if the handle refers to non-null registry and entity, false otherwise.
+     */
+    [[nodiscard]] explicit operator bool() const {
+        return reg && entt != null;
+    }
+
     /**
     /**
      * @brief Checks if a handle refers to a valid entity or not.
      * @brief Checks if a handle refers to a valid entity or not.
      * @return True if the handle refers to a valid entity, false otherwise.
      * @return True if the handle refers to a valid entity, false otherwise.
      */
      */
-    [[nodiscard]] explicit operator bool() const {
+    [[nodiscard]] bool valid() const {
         return reg->valid(entt);
         return reg->valid(entt);
     }
     }
 
 
     /**
     /**
-     * @brief Returns a reference to the underlying registry.
-     * @return A reference to the underlying registry.
+     * @brief Returns a pointer to the underlying registry.
+     * @return A pointer to the underlying registry.
      */
      */
-    [[nodiscard]] registry_type & registry() const ENTT_NOEXCEPT {
-        return *reg;
+    [[nodiscard]] registry_type  * registry() const ENTT_NOEXCEPT {
+        return reg;
     }
     }
 
 
     /**
     /**
      * @brief Returns the entity associated with a handle.
      * @brief Returns the entity associated with a handle.
-     * @return The entity associated with the handle.
+     * @param value he entity associated with the handle.
      */
      */
     [[nodiscard]] entity_type entity() const ENTT_NOEXCEPT {
     [[nodiscard]] entity_type entity() const ENTT_NOEXCEPT {
         return entt;
         return entt;
@@ -253,7 +266,7 @@ struct basic_handle {
     }
     }
 
 
 private:
 private:
-    registry_type *reg;
+    registry_type  *reg;
     entity_type entt;
     entity_type entt;
 };
 };
 
 

+ 128 - 5
test/entt/entity/handle.cpp

@@ -36,18 +36,141 @@ TEST(BasicHandle, Construction) {
 
 
     ASSERT_EQ(handle, chandle);
     ASSERT_EQ(handle, chandle);
 
 
-    static_assert(std::is_same_v<entt::registry &, decltype(handle.registry())>);
-    static_assert(std::is_same_v<const entt::registry &, decltype(chandle.registry())>);
+    static_assert(std::is_same_v<entt::registry *, decltype(handle.registry())>);
+    static_assert(std::is_same_v<const entt::registry *, decltype(chandle.registry())>);
+}
+
 
 
-    handle = entt::null;
+TEST(BasicHandle, Invalidation) {
+    entt::handle handle;
 
 
+    ASSERT_TRUE(nullptr == handle.registry());
     ASSERT_TRUE(entt::null == handle.entity());
     ASSERT_TRUE(entt::null == handle.entity());
-    ASSERT_NE(entity, handle);
     ASSERT_FALSE(handle);
     ASSERT_FALSE(handle);
 
 
-    ASSERT_NE(handle, chandle);
+    entt::registry registry;
+    const auto entity = registry.create();
+
+    handle = {registry, entity};
+
+    ASSERT_FALSE(nullptr == handle.registry());
+    ASSERT_FALSE(entt::null == handle.entity());
+    ASSERT_TRUE(handle);
+
+    handle = {};
+
+    ASSERT_TRUE(nullptr == handle.registry());
+    ASSERT_TRUE(entt::null == handle.entity());
+    ASSERT_FALSE(handle);
+}
+
+
+TEST(BasicHandle, Comparison) {
+    entt::registry registry;
+    const auto entity1 = registry.create();
+    const auto entity2 = registry.create();
+
+    entt::handle handle1{registry, entity1};
+    entt::handle handle2{registry, entity2};
+    entt::const_handle chandle1 = handle1;
+    entt::const_handle chandle2 = handle2;
+
+    ASSERT_NE(handle1, handle2);
+    ASSERT_FALSE(handle1 == handle2);
+    ASSERT_TRUE(handle1 != handle2);
+
+    ASSERT_NE(chandle1, chandle2);
+    ASSERT_FALSE(chandle1 == chandle2);
+    ASSERT_TRUE(chandle1 != chandle2);
+
+    ASSERT_EQ(handle1, chandle1);
+    ASSERT_TRUE(handle1 == chandle1);
+    ASSERT_FALSE(handle1 != chandle1);
+
+    ASSERT_EQ(handle2, chandle2);
+    ASSERT_TRUE(handle2 == chandle2);
+    ASSERT_FALSE(handle2 != chandle2);
+
+    ASSERT_NE(handle1, chandle2);
+    ASSERT_FALSE(handle1 == chandle2);
+    ASSERT_TRUE(handle1 != chandle2);
+
+    handle1 = {};
+    chandle2 = {};
+
+    ASSERT_NE(handle1, handle2);
+    ASSERT_FALSE(handle1 == handle2);
+    ASSERT_TRUE(handle1 != handle2);
+
+    ASSERT_NE(chandle1, chandle2);
+    ASSERT_FALSE(chandle1 == chandle2);
+    ASSERT_TRUE(chandle1 != chandle2);
+
+    ASSERT_NE(handle1, chandle1);
+    ASSERT_FALSE(handle1 == chandle1);
+    ASSERT_TRUE(handle1 != chandle1);
+
+    ASSERT_NE(handle2, chandle2);
+    ASSERT_FALSE(handle2 == chandle2);
+    ASSERT_TRUE(handle2 != chandle2);
+
+    ASSERT_EQ(handle1, chandle2);
+    ASSERT_TRUE(handle1 == chandle2);
+    ASSERT_FALSE(handle1 != chandle2);
+
+    handle2 = {};
+    chandle1 = {};
+
+    ASSERT_EQ(handle1, handle2);
+    ASSERT_TRUE(handle1 == handle2);
+    ASSERT_FALSE(handle1 != handle2);
+
+    ASSERT_EQ(chandle1, chandle2);
+    ASSERT_TRUE(chandle1 == chandle2);
+    ASSERT_FALSE(chandle1 != chandle2);
+
+    ASSERT_EQ(handle1, chandle1);
+    ASSERT_TRUE(handle1 == chandle1);
+    ASSERT_FALSE(handle1 != chandle1);
+
+    ASSERT_EQ(handle2, chandle2);
+    ASSERT_TRUE(handle2 == chandle2);
+    ASSERT_FALSE(handle2 != chandle2);
+
+    ASSERT_EQ(handle1, chandle2);
+    ASSERT_TRUE(handle1 == chandle2);
+    ASSERT_FALSE(handle1 != chandle2);
+
+    entt::registry registry_b;
+    const auto entity_b1 = registry.create();
+
+    handle1 = {registry_b, entity_b1};
+    handle2 = {registry, entity1};
+    chandle1 = handle1;
+    chandle2 = handle2;
+
+    ASSERT_NE(handle1, handle2);
+    ASSERT_FALSE(handle1 == handle2);
+    ASSERT_TRUE(handle1 != handle2);
+
+    ASSERT_NE(chandle1, chandle2);
+    ASSERT_FALSE(chandle1 == chandle2);
+    ASSERT_TRUE(chandle1 != chandle2);
+
+    ASSERT_EQ(handle1, chandle1);
+    ASSERT_TRUE(handle1 == chandle1);
+    ASSERT_FALSE(handle1 != chandle1);
+
+    ASSERT_EQ(handle2, chandle2);
+    ASSERT_TRUE(handle2 == chandle2);
+    ASSERT_FALSE(handle2 != chandle2);
+
+    ASSERT_NE(handle1, chandle2);
+    ASSERT_FALSE(handle1 == chandle2);
+    ASSERT_TRUE(handle1 != chandle2);
 }
 }
 
 
+
 TEST(BasicHandle, Component) {
 TEST(BasicHandle, Component) {
     entt::registry registry;
     entt::registry registry;
     const auto entity = registry.create();
     const auto entity = registry.create();