From 612017aaa2e8592fa4b0e5673d3c2fdc3e367ece Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Mon, 11 Jun 2018 08:23:07 +0200 Subject: [PATCH] null entity --- README.md | 41 +++++++++++------ src/entt/entity/entity.hpp | 87 +++++++++++++++++++++++++++++++++++++ src/entt/entt.hpp | 1 + test/CMakeLists.txt | 1 + test/entt/entity/entity.cpp | 27 ++++++++++++ 5 files changed, 144 insertions(+), 13 deletions(-) create mode 100644 src/entt/entity/entity.hpp create mode 100644 test/entt/entity/entity.cpp diff --git a/README.md b/README.md index e21a2e11d..b567fd82b 100644 --- a/README.md +++ b/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.
-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(entt::break_t{}, registry.construction()); ``` +### Null entity + +In `EnTT`, there exists a sort of _null entity_ made available to users that is +accessible via the `entt::null` variable.
+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.
+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?
diff --git a/src/entt/entity/entity.hpp b/src/entt/entity/entity.hpp new file mode 100644 index 000000000..0e24a4238 --- /dev/null +++ b/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 +static constexpr auto null = ~typename entt_traits::entity_type{}; + + +struct Null { + explicit constexpr Null() = default; + + template + constexpr operator Entity() const ENTT_NOEXCEPT { + return null; + } + + constexpr bool operator==(Null) const ENTT_NOEXCEPT { + return true; + } + + constexpr bool operator!=(Null) const ENTT_NOEXCEPT { + return false; + } + + template + constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { + return entity == null; + } + + template + constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { + return entity != null; + } +}; + + +template +constexpr bool operator==(const Entity entity, Null null) ENTT_NOEXCEPT { + return null == entity; +} + + +template +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 diff --git a/src/entt/entt.hpp b/src/entt/entt.hpp index 78c86d9e3..b9df9341f 100644 --- a/src/entt/entt.hpp +++ b/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" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c958f1f23..697a7d3a3 100644 --- a/test/CMakeLists.txt +++ b/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) diff --git a/test/entt/entity/entity.cpp b/test/entt/entity/entity.cpp new file mode 100644 index 000000000..ac9d3954d --- /dev/null +++ b/test/entt/entity/entity.cpp @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +template +struct S {}; + +TEST(Traits, Null) { + entt::DefaultRegistry registry{}; + + const auto entity = registry.create(); + registry.assign(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)); +}