cloning an entity is no longer allowed

This commit is contained in:
Michele Caini
2018-06-04 08:49:13 +02:00
parent 610b560fb5
commit e34bec7dee
5 changed files with 18 additions and 153 deletions

View File

@@ -396,25 +396,19 @@ Entities are represented by _entity identifiers_. An entity identifier is an
opaque type that users should not inspect or modify in any way. It carries
information about the entity itself and its version.
A registry can be used both to construct and destroy entities, as well as to
clone already existing entities:
A registry can be used both to construct and destroy entities:
```cpp
// constructs a naked entity with no components and returns its identifier
auto entity = registry.create();
// clones an entity and assigns all its components by copy to the newly created one
auto other = registry.clone(entity);
// destroys an entity and all its components
registry.destroy(entity);
```
Be aware that cloning an entity can lead to unexpected results under certain
conditions. Please refer to the inline documentation for more details.<br/>
Once an entity is deleted, the registry can freely reuse it internally with a
When an entity is destroyed, the registry can freely reuse it internally with a
slightly different identifier. In particular, the version of an entity is
increased each and every time it's destroyed.<br/>
increased each and every time it's discarded.<br/>
In case entity identifiers are stored around, the registry offers all the
functionalities required to test them and get out of the them all the
information they carry:

View File

@@ -408,51 +408,6 @@ public:
return entity;
}
/**
* @brief Clones an entity and returns the newly created one.
*
* There are two kinds of entity identifiers:
*
* * Newly created ones in case no entities have been previously destroyed.
* * Recycled ones with updated versions.
*
* Users should not care about the type of the returned entity identifier.
* In case entity identifers are stored around, the `valid` member
* function can be used to know if they are still valid or the entity has
* been destroyed and potentially recycled.
*
* @warning
* In case there are listeners that observe the construction of components
* and assign other components to the entity in their bodies, the result of
* invoking this function may not be as expected. In the worst case, it
* could lead to undefined behavior. An assertion will abort the execution
* at runtime in debug mode if a violation is detected.
*
* @warning
* Attempting to clone an invalid entity results in undefined behavior.<br/>
* An assertion will abort the execution at runtime in debug mode in case of
* invalid entity.
*
* @param entity A valid entity identifier
* @return A valid entity identifier.
*/
entity_type clone(const entity_type entity) ENTT_NOEXCEPT {
assert(valid(entity));
const auto other = create();
for(auto pos = pools.size(); pos; --pos) {
auto &tup = pools[pos-1];
auto &cpool = std::get<0>(tup);
if(cpool && cpool->has(entity)) {
cpool->clone(other, entity);
std::get<1>(tup).publish(*this, other);
}
}
return other;
}
/**
* @brief Destroys an entity and lets the registry recycle the identifier.
*

View File

@@ -377,28 +377,6 @@ public:
direct.push_back(entity);
}
/**
* @brief Assigns an entity to a sparse set by cloning another entity.
*
* @warning
* Attempting to clone an entity that doesn't belong to the sparse set or to
* assign an entity that already belongs to the sparse set results in
* undefined behavior.<br/>
* An assertion will abort the execution at runtime in debug mode if the
* sparse set doesn't contain the entity to clone or if it already contains
* the given entity.
*
* @param entity A valid entity identifier.
* @param source A valid entity identifier from which to clone.
*/
inline virtual void clone(const entity_type entity, const entity_type source) {
assert(has(source));
assert(!has(entity));
construct(entity);
// useful to suppress warnings when asserts are disabled
(void)source;
}
/**
* @brief Removes an entity from a sparse set.
*
@@ -842,24 +820,6 @@ public:
return instances.back();
}
/**
* @brief Assigns an entity to a sparse set by cloning another entity.
*
* @warning
* Attempting to clone an entity that doesn't belong to the sparse set or to
* assign an entity that already belongs to the sparse set results in
* undefined behavior.<br/>
* An assertion will abort the execution at runtime in debug mode if the
* sparse set doesn't contain the entity to clone or if it already contains
* the given entity.
*
* @param entity A valid entity identifier.
* @param source A valid entity identifier from which to clone.
*/
inline void clone(const entity_type entity, const entity_type source) override {
construct(entity, get(source));
}
/**
* @brief Removes an entity from a sparse set and destroies its object.
*

View File

@@ -354,23 +354,6 @@ TEST(DefaultRegistry, CreateDestroyEntities) {
ASSERT_EQ(registry.current(pre), registry.current(post));
}
TEST(DefaultRegistry, CloneEntities) {
entt::DefaultRegistry registry;
const auto entity = registry.create();
registry.assign<int>(entity, 42);
registry.assign<char>(entity, 'c');
const auto other = registry.clone(entity);
ASSERT_TRUE(registry.has<int>(other));
ASSERT_TRUE(registry.has<char>(other));
ASSERT_EQ(registry.get<int>(entity), registry.get<int>(other));
ASSERT_EQ(registry.get<char>(entity), registry.get<char>(other));
ASSERT_EQ(registry.get<int>(other), 42);
ASSERT_EQ(registry.get<char>(other), 'c');
}
TEST(DefaultRegistry, AttachSetRemoveTags) {
entt::DefaultRegistry registry;
const auto &cregistry = registry;
@@ -719,11 +702,6 @@ TEST(DefaultRegistry, ComponentSignals) {
ASSERT_EQ(listener.counter, 1);
ASSERT_EQ(listener.last, e1);
e1 = registry.clone(e0);
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, e1);
}
TEST(DefaultRegistry, TagSignals) {

View File

@@ -54,23 +54,6 @@ TEST(SparseSetNoType, Functionalities) {
other = std::move(set);
}
TEST(SparseSetNoType, Clone) {
entt::SparseSet<unsigned int> set;
ASSERT_FALSE(set.has(0));
ASSERT_FALSE(set.has(42));
set.construct(0);
ASSERT_TRUE(set.has(0));
ASSERT_FALSE(set.has(42));
set.clone(42, 0);
ASSERT_TRUE(set.has(0));
ASSERT_TRUE(set.has(42));
}
TEST(SparseSetNoType, DataBeginEnd) {
entt::SparseSet<unsigned int> set;
@@ -324,26 +307,6 @@ TEST(SparseSetWithType, Functionalities) {
other = std::move(set);
}
TEST(SparseSetWithType, Clone) {
entt::SparseSet<unsigned int, int> set;
ASSERT_FALSE(set.has(0));
ASSERT_FALSE(set.has(42));
set.construct(0, 3);
ASSERT_TRUE(set.has(0));
ASSERT_FALSE(set.has(42));
ASSERT_EQ(set.get(0), 3);
set.clone(42, 0);
ASSERT_TRUE(set.has(0));
ASSERT_TRUE(set.has(42));
ASSERT_EQ(set.get(0), set.get(42));
ASSERT_EQ(set.get(42), 3);
}
TEST(SparseSetWithType, AggregatesMustWork) {
struct AggregateType { int value; };
// the goal of this test is to enforce the requirements for aggregate types
@@ -723,3 +686,18 @@ TEST(SparseSetWithType, ReferencesGuaranteed) {
ASSERT_EQ(set.get(0).value, 3);
ASSERT_EQ(set.get(1).value, 3);
}
TEST(SparseSetWithType, MoveOnlyComponent) {
struct MoveOnlyComponent {
MoveOnlyComponent() = default;
~MoveOnlyComponent() = default;
MoveOnlyComponent(const MoveOnlyComponent &) = delete;
MoveOnlyComponent(MoveOnlyComponent &&) = default;
MoveOnlyComponent & operator=(const MoveOnlyComponent &) = delete;
MoveOnlyComponent & operator=(MoveOnlyComponent &&) = default;
};
// it's purpose is to ensure that move only components are always accepted
entt::SparseSet<unsigned int, MoveOnlyComponent> set;
(void)set;
}