Explorar el Código

tests, tags and few other features

Michele Caini hace 8 años
padre
commit
b6c950ffc5

+ 63 - 1
README.md

@@ -461,7 +461,7 @@ their components are destroyed:
   registry.reset();
   registry.reset();
   ```
   ```
 
 
-Finally, references to components can be retrieved by just doing this:
+Finally, references to components can be retrieved simply by doing this:
 
 
 ```cpp
 ```cpp
 // either a non-const reference ...
 // either a non-const reference ...
@@ -476,6 +476,68 @@ const auto &position = cregistry.get<Position>(entity);
 The `get` member function template gives direct access to the component of an
 The `get` member function template gives direct access to the component of an
 entity stored in the underlying data structures of the registry.
 entity stored in the underlying data structures of the registry.
 
 
+### Single instance components
+
+In those cases where all what is needed is a single instance component, tags are
+the right tool to achieve the purpose.<br/>
+Tags undergo the same requirements of components. They can be either plain old
+data structures or more complex and moveable data structures with a proper
+constructor.<br/>
+Actually, the same type can be used both as a tag and as a component and the
+registry will not complain about it. It is up to the users to properly manage
+their own types.
+
+Attaching tags to entities and removing them is trivial:
+
+```cpp
+auto player = registry.create();
+auto camera = registry.create();
+
+// attaches a default-initialized tag to an entity
+registry.attach<PlayingCharacter>(player);
+
+// attaches a tag to an entity and initializes it
+registry.attach<Camera>(camera, player);
+
+// removes tags from their owners
+registry.remove<PlayingCharacter>();
+registry.remove<Camera>();
+```
+
+If in doubt about whether or not a tag has already an owner, the `has` member
+function template may be useful:
+
+```cpp
+bool b = registry.has<PlayingCharacter>();
+```
+
+References to tags can be retrieved simply by doing this:
+
+```cpp
+// either a non-const reference ...
+entt::DefaultRegistry registry;
+auto &player = registry.get<PlayingCharacter>();
+
+// ... or a const one
+const auto &cregistry = registry;
+const auto &camera = cregistry.get<Camera>();
+```
+
+The `get` member function template gives direct access to the tag as stored in
+the underlying data structures of the registry.
+
+As shown above, in almost all the cases the entity identifier isn't required,
+since a single instance component can have only one associated entity and
+therefore it doesn't make much sense to mention it explicitly.<br/>
+To find out who the owner is, just do the following:
+
+```cpp
+auto player = registry.attachee<PlayingCharacter>();
+```
+
+Note that iterating tags isn't possible for obvious reasons. Tags give direct
+access to single entities and nothing more.
+
 ### Sorting: is it possible?
 ### Sorting: is it possible?
 
 
 It goes without saying that sorting entities and components is possible with
 It goes without saying that sorting entities and components is possible with

+ 205 - 20
src/entt/entity/registry.hpp

@@ -7,6 +7,7 @@
 #include <utility>
 #include <utility>
 #include <cstddef>
 #include <cstddef>
 #include <cassert>
 #include <cassert>
+#include <algorithm>
 #include "../core/family.hpp"
 #include "../core/family.hpp"
 #include "sparse_set.hpp"
 #include "sparse_set.hpp"
 #include "traits.hpp"
 #include "traits.hpp"
@@ -28,10 +29,26 @@ namespace entt {
  */
  */
 template<typename Entity>
 template<typename Entity>
 class Registry {
 class Registry {
+    using tag_family = Family<struct InternalRegistryTagFamily>;
     using component_family = Family<struct InternalRegistryComponentFamily>;
     using component_family = Family<struct InternalRegistryComponentFamily>;
     using view_family = Family<struct InternalRegistryViewFamily>;
     using view_family = Family<struct InternalRegistryViewFamily>;
     using traits_type = entt_traits<Entity>;
     using traits_type = entt_traits<Entity>;
 
 
+    struct Attachee {
+        Entity entity;
+    };
+
+    template<typename Tag>
+    struct Attaching: Attachee {
+        // requirements for aggregates are relaxed only since C++17
+        template<typename... Args>
+        Attaching(Entity entity, Tag tag)
+            : Attachee{entity}, tag{std::move(tag)}
+        {}
+
+        Tag tag;
+    };
+
     template<typename Component>
     template<typename Component>
     struct Pool: SparseSet<Entity, Component> {
     struct Pool: SparseSet<Entity, Component> {
         using test_fn_type = bool(Registry::*)(Entity) const;
         using test_fn_type = bool(Registry::*)(Entity) const;
@@ -42,7 +59,7 @@ class Registry {
 
 
             for(auto &&listener: listeners) {
             for(auto &&listener: listeners) {
                 if((registry.*listener.second)(entity)) {
                 if((registry.*listener.second)(entity)) {
-                    listener.first.construct(entity);
+                    listener.first->construct(entity);
                 }
                 }
             }
             }
 
 
@@ -53,20 +70,26 @@ class Registry {
             SparseSet<Entity, Component>::destroy(entity);
             SparseSet<Entity, Component>::destroy(entity);
 
 
             for(auto &&listener: listeners) {
             for(auto &&listener: listeners) {
-                auto &handler = listener.first;
+                auto *handler = listener.first;
 
 
-                if(handler.has(entity)) {
-                    handler.destroy(entity);
+                if(handler->has(entity)) {
+                    handler->destroy(entity);
                 }
                 }
             }
             }
         }
         }
 
 
-        inline void append(SparseSet<Entity> &handler, test_fn_type fn) {
+        inline void append(SparseSet<Entity> *handler, test_fn_type fn) {
             listeners.emplace_back(handler, fn);
             listeners.emplace_back(handler, fn);
         }
         }
 
 
+        inline void remove(SparseSet<Entity> *handler) {
+            listeners.erase(std::remove_if(listeners.begin(), listeners.end(), [handler](auto &listener) {
+                return listener.first == handler;
+            }), listeners.end());
+        }
+
     private:
     private:
-        std::vector<std::pair<SparseSet<Entity> &, test_fn_type>> listeners;
+        std::vector<std::pair<SparseSet<Entity> *, test_fn_type>> listeners;
     };
     };
 
 
     template<typename Component>
     template<typename Component>
@@ -121,7 +144,7 @@ class Registry {
             }
             }
 
 
             accumulator_type accumulator = {
             accumulator_type accumulator = {
-                (ensure<Component>().append(*set, &Registry::has<Component...>), 0)...
+                (ensure<Component>().append(set.get(), &Registry::has<Component...>), 0)...
             };
             };
 
 
             handlers[vtype] = std::move(set);
             handlers[vtype] = std::move(set);
@@ -358,6 +381,122 @@ public:
         }
         }
     }
     }
 
 
+    /**
+     * @brief Attaches a tag to an entity.
+     *
+     * Usually, pools of components allocate enough memory to store a bunch of
+     * elements even if only one of them is used. On the other hand, there are
+     * cases where all what is needed is a single instance component to attach
+     * to an entity.<br/>
+     * Tags are the right tool to achieve the purpose.
+     *
+     * @warning
+     * Attempting to use an invalid entity or to attach to an entity a tag that
+     * already has an owner results in undefined behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode in case of
+     * invalid entity or if the tag has been already attached to another entity.
+     *
+     * @tparam Tag Type of tag to create.
+     * @tparam Args Types of arguments to use to construct the tag.
+     * @param entity A valid entity identifier
+     * @param args Parameters to use to initialize the tag.
+     * @return A reference to the newly created tag.
+     */
+    template<typename Tag, typename... Args>
+    Tag & attach(entity_type entity, Args&&... args) {
+        assert(valid(entity));
+        assert(!has<Tag>());
+        const auto ttype = tag_family::type<Tag>();
+
+        if(!(ttype < tags.size())) {
+            tags.resize(ttype + 1);
+        }
+
+        tags[ttype].reset(new Attaching<Tag>{entity, { std::forward<Args>(args)... }});
+        tags[ttype]->entity = entity;
+
+        return static_cast<Attaching<Tag> *>(tags[ttype].get())->tag;
+    }
+
+    /**
+     * @brief Removes a tag from its owner, if any.
+     * @tparam Tag Type of tag to remove.
+     */
+    template<typename Tag>
+    void remove() {
+        if(has<Tag>()) {
+            tags[tag_family::type<Tag>()].reset();
+        }
+    }
+
+    /**
+     * @brief Checks if a tag has an owner.
+     * @tparam Tag Type of tag for which to perform the check.
+     * @return True if the tag already has an owner, false otherwise.
+     */
+    template<typename Tag>
+    bool has() const noexcept {
+        const auto ttype = tag_family::type<Tag>();
+        return (ttype < tags.size() &&
+                // it's a valid tag
+                tags[ttype] &&
+                // the associated entity hasn't been destroyed in the meantime
+                tags[ttype]->entity == (entities[tags[ttype]->entity & traits_type::entity_mask]));
+    }
+
+    /**
+     * @brief Returns a reference to a tag.
+     *
+     * @warning
+     * Attempting to get a tag that hasn't an owner results in undefined
+     * behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode if the
+     * tag hasn't been previously attached to an entity.
+     *
+     * @tparam Tag Type of tag to get.
+     * @return A reference to the tag.
+     */
+    template<typename Tag>
+    const Tag & get() const noexcept {
+        assert(has<Tag>());
+        return static_cast<Attaching<Tag> *>(tags[tag_family::type<Tag>()].get())->tag;
+    }
+
+    /**
+     * @brief Returns a reference to a tag.
+     *
+     * @warning
+     * Attempting to get a tag that hasn't an owner results in undefined
+     * behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode if the
+     * tag hasn't been previously attached to an entity.
+     *
+     * @tparam Tag Type of tag to get.
+     * @return A reference to the tag.
+     */
+    template<typename Tag>
+    Tag & get() noexcept {
+        return const_cast<Tag &>(const_cast<const Registry *>(this)->get<Tag>());
+    }
+
+    /**
+     * @brief Gets the owner of a tag, if any.
+     *
+     * @warning
+     * Attempting to get the owner of a tag that hasn't been previously attached
+     * to an entity results in undefined behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode if the
+     * tag hasn't an owner.
+     *
+     * @tparam Tag Type of tag of which to get the owner.
+     * @return A valid entity identifier.
+     */
+    template<typename Tag>
+    entity_type attachee() const noexcept {
+        assert(has<Tag>());
+        return tags[tag_family::type<Tag>()]->entity;
+    }
+
     /**
     /**
      * @brief Assigns the given component to an entity.
      * @brief Assigns the given component to an entity.
      *
      *
@@ -372,7 +511,7 @@ public:
      * invalid entity or if the entity already owns an instance of the given
      * invalid entity or if the entity already owns an instance of the given
      * component.
      * component.
      *
      *
-     * @tparam Component Type of the component to create.
+     * @tparam Component Type of component to create.
      * @tparam Args Types of arguments to use to construct the component.
      * @tparam Args Types of arguments to use to construct the component.
      * @param entity A valid entity identifier.
      * @param entity A valid entity identifier.
      * @param args Parameters to use to initialize the component.
      * @param args Parameters to use to initialize the component.
@@ -394,7 +533,7 @@ public:
      * invalid entity or if the entity doesn't own an instance of the given
      * invalid entity or if the entity doesn't own an instance of the given
      * component.
      * component.
      *
      *
-     * @tparam Component Type of the component to remove.
+     * @tparam Component Type of component to remove.
      * @param entity A valid entity identifier.
      * @param entity A valid entity identifier.
      */
      */
     template<typename Component>
     template<typename Component>
@@ -436,7 +575,7 @@ public:
      * invalid entity or if the entity doesn't own an instance of the given
      * invalid entity or if the entity doesn't own an instance of the given
      * component.
      * component.
      *
      *
-     * @tparam Component Type of the component to get.
+     * @tparam Component Type of component to get.
      * @param entity A valid entity identifier.
      * @param entity A valid entity identifier.
      * @return A reference to the instance of the component owned by the entity.
      * @return A reference to the instance of the component owned by the entity.
      */
      */
@@ -456,7 +595,7 @@ public:
      * invalid entity or if the entity doesn't own an instance of the given
      * invalid entity or if the entity doesn't own an instance of the given
      * component.
      * component.
      *
      *
-     * @tparam Component Type of the component to get.
+     * @tparam Component Type of component to get.
      * @param entity A valid entity identifier.
      * @param entity A valid entity identifier.
      * @return A reference to the instance of the component owned by the entity.
      * @return A reference to the instance of the component owned by the entity.
      */
      */
@@ -479,7 +618,7 @@ public:
      * invalid entity or if the entity doesn't own an instance of the given
      * invalid entity or if the entity doesn't own an instance of the given
      * component.
      * component.
      *
      *
-     * @tparam Component Type of the component to replace.
+     * @tparam Component Type of component to replace.
      * @tparam Args Types of arguments to use to construct the component.
      * @tparam Args Types of arguments to use to construct the component.
      * @param entity A valid entity identifier.
      * @param entity A valid entity identifier.
      * @param args Parameters to use to initialize the component.
      * @param args Parameters to use to initialize the component.
@@ -512,7 +651,7 @@ public:
      * An assertion will abort the execution at runtime in debug mode in case of
      * An assertion will abort the execution at runtime in debug mode in case of
      * invalid entity.
      * invalid entity.
      *
      *
-     * @tparam Component Type of the component to assign or replace.
+     * @tparam Component Type of component to assign or replace.
      * @tparam Args Types of arguments to use to construct the component.
      * @tparam Args Types of arguments to use to construct the component.
      * @param entity A valid entity identifier.
      * @param entity A valid entity identifier.
      * @param args Parameters to use to initialize the component.
      * @param args Parameters to use to initialize the component.
@@ -549,8 +688,8 @@ public:
      *
      *
      * Where `e1` and `e2` are valid entity identifiers.
      * Where `e1` and `e2` are valid entity identifiers.
      *
      *
-     * @tparam Component Type of the components to sort.
-     * @tparam Compare Type of the comparison function object.
+     * @tparam Component Type of components to sort.
+     * @tparam Compare Type of comparison function object.
      * @param compare A valid comparison function object.
      * @param compare A valid comparison function object.
      */
      */
     template<typename Component, typename Compare>
     template<typename Component, typename Compare>
@@ -589,8 +728,8 @@ public:
      *
      *
      * Any subsequent change to `B` won't affect the order in `A`.
      * Any subsequent change to `B` won't affect the order in `A`.
      *
      *
-     * @tparam To Type of the components to sort.
-     * @tparam From Type of the components to use to sort.
+     * @tparam To Type of components to sort.
+     * @tparam From Type of components to use to sort.
      */
      */
     template<typename To, typename From>
     template<typename To, typename From>
     void sort() {
     void sort() {
@@ -608,7 +747,7 @@ public:
      * An assertion will abort the execution at runtime in debug mode in case of
      * An assertion will abort the execution at runtime in debug mode in case of
      * invalid entity.
      * invalid entity.
      *
      *
-     * @tparam Component Type of the component to reset.
+     * @tparam Component Type of component to reset.
      * @param entity A valid entity identifier.
      * @param entity A valid entity identifier.
      */
      */
     template<typename Component>
     template<typename Component>
@@ -630,7 +769,7 @@ public:
      * For each entity that has an instance of the given component, the
      * For each entity that has an instance of the given component, the
      * component itself is removed and thus destroyed.
      * component itself is removed and thus destroyed.
      *
      *
-     * @tparam Component type of the component whose pool must be reset.
+     * @tparam Component Type of component whose pool must be reset.
      */
      */
     template<typename Component>
     template<typename Component>
     void reset() {
     void reset() {
@@ -673,6 +812,10 @@ public:
                 pool->reset();
                 pool->reset();
             }
             }
         }
         }
+
+        for(auto &&tag: tags) {
+            tag.reset();
+        }
     }
     }
 
 
     /**
     /**
@@ -733,6 +876,46 @@ public:
         handler<Component...>();
         handler<Component...>();
     }
     }
 
 
+    /**
+     * @brief Discards all the data structures used for a given persitent view.
+     *
+     * Persistent views occupy memory, no matter if they are in use or not.<br/>
+     * This function can be used to discard all the internal data structures
+     * dedicated to a specific persisten view, with the goal of reducing the
+     * memory pressure.
+     *
+     * @warning
+     * Attempting to use a persistent view created before calling this function
+     * results in undefined behavior. No assertion available in this case,
+     * neither in debug mode nor in release mode.
+     *
+     * @tparam Component Types of components of the persistent view.
+     */
+    template<typename... Component>
+    void discard() {
+        if(contains<Component...>()) {
+            using accumulator_type = int[];
+            const auto vtype = view_family::type<Component...>();
+            auto *set = handlers[vtype].get();
+            // if a set exists, pools have already been created for it
+            accumulator_type accumulator = { (pool<Component>().remove(set), 0)... };
+            handlers[vtype].reset();
+            (void)accumulator;
+        }
+    }
+
+    /**
+     * @brief Checks if a persistent view has already been prepared.
+     * @tparam Component Types of components of the persistent view.
+     * @return True if the view has already been prepared, false otherwise.
+     */
+    template<typename... Component>
+    bool contains() const noexcept {
+        static_assert(sizeof...(Component) > 1, "!");
+        const auto vtype = view_family::type<Component...>();
+        return vtype < handlers.size() && handlers[vtype];
+    }
+
     /**
     /**
      * @brief Returns a persistent view for the given components.
      * @brief Returns a persistent view for the given components.
      *
      *
@@ -772,12 +955,14 @@ public:
      */
      */
     template<typename... Component>
     template<typename... Component>
     PersistentView<Entity, Component...> persistent() {
     PersistentView<Entity, Component...> persistent() {
-        return PersistentView<Entity, Component...>{handler<Component...>(), ensure<Component>()...};
+        // after the calls to handler, pools have already been created
+        return PersistentView<Entity, Component...>{handler<Component...>(), pool<Component>()...};
     }
     }
 
 
 private:
 private:
     std::vector<std::unique_ptr<SparseSet<Entity>>> handlers;
     std::vector<std::unique_ptr<SparseSet<Entity>>> handlers;
     std::vector<std::unique_ptr<SparseSet<Entity>>> pools;
     std::vector<std::unique_ptr<SparseSet<Entity>>> pools;
+    std::vector<std::unique_ptr<Attachee>> tags;
     std::vector<entity_type> available;
     std::vector<entity_type> available;
     std::vector<entity_type> entities;
     std::vector<entity_type> entities;
 };
 };

+ 11 - 10
src/entt/entity/sparse_set.hpp

@@ -264,7 +264,7 @@ public:
         // has size 1), switching the two lines below doesn't work as expected
         // has size 1), switching the two lines below doesn't work as expected
         reverse[back] = pos | in_use;
         reverse[back] = pos | in_use;
         reverse[entt] = pos;
         reverse[entt] = pos;
-        // swap-and-pop the last element with the selected ont
+        // swapping isn't required here, we are getting rid of the last element
         direct[pos] = direct.back();
         direct[pos] = direct.back();
         direct.pop_back();
         direct.pop_back();
     }
     }
@@ -412,7 +412,7 @@ class SparseSet<Entity, Type>: public SparseSet<Entity> {
 
 
 public:
 public:
     /*! @brief Type of the objects associated to the entities. */
     /*! @brief Type of the objects associated to the entities. */
-    using type = Type;
+    using object_type = Type;
     /*! @brief Underlying entity identifier. */
     /*! @brief Underlying entity identifier. */
     using entity_type = typename underlying_type::entity_type;
     using entity_type = typename underlying_type::entity_type;
     /*! @brief Entity dependent position type. */
     /*! @brief Entity dependent position type. */
@@ -450,7 +450,7 @@ public:
      *
      *
      * @return A pointer to the array of objects.
      * @return A pointer to the array of objects.
      */
      */
-    const type * raw() const noexcept {
+    const object_type * raw() const noexcept {
         return instances.data();
         return instances.data();
     }
     }
 
 
@@ -469,7 +469,7 @@ public:
      *
      *
      * @return A pointer to the array of objects.
      * @return A pointer to the array of objects.
      */
      */
-    type * raw() noexcept {
+    object_type * raw() noexcept {
         return instances.data();
         return instances.data();
     }
     }
 
 
@@ -485,7 +485,7 @@ public:
      * @param entity A valid entity identifier.
      * @param entity A valid entity identifier.
      * @return The object associated to the entity.
      * @return The object associated to the entity.
      */
      */
-    const type & get(entity_type entity) const noexcept {
+    const object_type & get(entity_type entity) const noexcept {
         return instances[underlying_type::get(entity)];
         return instances[underlying_type::get(entity)];
     }
     }
 
 
@@ -501,8 +501,8 @@ public:
      * @param entity A valid entity identifier.
      * @param entity A valid entity identifier.
      * @return The object associated to the entity.
      * @return The object associated to the entity.
      */
      */
-    type & get(entity_type entity) noexcept {
-        return const_cast<type &>(const_cast<const SparseSet *>(this)->get(entity));
+    object_type & get(entity_type entity) noexcept {
+        return const_cast<object_type &>(const_cast<const SparseSet *>(this)->get(entity));
     }
     }
 
 
     /**
     /**
@@ -520,8 +520,9 @@ public:
      * @return The object associated to the entity.
      * @return The object associated to the entity.
      */
      */
     template<typename... Args>
     template<typename... Args>
-    type & construct(entity_type entity, Args&&... args) {
+    object_type & construct(entity_type entity, Args&&... args) {
         underlying_type::construct(entity);
         underlying_type::construct(entity);
+        // emplace_back doesn't work well with PODs because of its placement new
         instances.push_back({ std::forward<Args>(args)... });
         instances.push_back({ std::forward<Args>(args)... });
         return instances.back();
         return instances.back();
     }
     }
@@ -538,7 +539,7 @@ public:
      * @param entity A valid entity identifier.
      * @param entity A valid entity identifier.
      */
      */
     void destroy(entity_type entity) override {
     void destroy(entity_type entity) override {
-        // swaps isn't required here, we are getting rid of the last element
+        // swapping isn't required here, we are getting rid of the last element
         instances[underlying_type::get(entity)] = std::move(instances.back());
         instances[underlying_type::get(entity)] = std::move(instances.back());
         instances.pop_back();
         instances.pop_back();
         underlying_type::destroy(entity);
         underlying_type::destroy(entity);
@@ -574,7 +575,7 @@ public:
     }
     }
 
 
 private:
 private:
-    std::vector<type> instances;
+    std::vector<object_type> instances;
 };
 };
 
 
 
 

+ 1 - 1
src/entt/entity/view.hpp

@@ -571,7 +571,7 @@ public:
     /*! @brief Unsigned integer type. */
     /*! @brief Unsigned integer type. */
     using size_type = typename pool_type::size_type;
     using size_type = typename pool_type::size_type;
     /*! Type of the component iterated by the view. */
     /*! Type of the component iterated by the view. */
-    using raw_type = typename pool_type::type;
+    using raw_type = typename pool_type::object_type;
 
 
     /**
     /**
      * @brief Constructs a view out of a pool of components.
      * @brief Constructs a view out of a pool of components.

+ 1 - 1
src/entt/process/scheduler.hpp

@@ -292,7 +292,7 @@ public:
      * Unless an immediate operation is requested, the abort is scheduled for
      * Unless an immediate operation is requested, the abort is scheduled for
      * the next tick. Processes won't be executed anymore in any case.<br/>
      * the next tick. Processes won't be executed anymore in any case.<br/>
      * Once a process is fully aborted and thus finished, it's discarded along
      * Once a process is fully aborted and thus finished, it's discarded along
-     * with its child if any.
+     * with its child, if any.
      *
      *
      * @param immediately Requests an immediate operation.
      * @param immediately Requests an immediate operation.
      */
      */

+ 104 - 0
test/entt/entity/registry.cpp

@@ -134,6 +134,110 @@ TEST(DefaultRegistry, CreateDestroyEntities) {
     ASSERT_EQ(registry.current(pre), registry.current(post));
     ASSERT_EQ(registry.current(pre), registry.current(post));
 }
 }
 
 
+TEST(DefaultRegistry, AttachRemoveTags) {
+    entt::DefaultRegistry registry;
+    const auto &cregistry = registry;
+
+    ASSERT_FALSE(registry.has<int>());
+
+    auto entity = registry.create();
+    registry.attach<int>(entity, 42);
+
+    ASSERT_TRUE(registry.has<int>());
+    ASSERT_EQ(registry.get<int>(), 42);
+    ASSERT_EQ(cregistry.get<int>(), 42);
+    ASSERT_EQ(registry.attachee<int>(), entity);
+
+    registry.remove<int>();
+
+    ASSERT_FALSE(registry.has<int>());
+
+    registry.attach<int>(entity, 42);
+    registry.destroy(entity);
+
+    ASSERT_FALSE(registry.has<int>());
+}
+
+TEST(DefaultRegistry, StandardViews) {
+    entt::DefaultRegistry registry;
+    auto mview = registry.view<int, char>();
+    auto iview = registry.view<int>();
+    auto cview = registry.view<char>();
+
+    registry.create(0, 'c');
+    registry.create(0);
+    registry.create(0, 'c');
+
+    ASSERT_EQ(iview.size(), decltype(iview)::size_type{3});
+    ASSERT_EQ(cview.size(), decltype(cview)::size_type{2});
+
+    decltype(mview)::size_type cnt{0};
+    mview.each([&cnt](auto...) { ++cnt; });
+
+    ASSERT_EQ(cnt, decltype(mview)::size_type{2});
+}
+
+TEST(DefaultRegistry, PersistentViews) {
+    entt::DefaultRegistry registry;
+    auto view = registry.persistent<int, char>();
+
+    ASSERT_TRUE((registry.contains<int, char>()));
+    ASSERT_FALSE((registry.contains<int, double>()));
+
+    registry.prepare<int, double>();
+
+    ASSERT_TRUE((registry.contains<int, double>()));
+
+    registry.discard<int, double>();
+
+    ASSERT_FALSE((registry.contains<int, double>()));
+
+    registry.create(0, 'c');
+    registry.create(0);
+    registry.create(0, 'c');
+
+    decltype(view)::size_type cnt{0};
+    view.each([&cnt](auto...) { ++cnt; });
+
+    ASSERT_EQ(cnt, decltype(view)::size_type{2});
+}
+
+TEST(DefaultRegistry, CleanStandardViewsAfterReset) {
+    entt::DefaultRegistry registry;
+    auto view = registry.view<int>();
+    registry.create(0);
+
+    ASSERT_EQ(view.size(), entt::DefaultRegistry::size_type{1});
+
+    registry.reset();
+
+    ASSERT_EQ(view.size(), entt::DefaultRegistry::size_type{0});
+}
+
+TEST(DefaultRegistry, CleanPersistentViewsAfterReset) {
+    entt::DefaultRegistry registry;
+    auto view = registry.persistent<int, char>();
+    registry.create(0, 'c');
+
+    ASSERT_EQ(view.size(), entt::DefaultRegistry::size_type{1});
+
+    registry.reset();
+
+    ASSERT_EQ(view.size(), entt::DefaultRegistry::size_type{0});
+}
+
+TEST(DefaultRegistry, CleanTagsAfterReset) {
+    entt::DefaultRegistry registry;
+    auto entity = registry.create();
+    registry.attach<int>(entity);
+
+    ASSERT_TRUE(registry.has<int>());
+
+    registry.reset();
+
+    ASSERT_FALSE(registry.has<int>());
+}
+
 TEST(DefaultRegistry, SortSingle) {
 TEST(DefaultRegistry, SortSingle) {
     entt::DefaultRegistry registry;
     entt::DefaultRegistry registry;