Truly invalid entt::handle (#551)
This commit is contained in:
@@ -674,10 +674,15 @@ the requested method, passing on the arguments if necessary.
|
||||
|
||||
### 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
|
||||
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
|
||||
around without affecting its entity (in fact, handles happen to be trivially
|
||||
copyable). An implication of this is that mutability becomes part of the
|
||||
|
||||
@@ -27,31 +27,36 @@ struct basic_handle {
|
||||
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.
|
||||
* @param ref An instance of the registry class.
|
||||
* @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}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
[[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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @return The entity associated with the handle.
|
||||
* @param value he entity associated with the handle.
|
||||
*/
|
||||
[[nodiscard]] entity_type entity() const ENTT_NOEXCEPT {
|
||||
return entt;
|
||||
@@ -253,7 +266,7 @@ struct basic_handle {
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type *reg;
|
||||
registry_type *reg;
|
||||
entity_type entt;
|
||||
};
|
||||
|
||||
|
||||
@@ -36,18 +36,141 @@ TEST(BasicHandle, Construction) {
|
||||
|
||||
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_NE(entity, 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) {
|
||||
entt::registry registry;
|
||||
const auto entity = registry.create();
|
||||
|
||||
Reference in New Issue
Block a user