Selaa lähdekoodia

signals on tags

Michele Caini 8 vuotta sitten
vanhempi
commit
c0213e84f6
4 muutettua tiedostoa jossa 345 lisäystä ja 131 poistoa
  1. 81 49
      README.md
  2. 2 0
      TODO
  3. 172 73
      src/entt/entity/registry.hpp
  4. 90 9
      test/entt/entity/registry.cpp

+ 81 - 49
README.md

@@ -15,8 +15,9 @@
       * [Pay per use](#pay-per-use)
       * [Pay per use](#pay-per-use)
    * [Vademecum](#vademecum)
    * [Vademecum](#vademecum)
    * [The Registry, the Entity and the Component](#the-registry-the-entity-and-the-component)
    * [The Registry, the Entity and the Component](#the-registry-the-entity-and-the-component)
-      * [Observe changes](#observe-changes)
       * [Single instance components](#single-instance-components)
       * [Single instance components](#single-instance-components)
+      * [Observe changes](#observe-changes)
+         * [Who let the tags out?](#who-let-the-tags-out?)
       * [Runtime components](#runtime-components)
       * [Runtime components](#runtime-components)
          * [A journey through a plugin](#a-journey-through-a-plugin)
          * [A journey through a plugin](#a-journey-through-a-plugin)
       * [Sorting: is it possible?](#sorting-is-it-possible)
       * [Sorting: is it possible?](#sorting-is-it-possible)
@@ -529,54 +530,6 @@ std::tuple<Position &, Velocity &> tup = registry.get<Position, Velocity>(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.
 
 
-### Observe changes
-
-Because of how the registry works internally, it stores a couple of signal
-handlers for each pool in order to notify some of its data structures on the
-construction and destruction of components.<br/>
-These signal handlers are also exposed and made available to users. This is the
-basic brick to build fancy things like blueprints and reactive systems.
-
-To get a sink to be used to connect and disconnect listeners so as to be
-notified on the creation of a component, use the `construction` member function:
-
-```cpp
-// connects a free function
-registry.construction<Position>().connect<&MyFreeFunction>();
-
-// connects a member function
-registry.construction<Position>().connect<MyClass, &MyClass::member>(&instance);
-
-// disconnects a free function
-registry.construction<Position>().disconnect<&MyFreeFunction>();
-
-// disconnects a member function
-registry.construction<Position>().disconnect<MyClass, &MyClass::member>(&instance);
-```
-
-To be notified when components are destroyed, use the `destruction` member
-function instead.
-
-The function type of a listener is the same in both cases:
-
-```cpp
-void(Registry<Entity> &, Entity);
-```
-
-In other terms, a listener is provided with the registry that triggered the
-notification and the entity affected by the change. Note also that:
-
-* Listeners are invoked **after** components have been assigned to entities.
-* Listeners are invoked **before** components have been removed from entities.
-* The order of invocation of the listeners isn't guaranteed in any case.
-
-There are also some limitations on what a listener can and cannot do. In
-particular, connecting and disconnecting other functions from within the body of
-a listener should be avoided. It can result in undefined behavior.<br/>
-In general, events and therefore listeners must not be used as replacements for
-systems. They should not contain much logic and interactions with a registry
-should be kept to a minimum, if possible.
-
 ### Single instance components
 ### Single instance components
 
 
 In those cases where all what is needed is a single instance component, tags are
 In those cases where all what is needed is a single instance component, tags are
@@ -652,6 +605,85 @@ auto player = registry.attachee<PlayingCharacter>();
 Note that iterating tags isn't possible for obvious reasons. Tags give direct
 Note that iterating tags isn't possible for obvious reasons. Tags give direct
 access to single entities and nothing more.
 access to single entities and nothing more.
 
 
+### Observe changes
+
+Because of how the registry works internally, it stores a couple of signal
+handlers for each pool in order to notify some of its data structures on the
+construction and destruction of components.<br/>
+These signal handlers are also exposed and made available to users. This is the
+basic brick to build fancy things like blueprints and reactive systems.
+
+To get a sink to be used to connect and disconnect listeners so as to be
+notified on the creation of a component, use the `construction` member function:
+
+```cpp
+// connects a free function
+registry.construction<Position>().connect<&MyFreeFunction>();
+
+// connects a member function
+registry.construction<Position>().connect<MyClass, &MyClass::member>(&instance);
+
+// disconnects a free function
+registry.construction<Position>().disconnect<&MyFreeFunction>();
+
+// disconnects a member function
+registry.construction<Position>().disconnect<MyClass, &MyClass::member>(&instance);
+```
+
+To be notified when components are destroyed, use the `destruction` member
+function instead.
+
+The function type of a listener is the same in both cases:
+
+```cpp
+void(Registry<Entity> &, Entity);
+```
+
+In other terms, a listener is provided with the registry that triggered the
+notification and the entity affected by the change. Note also that:
+
+* Listeners are invoked **after** components have been assigned to entities.
+* Listeners are invoked **before** components have been removed from entities.
+* The order of invocation of the listeners isn't guaranteed in any case.
+
+There are also some limitations on what a listener can and cannot do. In
+particular:
+
+* Connecting and disconnecting other functions from within the body of a
+  listener should be avoided. It can lead to undefined behavior.
+* Assigning and removing components and tags from within the body of a listener
+  that observes the destruction of instances of a given type should be avoided.
+  It can lead to undefined behavior. This type of listeners is intended to
+  provide users with an easy way to perform cleanup and nothing more.
+
+In general, events and therefore listeners must not be used as replacements for
+systems. They should not contain much logic and interactions with a registry
+should be kept to a minimum, if possible. Note also that the greater the number
+of listeners, the greater the performance hit when components are created or
+destroyed.
+
+#### Who let the tags out?
+
+As an extension, signals are also provided with tags. Although they are not
+strictly required internally, it makes sense that an user expects signal support
+even when it comes to tags actually.<br/>
+Signals for tags undergo exactly the same requirements of those introduced for
+components. Also the function type for a listener is the same and it's invoked
+with the same guarantees discussed above.
+
+To get the sinks for a tag just use `entt::tag_type_t` to disambiguate overloads
+of member functions as in the following example:
+
+```cpp
+registry.construction<MyTag>(entt::tag_type_t{}).connect<&MyFreeFunction>();
+registry.destruction<MyTag>(entt::tag_type_t{}).connect<MyClass, &MyClass::member>(&instance);
+```
+
+Listeners for tags and components are managed separately and do not influence
+each other in any case. Therefore, note that the greater the number of listeners
+for a type, the greater the performance hit when a tag of the given type is
+created or destroyed.
+
 ### Runtime components
 ### Runtime components
 
 
 Defining components at runtime is useful to support plugins and mods in general.
 Defining components at runtime is useful to support plugins and mods in general.

+ 2 - 0
TODO

@@ -8,5 +8,7 @@
 * does it worth it to add an optional functor to the member functions of snapshot so as to filter out instances and entities?
 * does it worth it to add an optional functor to the member functions of snapshot so as to filter out instances and entities?
 * ease the assignment of tags as string (use a template class with a non-type template parameter behind the scene)
 * ease the assignment of tags as string (use a template class with a non-type template parameter behind the scene)
 * dictionary based dependency class (templates copied over) + prefabs (shared state/copy-on-write)
 * dictionary based dependency class (templates copied over) + prefabs (shared state/copy-on-write)
+* update benchmarks, destroy is probably slower now because of signals on components/tags
 * "singleton mode" for tags (see #66)
 * "singleton mode" for tags (see #66)
+* introduce a fast destroy that doesn't check for components or tags (it does it only in debug) and discards the entity immediately (use it with snapshot orphans)
 * AOB
 * AOB

+ 172 - 73
src/entt/entity/registry.hpp

@@ -38,24 +38,19 @@ class Registry {
     using tag_family = Family<struct InternalRegistryTagFamily>;
     using tag_family = Family<struct InternalRegistryTagFamily>;
     using component_family = Family<struct InternalRegistryComponentFamily>;
     using component_family = Family<struct InternalRegistryComponentFamily>;
     using handler_family = Family<struct InternalRegistryHandlerFamily>;
     using handler_family = Family<struct InternalRegistryHandlerFamily>;
+    using signal_type = SigH<void(Registry &, Entity)>;
     using traits_type = entt_traits<Entity>;
     using traits_type = entt_traits<Entity>;
 
 
     template<typename... Component>
     template<typename... Component>
     static void creating(Registry &registry, Entity entity) {
     static void creating(Registry &registry, Entity entity) {
-        if(registry.has<Component...>(entity)) {
-            const auto htype = handler_family::type<Component...>();
-            registry.handlers[htype]->construct(entity);
-        }
+        const auto ttype = handler_family::type<Component...>();
+        return registry.has<Component...>(entity) ? registry.handlers[ttype]->construct(entity) : void();
     }
     }
 
 
     template<typename... Component>
     template<typename... Component>
     static void destroying(Registry &registry, Entity entity) {
     static void destroying(Registry &registry, Entity entity) {
-        const auto htype = handler_family::type<Component...>();
-        auto &handler = registry.handlers[htype];
-
-        if(handler->has(entity)) {
-            handler->destroy(entity);
-        }
+        auto &handler = registry.handlers[handler_family::type<Component...>()];
+        return handler->has(entity) ? handler->destroy(entity) : void();
     }
     }
 
 
     struct Attachee {
     struct Attachee {
@@ -76,7 +71,19 @@ class Registry {
     };
     };
 
 
     template<typename Component>
     template<typename Component>
-    SparseSet<Entity, Component> & assure() {
+    const SparseSet<Entity, Component> & pool() const noexcept {
+        const auto ctype = component_family::type<Component>();
+        assert(ctype < pools.size() && std::get<0>(pools[ctype]));
+        return static_cast<SparseSet<Entity, Component> &>(*std::get<0>(pools[ctype]));
+    }
+
+    template<typename Component>
+    SparseSet<Entity, Component> & pool() noexcept {
+        return const_cast<SparseSet<Entity, Component> &>(const_cast<const Registry *>(this)->pool<Component>());
+    }
+
+    template<typename Component>
+    void assure() {
         const auto ctype = component_family::type<Component>();
         const auto ctype = component_family::type<Component>();
 
 
         if(!(ctype < pools.size())) {
         if(!(ctype < pools.size())) {
@@ -88,8 +95,15 @@ class Registry {
         if(!cpool) {
         if(!cpool) {
             cpool = std::make_unique<SparseSet<Entity, Component>>();
             cpool = std::make_unique<SparseSet<Entity, Component>>();
         }
         }
+    }
+
+    template<typename Tag>
+    void assure(tag_type_t) {
+        const auto ttype = tag_family::type<Tag>();
 
 
-        return static_cast<SparseSet<Entity, Component> &>(*cpool);
+        if(!(ttype < tags.size())) {
+            tags.resize(ttype + 1);
+        }
     }
     }
 
 
 public:
 public:
@@ -104,7 +118,7 @@ public:
     /*! @brief Unsigned integer type. */
     /*! @brief Unsigned integer type. */
     using component_type = typename component_family::family_type;
     using component_type = typename component_family::family_type;
     /*! @brief Type of sink for the given component. */
     /*! @brief Type of sink for the given component. */
-    using sink_type = typename SigH<void(Registry &, Entity)>::sink_type;
+    using sink_type = typename signal_type::sink_type;
 
 
     /*! @brief Default constructor. */
     /*! @brief Default constructor. */
     Registry() = default;
     Registry() = default;
@@ -183,7 +197,8 @@ public:
      */
      */
     template<typename Component>
     template<typename Component>
     void reserve(size_type cap) {
     void reserve(size_type cap) {
-        assure<Component>().reserve(cap);
+        assure<Component>();
+        pool<Component>().reserve(cap);
     }
     }
 
 
     /**
     /**
@@ -345,6 +360,25 @@ public:
      */
      */
     void destroy(entity_type entity) {
     void destroy(entity_type entity) {
         assert(valid(entity));
         assert(valid(entity));
+
+        std::for_each(pools.begin(), pools.end(), [entity, this](auto &&tup) {
+            auto &cpool = std::get<0>(tup);
+
+            if(cpool && cpool->has(entity)) {
+                std::get<2>(tup).publish(*this, entity);
+                cpool->destroy(entity);
+            }
+        });
+
+        std::for_each(tags.begin(), tags.end(), [entity, this](auto &&tup) {
+            auto &tag = std::get<0>(tup);
+
+            if(tag && tag->entity == entity) {
+                std::get<2>(tup).publish(*this, entity);
+                tag.reset();
+            }
+        });
+
         const auto entt = entity & traits_type::entity_mask;
         const auto entt = entity & traits_type::entity_mask;
         const auto version = (((entity >> traits_type::entity_shift) + 1) & traits_type::version_mask) << traits_type::entity_shift;
         const auto version = (((entity >> traits_type::entity_shift) + 1) & traits_type::version_mask) << traits_type::entity_shift;
         const auto node = (available ? next : ((entt + 1) & traits_type::entity_mask)) | version;
         const auto node = (available ? next : ((entt + 1) & traits_type::entity_mask)) | version;
@@ -352,16 +386,6 @@ public:
         entities[entt] = node;
         entities[entt] = node;
         next = entt;
         next = entt;
         ++available;
         ++available;
-
-        // changing the number of pools during a destroy is supported now
-        for(std::size_t pos = pools.size(); pos; --pos) {
-            auto &cpool = std::get<0>(pools[pos-1]);
-
-            if(cpool && cpool->has(entity)) {
-                cpool->destroy(entity);
-                std::get<2>(pools[pos-1]).publish(*this, entity);
-            }
-        }
     }
     }
 
 
     /**
     /**
@@ -389,15 +413,11 @@ public:
     Tag & assign(tag_type_t, entity_type entity, Args &&... args) {
     Tag & assign(tag_type_t, entity_type entity, Args &&... args) {
         assert(valid(entity));
         assert(valid(entity));
         assert(!has<Tag>());
         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, Tag{std::forward<Args>(args)...}});
-
-        return static_cast<Attaching<Tag> *>(tags[ttype].get())->tag;
+        assure<Tag>(tag_type_t{});
+        auto &tup = tags[tag_family::type<Tag>()];
+        std::get<0>(tup).reset(new Attaching<Tag>{entity, Tag{std::forward<Args>(args)...}});
+        std::get<1>(tup).publish(*this, entity);
+        return get<Tag>();
     }
     }
 
 
     /**
     /**
@@ -423,10 +443,10 @@ public:
     template<typename Component, typename... Args>
     template<typename Component, typename... Args>
     Component & assign(entity_type entity, Args &&... args) {
     Component & assign(entity_type entity, Args &&... args) {
         assert(valid(entity));
         assert(valid(entity));
-        auto &pool = assure<Component>();
-        auto &component = pool.construct(entity, std::forward<Args>(args)...);
+        assure<Component>();
+        pool<Component>().construct(entity, std::forward<Args>(args)...);
         std::get<1>(pools[component_family::type<Component>()]).publish(*this, entity);
         std::get<1>(pools[component_family::type<Component>()]).publish(*this, entity);
-        return component;
+        return pool<Component>().get(entity);
     }
     }
 
 
     /**
     /**
@@ -436,7 +456,10 @@ public:
     template<typename Tag>
     template<typename Tag>
     void remove() {
     void remove() {
         if(has<Tag>()) {
         if(has<Tag>()) {
-            tags[tag_family::type<Tag>()].reset();
+            auto &tup = tags[tag_family::type<Tag>()];
+            auto &tag = std::get<0>(tup);
+            std::get<2>(tup).publish(*this, tag->entity);
+            tag.reset();
         }
         }
     }
     }
 
 
@@ -456,8 +479,10 @@ public:
     template<typename Component>
     template<typename Component>
     void remove(entity_type entity) {
     void remove(entity_type entity) {
         assert(valid(entity));
         assert(valid(entity));
-        assure<Component>().destroy(entity);
-        std::get<2>(pools[component_family::type<Component>()]).publish(*this, entity);
+        const auto ctype = component_family::type<Component>();
+        assert(ctype < pools.size() && std::get<0>(pools[ctype]));
+        std::get<2>(pools[ctype]).publish(*this, entity);
+        pool<Component>().destroy(entity);
     }
     }
 
 
     /**
     /**
@@ -468,11 +493,15 @@ public:
     template<typename Tag>
     template<typename Tag>
     bool has() const noexcept {
     bool has() const noexcept {
         const auto ttype = tag_family::type<Tag>();
         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]));
+        bool found = false;
+
+        if(ttype < tags.size()) {
+            auto &tag = std::get<0>(tags[ttype]);
+            // it's a valid tag and the associated entity hasn't been destroyed in the meantime
+            found = tag && (tag->entity == (entities[tag->entity & traits_type::entity_mask]));
+        }
+
+        return found;
     }
     }
 
 
     /**
     /**
@@ -513,7 +542,7 @@ public:
     template<typename Tag>
     template<typename Tag>
     const Tag & get() const noexcept {
     const Tag & get() const noexcept {
         assert(has<Tag>());
         assert(has<Tag>());
-        return static_cast<Attaching<Tag> *>(tags[tag_family::type<Tag>()].get())->tag;
+        return static_cast<Attaching<Tag> *>(std::get<0>(tags[tag_family::type<Tag>()]).get())->tag;
     }
     }
 
 
     /**
     /**
@@ -634,7 +663,7 @@ public:
      */
      */
     template<typename Tag, typename... Args>
     template<typename Tag, typename... Args>
     Tag & replace(tag_type_t, Args &&... args) {
     Tag & replace(tag_type_t, Args &&... args) {
-        return get<Tag>() = Tag{std::forward<Args>(args)...};
+        return (get<Tag>() = Tag{std::forward<Args>(args)...});
     }
     }
 
 
     /**
     /**
@@ -682,9 +711,9 @@ public:
     entity_type move(entity_type entity) {
     entity_type move(entity_type entity) {
         assert(valid(entity));
         assert(valid(entity));
         assert(has<Tag>());
         assert(has<Tag>());
-        const auto ttype = tag_family::type<Tag>();
-        const auto owner = tags[ttype]->entity;
-        tags[ttype]->entity = entity;
+        auto &tag = std::get<0>(tags[tag_family::type<Tag>()]);
+        const auto owner = tag->entity;
+        tag->entity = entity;
         return owner;
         return owner;
     }
     }
 
 
@@ -703,7 +732,7 @@ public:
     template<typename Tag>
     template<typename Tag>
     entity_type attachee() const noexcept {
     entity_type attachee() const noexcept {
         assert(has<Tag>());
         assert(has<Tag>());
-        return tags[tag_family::type<Tag>()]->entity;
+        return std::get<0>(tags[tag_family::type<Tag>()])->entity;
     }
     }
 
 
     /**
     /**
@@ -737,6 +766,35 @@ public:
                 : assign<Component>(entity, std::forward<Args>(args)...));
                 : assign<Component>(entity, std::forward<Args>(args)...));
     }
     }
 
 
+    /**
+     * @brief Returns a sink object for the given tag.
+     *
+     * A sink is an opaque object used to connect listeners to tags.<br/>
+     * The sink returned by this function can be used to receive notifications
+     * whenever a new instance of the given tag is created and assigned to an
+     * entity.
+     *
+     * The function type for a listener is:
+     * @code{.cpp}
+     * void(Registry<Entity> &, Entity);
+     * @endcode
+     *
+     * Listeners are invoked **after** the tag has been assigned to the entity.
+     * The order of invocation of the listeners isn't guaranteed.<br/>
+     * Note also that the greater the number of listeners, the greater the
+     * performance hit when a new tag is created.
+     *
+     * @sa SigH::Sink
+     *
+     * @tparam Tag Type of tag of which to get the sink.
+     * @return A temporary sink object.
+     */
+    template<typename Tag>
+    sink_type construction(tag_type_t) noexcept {
+        assure<Tag>(tag_type_t{});
+        return std::get<1>(tags[tag_family::type<Tag>()]).sink();
+    }
+
     /**
     /**
      * @brief Returns a sink object for the given component.
      * @brief Returns a sink object for the given component.
      *
      *
@@ -751,7 +809,9 @@ public:
      * @endcode
      * @endcode
      *
      *
      * Listeners are invoked **after** the component has been assigned to the
      * Listeners are invoked **after** the component has been assigned to the
-     * entity. The order of invocation of the listeners isn't guaranteed.
+     * entity. The order of invocation of the listeners isn't guaranteed.<br/>
+     * Note also that the greater the number of listeners, the greater the
+     * performance hit when a new component is created.
      *
      *
      * @sa SigH::Sink
      * @sa SigH::Sink
      *
      *
@@ -760,7 +820,37 @@ public:
      */
      */
     template<typename Component>
     template<typename Component>
     sink_type construction() noexcept {
     sink_type construction() noexcept {
-        return (assure<Component>(), std::get<1>(pools[component_family::type<Component>()]).sink());
+        assure<Component>();
+        return std::get<1>(pools[component_family::type<Component>()]).sink();
+    }
+
+    /**
+     * @brief Returns a sink object for the given tag.
+     *
+     * A sink is an opaque object used to connect listeners to tag.<br/>
+     * The sink returned by this function can be used to receive notifications
+     * whenever an instance of the given tag is removed from an entity and thus
+     * destroyed.
+     *
+     * The function type for a listener is:
+     * @code{.cpp}
+     * void(Registry<Entity> &, Entity);
+     * @endcode
+     *
+     * Listeners are invoked **before** the tag has been removed from the
+     * entity. The order of invocation of the listeners isn't guaranteed.<br/>
+     * Note also that the greater the number of listeners, the greater the
+     * performance hit when a tag is destroyed.
+     *
+     * @sa SigH::Sink
+     *
+     * @tparam Tag Type of tag of which to get the sink.
+     * @return A temporary sink object.
+     */
+    template<typename Tag>
+    sink_type destruction(tag_type_t) noexcept {
+        assure<Tag>(tag_type_t{});
+        return std::get<2>(tags[tag_family::type<Tag>()]).sink();
     }
     }
 
 
     /**
     /**
@@ -777,7 +867,9 @@ public:
      * @endcode
      * @endcode
      *
      *
      * Listeners are invoked **before** the component has been removed from the
      * Listeners are invoked **before** the component has been removed from the
-     * entity. The order of invocation of the listeners isn't guaranteed.
+     * entity. The order of invocation of the listeners isn't guaranteed.<br/>
+     * Note also that the greater the number of listeners, the greater the
+     * performance hit when a component is destroyed.
      *
      *
      * @sa SigH::Sink
      * @sa SigH::Sink
      *
      *
@@ -786,7 +878,8 @@ public:
      */
      */
     template<typename Component>
     template<typename Component>
     sink_type destruction() noexcept {
     sink_type destruction() noexcept {
-        return (assure<Component>(), std::get<2>(pools[component_family::type<Component>()]).sink());
+        assure<Component>();
+        return std::get<2>(pools[component_family::type<Component>()]).sink();
     }
     }
 
 
     /**
     /**
@@ -814,7 +907,8 @@ public:
      */
      */
     template<typename Component, typename Compare>
     template<typename Component, typename Compare>
     void sort(Compare compare) {
     void sort(Compare compare) {
-        assure<Component>().sort(std::move(compare));
+        assure<Component>();
+        pool<Component>().sort(std::move(compare));
     }
     }
 
 
     /**
     /**
@@ -849,7 +943,9 @@ public:
      */
      */
     template<typename To, typename From>
     template<typename To, typename From>
     void sort() {
     void sort() {
-        assure<To>().respect(assure<From>());
+        assure<To>();
+        assure<From>();
+        pool<To>().respect(pool<From>());
     }
     }
 
 
     /**
     /**
@@ -869,11 +965,13 @@ public:
     template<typename Component>
     template<typename Component>
     void reset(entity_type entity) {
     void reset(entity_type entity) {
         assert(valid(entity));
         assert(valid(entity));
-        auto &cpool = assure<Component>();
+        assure<Component>();
+        const auto ctype = component_family::type<Component>();
+        auto &cpool = *std::get<0>(pools[ctype]);
 
 
         if(cpool.has(entity)) {
         if(cpool.has(entity)) {
+            std::get<2>(pools[ctype]).publish(*this, entity);
             cpool.destroy(entity);
             cpool.destroy(entity);
-            std::get<2>(pools[component_family::type<Component>()]).publish(*this, entity);
         }
         }
     }
     }
 
 
@@ -887,15 +985,15 @@ public:
      */
      */
     template<typename Component>
     template<typename Component>
     void reset() {
     void reset() {
-        auto &cpool = assure<Component>();
-        auto &sig = std::get<2>(pools[component_family::type<Component>()]);
+        assure<Component>();
+        const auto ctype = component_family::type<Component>();
+        auto &cpool = *std::get<0>(pools[ctype]);
+        auto &sig = std::get<2>(pools[ctype]);
 
 
-        each([&cpool, &sig, this](auto entity) {
-            if(cpool.has(entity)) {
-                cpool.destroy(entity);
-                sig.publish(*this, entity);
-            }
-        });
+        for(const auto entity: cpool) {
+            sig.publish(*this, entity);
+            cpool.destroy(entity);
+        }
     }
     }
 
 
     /**
     /**
@@ -970,7 +1068,7 @@ public:
         }
         }
 
 
         for(std::size_t i = 0; i < tags.size() && orphan; ++i) {
         for(std::size_t i = 0; i < tags.size() && orphan; ++i) {
-            const auto &tag = tags[i];
+            const auto &tag = std::get<0>(tags[i]);
             orphan = !(tag && (tag->entity == entity));
             orphan = !(tag && (tag->entity == entity));
         }
         }
 
 
@@ -1037,7 +1135,7 @@ public:
      */
      */
     template<typename... Component>
     template<typename... Component>
     View<Entity, Component...> view() {
     View<Entity, Component...> view() {
-        return View<Entity, Component...>{assure<Component>()...};
+        return View<Entity, Component...>{(assure<Component>(), pool<Component>())...};
     }
     }
 
 
     /**
     /**
@@ -1090,7 +1188,7 @@ public:
      *
      *
      * Persistent views occupy memory, no matter if they are in use or not.<br/>
      * 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
      * This function can be used to discard all the internal data structures
-     * dedicated to a specific persisten view, with the goal of reducing the
+     * dedicated to a specific persistent view, with the goal of reducing the
      * memory pressure.
      * memory pressure.
      *
      *
      * @warning
      * @warning
@@ -1173,7 +1271,7 @@ public:
     PersistentView<Entity, Component...> persistent() {
     PersistentView<Entity, Component...> persistent() {
         prepare<Component...>();
         prepare<Component...>();
         const auto htype = handler_family::type<Component...>();
         const auto htype = handler_family::type<Component...>();
-        return PersistentView<Entity, Component...>{*handlers[htype], assure<Component>()...};
+        return PersistentView<Entity, Component...>{*handlers[htype], (assure<Component>(), pool<Component>())...};
     }
     }
 
 
     /**
     /**
@@ -1200,7 +1298,8 @@ public:
      */
      */
     template<typename Component>
     template<typename Component>
     RawView<Entity, Component> raw() {
     RawView<Entity, Component> raw() {
-        return RawView<Entity, Component>{assure<Component>()};
+        assure<Component>();
+        return RawView<Entity, Component>{pool<Component>()};
     }
     }
 
 
     /**
     /**
@@ -1277,8 +1376,8 @@ public:
 
 
 private:
 private:
     std::vector<std::unique_ptr<SparseSet<Entity>>> handlers;
     std::vector<std::unique_ptr<SparseSet<Entity>>> handlers;
-    std::vector<std::tuple<std::unique_ptr<SparseSet<Entity>>, SigH<void(Registry &, Entity)>, SigH<void(Registry &, Entity)>>> pools;
-    std::vector<std::unique_ptr<Attachee>> tags;
+    std::vector<std::tuple<std::unique_ptr<SparseSet<Entity>>, signal_type, signal_type>> pools;
+    std::vector<std::tuple<std::unique_ptr<Attachee>, signal_type, signal_type>> tags;
     std::vector<entity_type> entities;
     std::vector<entity_type> entities;
     size_type available{};
     size_type available{};
     entity_type next{};
     entity_type next{};

+ 90 - 9
test/entt/entity/registry.cpp

@@ -6,13 +6,37 @@
 #include <entt/entity/entt_traits.hpp>
 #include <entt/entity/entt_traits.hpp>
 #include <entt/entity/registry.hpp>
 #include <entt/entity/registry.hpp>
 
 
-struct ComponentListener {
-    void incr(entt::DefaultRegistry &, entt::DefaultRegistry::entity_type entity) {
+struct Listener {
+    template<typename Component>
+    void incrComponent(entt::DefaultRegistry &registry, entt::DefaultRegistry::entity_type entity) {
+        ASSERT_TRUE(registry.valid(entity));
+        ASSERT_TRUE(registry.has<Component>(entity));
         last = entity;
         last = entity;
         ++counter;
         ++counter;
     }
     }
 
 
-    void decr(entt::DefaultRegistry &, entt::DefaultRegistry::entity_type entity) {
+    template<typename Tag>
+    void incrTag(entt::DefaultRegistry &registry, entt::DefaultRegistry::entity_type entity) {
+        ASSERT_TRUE(registry.valid(entity));
+        ASSERT_TRUE(registry.has<Tag>());
+        ASSERT_EQ(registry.attachee<Tag>(), entity);
+        last = entity;
+        ++counter;
+    }
+
+    template<typename Component>
+    void decrComponent(entt::DefaultRegistry &registry, entt::DefaultRegistry::entity_type entity) {
+        ASSERT_TRUE(registry.valid(entity));
+        ASSERT_TRUE(registry.has<Component>(entity));
+        last = entity;
+        --counter;
+    }
+
+    template<typename Tag>
+    void decrTag(entt::DefaultRegistry &registry, entt::DefaultRegistry::entity_type entity) {
+        ASSERT_TRUE(registry.valid(entity));
+        ASSERT_TRUE(registry.has<Tag>());
+        ASSERT_EQ(registry.attachee<Tag>(), entity);
         last = entity;
         last = entity;
         --counter;
         --counter;
     }
     }
@@ -568,12 +592,12 @@ TEST(DefaultRegistry, MergeTwoRegistries) {
     ne(dst.view<char, float, int>().begin(), dst.view<char, float, int>().end());
     ne(dst.view<char, float, int>().begin(), dst.view<char, float, int>().end());
 }
 }
 
 
-TEST(DefaultRegistry, Signals) {
+TEST(DefaultRegistry, ComponentSignals) {
     entt::DefaultRegistry registry;
     entt::DefaultRegistry registry;
-    ComponentListener listener;
+    Listener listener;
 
 
-    registry.construction<int>().connect<ComponentListener, &ComponentListener::incr>(&listener);
-    registry.destruction<int>().connect<ComponentListener, &ComponentListener::decr>(&listener);
+    registry.construction<int>().connect<Listener, &Listener::incrComponent<int>>(&listener);
+    registry.destruction<int>().connect<Listener, &Listener::decrComponent<int>>(&listener);
 
 
     auto e0 = registry.create();
     auto e0 = registry.create();
     auto e1 = registry.create();
     auto e1 = registry.create();
@@ -589,16 +613,73 @@ TEST(DefaultRegistry, Signals) {
     ASSERT_EQ(listener.counter, 1);
     ASSERT_EQ(listener.counter, 1);
     ASSERT_EQ(listener.last, e0);
     ASSERT_EQ(listener.last, e0);
 
 
-    registry.destruction<int>().disconnect<ComponentListener, &ComponentListener::decr>(&listener);
+    registry.destruction<int>().disconnect<Listener, &Listener::decrComponent<int>>(&listener);
     registry.remove<int>(e1);
     registry.remove<int>(e1);
 
 
     ASSERT_EQ(listener.counter, 1);
     ASSERT_EQ(listener.counter, 1);
     ASSERT_EQ(listener.last, e0);
     ASSERT_EQ(listener.last, e0);
 
 
-    registry.construction<int>().disconnect<ComponentListener, &ComponentListener::incr>(&listener);
+    registry.construction<int>().disconnect<Listener, &Listener::incrComponent<int>>(&listener);
+    registry.assign<int>(e1);
+
+    ASSERT_EQ(listener.counter, 1);
+    ASSERT_EQ(listener.last, e0);
+
+    registry.construction<int>().connect<Listener, &Listener::incrComponent<int>>(&listener);
+    registry.destruction<int>().connect<Listener, &Listener::decrComponent<int>>(&listener);
+    registry.assign<int>(e0);
+    registry.reset<int>(e1);
+
+    ASSERT_EQ(listener.counter, 1);
+    ASSERT_EQ(listener.last, e1);
+
+    registry.reset<int>();
+
+    ASSERT_EQ(listener.counter, 0);
+    ASSERT_EQ(listener.last, e0);
+
     registry.assign<int>(e0);
     registry.assign<int>(e0);
     registry.assign<int>(e1);
     registry.assign<int>(e1);
+    registry.destroy(e1);
 
 
     ASSERT_EQ(listener.counter, 1);
     ASSERT_EQ(listener.counter, 1);
+    ASSERT_EQ(listener.last, e1);
+}
+
+TEST(DefaultRegistry, TagSignals) {
+    entt::DefaultRegistry registry;
+    Listener listener;
+
+    registry.construction<int>(entt::tag_type_t{}).connect<Listener, &Listener::incrTag<int>>(&listener);
+    registry.destruction<int>(entt::tag_type_t{}).connect<Listener, &Listener::decrTag<int>>(&listener);
+
+    auto e0 = registry.create();
+    registry.assign<int>(entt::tag_type_t{}, e0);
+
+    ASSERT_EQ(listener.counter, 1);
+    ASSERT_EQ(listener.last, e0);
+
+    auto e1 = registry.create();
+    registry.move<int>(e1);
+    registry.remove<int>();
+
+    ASSERT_EQ(listener.counter, 0);
+    ASSERT_EQ(listener.last, e1);
+
+    registry.construction<int>(entt::tag_type_t{}).disconnect<Listener, &Listener::incrTag<int>>(&listener);
+    registry.destruction<int>(entt::tag_type_t{}).disconnect<Listener, &Listener::decrTag<int>>(&listener);
+    registry.assign<int>(entt::tag_type_t{}, e0);
+    registry.remove<int>();
+
+    ASSERT_EQ(listener.counter, 0);
+    ASSERT_EQ(listener.last, e1);
+
+    registry.construction<int>(entt::tag_type_t{}).connect<Listener, &Listener::incrTag<int>>(&listener);
+    registry.destruction<int>(entt::tag_type_t{}).connect<Listener, &Listener::decrTag<int>>(&listener);
+
+    registry.assign<int>(entt::tag_type_t{}, e0);
+    registry.destroy(e0);
+
+    ASSERT_EQ(listener.counter, 0);
     ASSERT_EQ(listener.last, e0);
     ASSERT_EQ(listener.last, e0);
 }
 }