Browse Source

registry:
* deprecated ::destroyed (use ::released instead)
* added ::release overloads (aka force-destroy)

Michele Caini 4 years ago
parent
commit
1f8c896181

+ 21 - 9
docs/md/entity.md

@@ -207,10 +207,22 @@ auto view = registry.view<a_component, another_component>();
 registry.destroy(view.begin(), view.end());
 ```
 
-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 after destruction (unless the overload that forces a version is used
-instead of the default one).<br/>
+In addition to offering an overload to force the version upon destruction. Note
+that this function removes all components from an entity before releasing its
+identifier. There exists also a _lighter_ alternative that only releases the
+elements without poking in any pool, for use with orphaned entities:
+
+```cpp
+// releases an orphaned identifier
+registry.release(entity);
+```
+
+As with the `destroy` function, also in this case entity ranges are supported
+and it's possible to force the version during release.
+
+In both cases, when an identifier is released, the registry can freely reuse it
+internally. In particular, the version of an entity is increased (unless the
+overload that forces a version is used instead of the default one).<br/>
 Users can probe an identifier to know the information it carries:
 
 ```cpp
@@ -648,7 +660,7 @@ Also in this case, the following expression always returns false:
 registry.valid(entt::tombstone);
 ```
 
-Moreover, users cannot set set the tombstone version when deleting an entity:
+Moreover, users cannot set set the tombstone version when releasing an entity:
 
 ```
 registry.destroy(entity, entt::tombstone);
@@ -1169,7 +1181,7 @@ to use in which case mostly depends on the goal and there is not a golden rule
 for that.
 
 The `entities` member function makes the snapshot serialize all entities (both
-those still alive and those destroyed) along with their versions.<br/>
+those still alive and those released) along with their versions.<br/>
 On the other hand, the `component` member function is a function template the
 aim of which is to store aside components. The presence of a template parameter
 list is a consequence of a couple of design choices from the past and in the
@@ -1234,11 +1246,11 @@ The `component` member function restores all and only the components specified
 and assigns them to the right entities. Note that the template parameter list
 must be exactly the same used during the serialization.
 
-The `orphans` member function literally destroys those entities that have no
+The `orphans` member function literally releases those entities that have no
 components attached. It's usually useless if the snapshot is a full dump of the
 source. However, in case all the entities are serialized but only few components
 are saved, it could happen that some of the entities have no components once
-restored. The best the users can do to deal with them is to destroy those
+restored. The best the users can do to deal with them is to release those
 entities and thus update their versions.
 
 ### Continuous loader
@@ -1285,7 +1297,7 @@ In case the component contains entities itself (either as data members of type
 automatically. To do that, it's enough to specify the data members to update as
 shown in the example.
 
-The `orphans` member function literally destroys those entities that have no
+The `orphans` member function literally releases those entities that have no
 components after a restore. It has exactly the same purpose described in the
 previous section and works the same way.
 

+ 61 - 7
src/entt/entity/registry.hpp

@@ -341,17 +341,23 @@ public:
     }
 
     /**
-     * @brief Returns the head of the list of destroyed entities.
+     * @brief Returns the head of the list of released entities.
      *
      * This function is intended for use in conjunction with `assign`.<br/>
      * The returned entity has an invalid identifier in all cases.
      *
-     * @return The head of the list of destroyed entities.
+     * @return The head of the list of released entities.
      */
-    [[nodiscard]] entity_type destroyed() const ENTT_NOEXCEPT {
+    [[nodiscard]] entity_type released() const ENTT_NOEXCEPT {
         return free_list;
     }
 
+    /*! @copydoc released */
+    [[deprecated("Use ::released instead")]]
+    [[nodiscard]] entity_type destroyed() const ENTT_NOEXCEPT {
+        return released();
+    }
+
     /**
      * @brief Checks if an entity identifier refers to a valid entity.
      * @param entity An entity identifier, either valid or not.
@@ -475,7 +481,55 @@ public:
     }
 
     /**
-     * @brief Destroys an entity.
+     * @brief Releases an entity identifier.
+     *
+     * The version is updated and the identifier can be recycled at any time.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.
+     *
+     * @param entity A valid entity identifier.
+     * @return The version of the recycled entity.
+     */
+    version_type release(const entity_type entity) {
+        return release(entity, version(entity) + 1u);
+    }
+
+    /**
+     * @brief Releases an entity identifier.
+     *
+     * The suggested version or the valid version closest to the suggested one
+     * is used instead of the implicitly generated version.
+     *
+     * @sa release
+     *
+     * @param entity A valid entity identifier.
+     * @param version A desired version upon destruction.
+     * @return The version actually assigned to the entity.
+     */
+    version_type release(const entity_type entity, const version_type version) {
+        ENTT_ASSERT(orphan(entity), "Non-orphan entity");
+        return release_entity(entity, version);
+    }
+
+    /**
+     * @brief Releases all entity identifiers in a range.
+     *
+     * @sa release
+     *
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+    template<typename It>
+    void release(It first, It last) {
+        for(; first != last; ++first) {
+            release(*first, version(*first) + 1u);
+        }
+    }
+
+    /**
+     * @brief Destroys an entity and releases its identifier.
      *
      * The version is updated and the identifier can be recycled at any time.
      *
@@ -490,11 +544,11 @@ public:
      * @return The version of the recycled entity.
      */
     version_type destroy(const entity_type entity) {
-        return destroy(entity, traits_type::to_version(entity) + 1u);
+        return destroy(entity, version(entity) + 1u);
     }
 
     /**
-     * @brief Destroys an entity.
+     * @brief Destroys an entity and releases its identifier.
      *
      * The suggested version or the valid version closest to the suggested one
      * is used instead of the implicitly generated version.
@@ -516,7 +570,7 @@ public:
     }
 
     /**
-     * @brief Destroys all the entities in a range.
+     * @brief Destroys all entities in a range and releases their identifiers.
      *
      * @sa destroy
      *

+ 3 - 3
src/entt/entity/snapshot.hpp

@@ -99,7 +99,7 @@ public:
             archive(*first);
         }
 
-        archive(reg->destroyed());
+        archive(reg->released());
 
         return *this;
     }
@@ -273,7 +273,7 @@ public:
      */
     const basic_snapshot_loader & orphans() const {
         reg->orphans([this](const auto entt) {
-            reg->destroy(entt);
+            reg->release(entt);
         });
 
         return *this;
@@ -529,7 +529,7 @@ public:
      */
     basic_continuous_loader & orphans() {
         reg->orphans([this](const auto entt) {
-            reg->destroy(entt);
+            reg->release(entt);
         });
 
         return *this;

+ 1 - 1
test/entt/entity/poly_storage.cpp

@@ -109,7 +109,7 @@ TEST(PolyStorage, CopyRegistry) {
     ASSERT_EQ(registry.size(), 10u);
     ASSERT_EQ(other.size(), 0u);
 
-    other.assign(registry.data(), registry.data() + registry.size(), registry.destroyed());
+    other.assign(registry.data(), registry.data() + registry.size(), registry.released());
     registry.visit([&](const auto info) { std::as_const(registry).storage(info)->copy_to(other); });
 
     ASSERT_EQ(registry.size(), other.size());

+ 77 - 34
test/entt/entity/registry.cpp

@@ -246,7 +246,9 @@ TEST(Registry, Functionalities) {
 
     ASSERT_EQ(registry.version(e2), 0u);
     ASSERT_EQ(registry.current(e2), 0u);
+    ASSERT_DEATH(registry.release(e2), "");
     ASSERT_NO_FATAL_FAILURE(registry.destroy(e2));
+    ASSERT_DEATH(registry.destroy(e2), "");
     ASSERT_EQ(registry.version(e2), 0u);
     ASSERT_EQ(registry.current(e2), 1u);
 
@@ -362,7 +364,7 @@ TEST(Registry, Identifiers) {
 
     ASSERT_EQ(pre, registry.entity(pre));
 
-    registry.destroy(pre);
+    registry.release(pre);
     const auto post = registry.create();
 
     ASSERT_NE(pre, post);
@@ -382,7 +384,7 @@ TEST(Registry, Data) {
     ASSERT_EQ(*std::as_const(registry).data(), entity);
 
     const auto other = registry.create();
-    registry.destroy(entity);
+    registry.release(entity);
 
     ASSERT_NE(*std::as_const(registry).data(), entity);
     ASSERT_EQ(*(std::as_const(registry).data() + 1u), other);
@@ -393,9 +395,9 @@ TEST(Registry, CreateManyEntitiesAtOnce) {
     entt::entity entities[3];
 
     const auto entity = registry.create();
-    registry.destroy(registry.create());
-    registry.destroy(entity);
-    registry.destroy(registry.create());
+    registry.release(registry.create());
+    registry.release(entity);
+    registry.release(registry.create());
 
     registry.create(std::begin(entities), std::end(entities));
 
@@ -447,7 +449,7 @@ TEST(Registry, CreateWithHint) {
     ASSERT_FALSE(registry.valid(entt::entity{1}));
     ASSERT_EQ(e3, entt::entity{3});
 
-    registry.destroy(e2);
+    registry.release(e2);
 
     ASSERT_EQ(registry.version(e2), entt::registry::version_type{});
     ASSERT_EQ(registry.current(e2), entt::registry::version_type{1});
@@ -461,31 +463,15 @@ TEST(Registry, CreateWithHint) {
     ASSERT_EQ(registry.entity(e1), entt::entity{1});
     ASSERT_EQ(registry.version(e1), entt::registry::version_type{});
 
-    registry.destroy(e1);
-    registry.destroy(e2);
+    registry.release(e1);
+    registry.release(e2);
     auto e0 = registry.create(entt::entity{0});
 
     ASSERT_EQ(e0, entt::entity{0});
     ASSERT_EQ(registry.version(e0), entt::registry::version_type{});
 }
 
-TEST(Registry, DestroyWithVersion) {
-    entt::registry registry;
-
-    const auto e0 = registry.create();
-    const auto e1 = registry.create();
-
-    ASSERT_EQ(registry.current(e0), entt::registry::version_type{});
-    ASSERT_EQ(registry.current(e1), entt::registry::version_type{});
-
-    registry.destroy(e0);
-    registry.destroy(e1, 3);
-
-    ASSERT_EQ(registry.current(e0), entt::registry::version_type{1});
-    ASSERT_EQ(registry.current(e1), entt::registry::version_type{3});
-}
-
-TEST(Registry, CreateDestroyEntities) {
+TEST(Registry, CreateClearCycle) {
     entt::registry registry;
     entt::entity pre{}, post{};
 
@@ -516,14 +502,14 @@ TEST(Registry, CreateDestroyEntities) {
     ASSERT_EQ(registry.current(pre), registry.current(post));
 }
 
-TEST(Registry, CreateDestroyCornerCase) {
+TEST(Registry, CreateDestroyReleaseCornerCase) {
     entt::registry registry;
 
     const auto e0 = registry.create();
     const auto e1 = registry.create();
 
     registry.destroy(e0);
-    registry.destroy(e1);
+    registry.release(e1);
 
     registry.each([](auto) { FAIL(); });
 
@@ -531,6 +517,24 @@ TEST(Registry, CreateDestroyCornerCase) {
     ASSERT_EQ(registry.current(e1), entt::registry::version_type{1});
 }
 
+TEST(Registry, DestroyVersion) {
+    entt::registry registry;
+
+    const auto e0 = registry.create();
+    const auto e1 = registry.create();
+
+    ASSERT_EQ(registry.current(e0), entt::registry::version_type{});
+    ASSERT_EQ(registry.current(e1), entt::registry::version_type{});
+
+    registry.destroy(e0);
+    registry.destroy(e1, 3);
+
+    ASSERT_DEATH(registry.destroy(e0), "");
+    ASSERT_DEATH(registry.destroy(e1, 3), "");
+    ASSERT_EQ(registry.current(e0), entt::registry::version_type{1});
+    ASSERT_EQ(registry.current(e1), entt::registry::version_type{3});
+}
+
 TEST(Registry, RangeDestroy) {
     entt::registry registry;
     const auto iview = registry.view<int>();
@@ -618,19 +622,58 @@ TEST(Registry, StableDestroy) {
     ASSERT_EQ(registry.size<double>(), 0u);
 }
 
+TEST(Registry, ReleaseVersion) {
+    entt::registry registry;
+
+    const auto e0 = registry.create();
+    const auto e1 = registry.create();
+
+    ASSERT_EQ(registry.current(e0), entt::registry::version_type{});
+    ASSERT_EQ(registry.current(e1), entt::registry::version_type{});
+
+    registry.release(e0);
+    registry.release(e1, 3);
+
+    ASSERT_DEATH(registry.release(e0), "");
+    ASSERT_DEATH(registry.release(e1, 3), "");
+    ASSERT_EQ(registry.current(e0), entt::registry::version_type{1});
+    ASSERT_EQ(registry.current(e1), entt::registry::version_type{3});
+}
+
+TEST(Registry, RangeRelease) {
+    entt::registry registry;
+    entt::entity entities[3u];
+
+    registry.create(std::begin(entities), std::end(entities));
+
+    ASSERT_TRUE(registry.valid(entities[0u]));
+    ASSERT_TRUE(registry.valid(entities[1u]));
+    ASSERT_TRUE(registry.valid(entities[2u]));
+
+    registry.release(std::begin(entities), std::end(entities) - 1u);
+
+    ASSERT_FALSE(registry.valid(entities[0u]));
+    ASSERT_FALSE(registry.valid(entities[1u]));
+    ASSERT_TRUE(registry.valid(entities[2u]));
+
+    registry.release(std::end(entities) - 1u, std::end(entities));
+
+    ASSERT_FALSE(registry.valid(entities[2u]));
+}
+
 TEST(Registry, VersionOverflow) {
     using traits_type = entt::entt_traits<entt::entity>;
 
     entt::registry registry;
     const auto entity = registry.create();
 
-    registry.destroy(entity);
+    registry.release(entity);
 
     ASSERT_NE(registry.current(entity), registry.version(entity));
     ASSERT_NE(registry.current(entity), typename traits_type::version_type{});
 
-    registry.destroy(registry.create(), traits_type::to_version(traits_type::construct()) - 1u);
-    registry.destroy(registry.create());
+    registry.release(registry.create(), traits_type::to_version(traits_type::construct()) - 1u);
+    registry.release(registry.create());
 
     ASSERT_EQ(registry.current(entity), registry.version(entity));
     ASSERT_EQ(registry.current(entity), typename traits_type::version_type{});
@@ -656,7 +699,7 @@ TEST(Registry, TombstoneVersion) {
     const auto vers = traits_type::to_version(entity);
     const auto required = traits_type::construct(traits_type::to_entity(other), vers);
 
-    ASSERT_NE(registry.destroy(other, vers), vers);
+    ASSERT_NE(registry.release(other, vers), vers);
     ASSERT_NE(registry.create(required), required);
 }
 
@@ -1750,12 +1793,12 @@ TEST(Registry, AssignEntities) {
     entt::registry registry;
     entt::entity entities[3];
     registry.create(std::begin(entities), std::end(entities));
-    registry.destroy(entities[1]);
-    registry.destroy(entities[2]);
+    registry.release(entities[1]);
+    registry.release(entities[2]);
 
     entt::registry other;
     const auto *data = registry.data();
-    other.assign(data, data + registry.size(), registry.destroyed());
+    other.assign(data, data + registry.size(), registry.released());
 
     ASSERT_EQ(registry.size(), other.size());
     ASSERT_TRUE(other.valid(entities[0]));