Michele Caini před 7 roky
rodič
revize
612017aaa2
5 změnil soubory, kde provedl 144 přidání a 13 odebrání
  1. 28 13
      README.md
  2. 87 0
      src/entt/entity/entity.hpp
  3. 1 0
      src/entt/entt.hpp
  4. 1 0
      test/CMakeLists.txt
  5. 27 0
      test/entt/entity/entity.cpp

+ 28 - 13
README.md

@@ -425,19 +425,6 @@ auto version = registry.version(entity);
 auto curr = registry.current(entity);
 ```
 
-Finally, there is also a sort of _null identifier_ made available to users. It's
-treated as if it were a _null pointer_ that doesn't identify any entity. A
-registry will reject this identifier in all cases because it isn't considered
-valid.<br/>
-The rules that define a _null identifier_ are a bit tricky to explain. However,
-being `Entity` the type of the entities (for example, `std::uint32_t`), users
-can easily construct a _null identifier_ by flipping all the bits of the _zero_:
-
-```cpp
-using Entity = std::uint32_t;
-const auto null = ~Entity{};
-```
-
 Components can be assigned to or removed from entities at any time with a few
 calls to member functions of the registry. As for the entities, the registry
 offers also a set of functionalities users can use to work with the components.
@@ -1136,6 +1123,34 @@ A dependency can easily be broken by means of the same function template:
 entt::dependency<AType, AnotherType>(entt::break_t{}, registry.construction<MyType>());
 ```
 
+### Null entity
+
+In `EnTT`, there exists a sort of _null entity_ made available to users that is
+accessible via the `entt::null` variable.<br/>
+The framework guarantees that the following expression always returns false:
+
+```cpp
+registry.valid(entt::null);
+```
+
+In other terms, a registry will reject the null entity in all cases because it
+isn't considered valid. It means that the null entity cannot own components or
+tags for obvious reasons.<br/>
+The type of the null entity is internal and should not be used for any purpose
+other than defining the null entity itself. However, there exist implicit
+conversions from the null entity to identifiers of any allowed type:
+
+```cpp
+typename entt::DefaultRegistry::entity_type null = entt::null;
+```
+
+Similarly, the null entity can be compared to any other identifier:
+
+```cpp
+const auto entity = registry.create();
+const bool null = (entity == entt::null);
+```
+
 ## View: to persist or not to persist?
 
 First of all, it is worth answering an obvious question: why views?<br/>

+ 87 - 0
src/entt/entity/entity.hpp

@@ -0,0 +1,87 @@
+#ifndef ENTT_ENTITY_ENTITY_HPP
+#define ENTT_ENTITY_ENTITY_HPP
+
+
+#include "../config/config.h"
+#include "entt_traits.hpp"
+
+
+namespace entt {
+
+
+namespace internal {
+
+
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+
+template<typename Entity>
+static constexpr auto null = ~typename entt_traits<Entity>::entity_type{};
+
+
+struct Null {
+    explicit constexpr Null() = default;
+
+    template<typename Entity>
+    constexpr operator Entity() const ENTT_NOEXCEPT {
+        return null<Entity>;
+    }
+
+    constexpr bool operator==(Null) const ENTT_NOEXCEPT {
+        return true;
+    }
+
+    constexpr bool operator!=(Null) const ENTT_NOEXCEPT {
+        return false;
+    }
+
+    template<typename Entity>
+    constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
+        return entity == null<Entity>;
+    }
+
+    template<typename Entity>
+    constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
+        return entity != null<Entity>;
+    }
+};
+
+
+template<typename Entity>
+constexpr bool operator==(const Entity entity, Null null) ENTT_NOEXCEPT {
+    return null == entity;
+}
+
+
+template<typename Entity>
+constexpr bool operator!=(const Entity entity, Null null) ENTT_NOEXCEPT {
+    return null != entity;
+}
+
+
+/**
+ * Internal details not to be documented.
+ * @endcond TURN_OFF_DOXYGEN
+ */
+
+
+}
+
+
+/**
+ * @brief Null entity.
+ *
+ * There exist implicit conversions from this variable to entity identifiers of
+ * any allowed type. Similarly, there exist comparision operators between the
+ * null entity and any other entity identifier.
+ */
+constexpr auto null = internal::Null{};
+
+
+}
+
+
+#endif // ENTT_ENTITY_ENTITY_HPP

+ 1 - 0
src/entt/entt.hpp

@@ -3,6 +3,7 @@
 #include "core/hashed_string.hpp"
 #include "core/ident.hpp"
 #include "entity/actor.hpp"
+#include "entity/entity.hpp"
 #include "entity/entt_traits.hpp"
 #include "entity/helper.hpp"
 #include "entity/prototype.hpp"

+ 1 - 0
test/CMakeLists.txt

@@ -53,6 +53,7 @@ ADD_ENTT_TEST(ident entt/core/ident.cpp)
 # Test entity
 
 ADD_ENTT_TEST(actor entt/entity/actor.cpp)
+ADD_ENTT_TEST(entity entt/entity/entity.cpp)
 ADD_ENTT_TEST(helper entt/entity/helper.cpp)
 ADD_ENTT_TEST(prototype entt/entity/prototype.cpp)
 ADD_ENTT_TEST(registry entt/entity/registry.cpp)

+ 27 - 0
test/entt/entity/entity.cpp

@@ -0,0 +1,27 @@
+#include <functional>
+#include <gtest/gtest.h>
+#include <entt/entity/entity.hpp>
+#include <entt/entity/registry.hpp>
+
+template<bool>
+struct S {};
+
+TEST(Traits, Null) {
+    entt::DefaultRegistry registry{};
+
+    const auto entity = registry.create();
+    registry.assign<int>(entity, 42);
+
+    ASSERT_TRUE(~typename entt::DefaultRegistry::entity_type{} == entt::null);
+
+    ASSERT_TRUE(entt::null == entt::null);
+    ASSERT_FALSE(entt::null != entt::null);
+
+    ASSERT_FALSE(entity == entt::null);
+    ASSERT_FALSE(entt::null == entity);
+
+    ASSERT_TRUE(entity != entt::null);
+    ASSERT_TRUE(entt::null != entity);
+
+    ASSERT_FALSE(registry.valid(entt::null));
+}