Bläddra i källkod

registry: updated signatures for callbacks, deprecated dependency

Michele Caini 6 år sedan
förälder
incheckning
a06d5dd7bb

+ 1 - 3
TODO

@@ -19,6 +19,7 @@
 * allow to "merge" registries easily
 * allow for custom stomp functions
 * deprecate/replace snapshot
+* remove dependency
 * remove prototype
 
 TODO
@@ -27,6 +28,3 @@ TODO
 * make meta work across boundaries
   - inline variables are fine here, only the head represents a problem
   - we should always resolve by looking into the list of types when working across boundaries, no direct resolve
-* signals: entity/registry/component instead of registry/entity/component
-  - dependency becomes a short circuit on the registry
-  - deprecate dependency

+ 141 - 156
docs/md/entity.md

@@ -18,18 +18,18 @@
   * [Runtime components](#runtime-components)
     * [A journey through a plugin](#a-journey-through-a-plugin)
   * [Sorting: is it possible?](#sorting-is-it-possible)
-  * [Multiple registries to the rescue](#multiple-registries-to-the-rescue)
+  * [Helpers](#helpers)
+    * [Null entity](#null-entity)
+    * [Multiple registries](#multiple-registries)
+    * [Dependencies](#dependencies)
+    * [Tags](#tags)
+    * [Actor](#actor)
+    * [Context variables](#context-variables)
   * [Snapshot: complete vs continuous](#snapshot-complete-vs-continuous)
     * [Snapshot loader](#snapshot-loader)
     * [Continuous loader](#continuous-loader)
     * [Archives](#archives)
     * [One example to rule them all](#one-example-to-rule-them-all)
-  * [The actor class](#the-actor-class)
-  * [Helpers](#helpers)
-    * [Dependency function](#dependency-function)
-    * [Tags](#tags)
-  * [Null entity](#null-entity)
-  * [Context variables](#context-variables)
 * [Views and Groups](#views-and-groups)
   * [Views](#views)
   * [Runtime views](#runtime-views)
@@ -365,7 +365,7 @@ The function type of a listener for the construction signal should be equivalent
 to the following:
 
 ```cpp
-void(entt::registry &, entt::entity, Component &);
+void(entt::entity, entt::registry &, Component &);
 ```
 
 Where `Component` is intuitively the type of component of interest. In other
@@ -377,7 +377,7 @@ signature of which is the same of that of the construction signal. The one of
 the destruction signal is also similar, except for the `Component` parameter:
 
 ```cpp
-void(entt::registry &, entt::entity);
+void(entt::entity, entt::registry &);
 ```
 
 This is mainly due to performance reasons. While the component is made available
@@ -632,7 +632,41 @@ separately to the elements that are part of the group and to those that are not,
 effectively generating two partitions, both of which can be ordered
 independently of each other.
 
-## Multiple registries to the rescue
+## Helpers
+
+The so called _helpers_ are small classes and functions mainly designed to offer
+built-in support for the most basic functionalities.<br/>
+The list of helpers will grow longer as time passes and new ideas come out.
+
+### Null entity
+
+In `EnTT`, there exists a sort of _null entity_ made available to users that is
+accessible via the `entt::null` variable.<br/>
+The library guarantees that the following expression always returns false:
+
+```cpp
+registry.valid(entt::null);
+```
+
+In other terms, a registry will reject the null entity in all cases because it
+isn't considered valid. It means that the null entity cannot own components for
+obvious reasons.<br/>
+The type of the null entity is internal and should not be used for any purpose
+other than defining the null entity itself. However, there exist implicit
+conversions from the null entity to identifiers of any allowed type:
+
+```cpp
+entt::entity null = entt::null;
+```
+
+Similarly, the null entity can be compared to any other identifier:
+
+```cpp
+const auto entity = registry.create();
+const bool null = (entity == entt::null);
+```
+
+### Multiple registries
 
 The use of multiple registries is quite common. Examples of use are the
 separation of the UI from the simulation or the loading of different scenes in
@@ -645,36 +679,109 @@ different containers.
 Once there are multiple registries available, however, a method is needed to
 transfer information from one container to another and this results in the
 `stomp` member function of the `registry` class.<br/>
-This function allows to take an entity from a registry and copy it over another
-entity in another registry (or even in place, actually making a local copy).
+This function allows to take one or more entities from a registry and use them
+to _stomp_ other entities in another registry (or even the same, actually making
+local copies).<br/>
+It opens definitely the doors to a lot of interesting features like migrating
+entities between registries, prototypes, shadow registry, prefabs, shared
+components without an explicit owner and copy-on-write policies among the other
+things.
+
+### Dependencies
+
+The `registry` class is designed to create short circuits between its functions
+within certain limits. This allows to easily define dependencies between
+different operations.<br/>
+For example, the following adds (or replaces) the component `a_type` whenever
+`my_type` is assigned to an entity:
+
+```cpp
+registry.on_construct<my_type>().connect<&entt::registry::assign_or_replace<a_type>>(registry);
+```
+
+Similarly, the code shown below removes `a_type` from an entity whenever
+`my_type` is assigned to it:
+
+```cpp
+registry.on_construct<my_type>().connect<&entt::registry::reset<a_type>>(registry);
+```
+
+A dependency can also be easily broken as follows:
+
+```cpp
+registry.on_construct<my_type>().disconnect<&entt::registry::assign_or_replace<a_type>>(registry);
+```
+
+There are many other types of dependencies besides those shown above. In
+general, all functions that accept an entity as the first argument are good
+candidates for this purpose.
+
+### Tags
+
+There's nothing magical about the way tags can be assigned to entities while
+avoiding a performance hit at runtime. Nonetheless, the syntax can be annoying
+and that's why a more user-friendly shortcut is provided to do it.<br/>
+This shortcut is the alias template `entt::tag`.
+
+If used in combination with hashed strings, it helps to use tags where types
+would be required otherwise. As an example:
+
+```cpp
+registry.assign<entt::tag<"enemy"_hs>>(entity);
+```
+
+### Actor
+
+The `actor` class is designed for those who don't feel immediately comfortable
+working with components or for those who are migrating a project and want to
+approach it one step at a time.
+
+This class acts as a thin wrapper for an entity and for all its components. It's
+constructed with a registry to be used behind the scenes and is in charge of the
+destruction of the entity when it goes out of the scope.<br/>
+An actor offers all the functionalities required to work with components, such
+as the `assign` and` remove` member functions, but also `has`,` get`, `try_get`
+and so on.
+
+My advice isn't to use the `actor` class to hide entities and components behind
+a more object-oriented interface. Instead, users should rely on it only where
+strictly necessary. In all other cases, it's highly advisable to become familiar
+with the model of `EnTT` and work directly with the registry, the views and the
+groups, rather than with a tool that could introduce a performance degradation.
 
-It opens definitely the doors to a lot of interesting features. Below is a
-brief, yet incomplete list of some of them:
+### Context variables
 
-* Prototypes or templates for high level _concepts_ to use to spawn new entities
-  when needed or to _apply_ a given set of components to an existing
-  entity.<br/>
-  Put aside the fact that having the prototypes separated from the simulation is
-  useful in many cases, they make also the codebase easier to maintain, since
-  updating a template is less error prone than jumping in the code to update all
-  the snippets copied and pasted around to initialize entities and components.
+It is often convenient to assign context variables to a registry, so as to make
+it the only _source of truth_ of an application.<br/>
+This is possible by means of a member function named `set` to use to create a
+context variable from a given type. Later on, either `ctx` or `try_ctx` can be
+used to retrieve the newly created instance and `unset` is there to literally
+reset it if needed.
 
-* Literally _move_ entities from one registry to another (that is a copy
-  followed by a destroy), which can be useful for solving problems such as the
-  migration of entities between different scenes without loss of information.
+Example of use:
 
-* Even prefabs, shared instances without explicit owner and copy-on-write
-  policies among other things are easily achievable in this way and with the
-  _right_ types in use for the components.
+```cpp
+// creates a new context variable initialized with the given values
+registry.set<my_type>(42, 'c');
 
-* Remove entities that are distant or not visible so as to reduce the processing
-  time, moving them to a _cold_ registry from which they can be easily resumed
-  at any time.
+// gets the context variable
+const auto &var = registry.ctx<my_type>();
 
-And so on. There are many things that become possible by combining multiple
-containers but none is really useful without the right means to move entities
-and components between registries.<br/>
-Therefore, this seemed a good reason to implement such a feature.
+// if in doubts, probe the registry to avoid assertions in case of errors
+if(auto *ptr = registry.try_ctx<my_type>(); ptr) {
+    // uses the context variable associated with the registry, if any
+}
+
+// unsets the context variable
+registry.unset<my_type>();
+```
+
+The type of a context variable must be such that it's default constructible and
+can be moved. The `set` member function either creates a new instance of the
+context variable or overwrites an already existing one if any. The `try_ctx`
+member function returns a pointer to the context variable if it exists,
+otherwise it returns a null pointer. This fits well with the `if` statement with
+initializer.
 
 ## Snapshot: complete vs continuous
 
@@ -915,128 +1022,6 @@ the best way to do it. However, feel free to use it at your own risk.
 The basic idea is to store everything in a group of queues in memory, then bring
 everything back to the registry with different loaders.
 
-## The actor class
-
-The `actor` class is designed for those who don't feel immediately comfortable
-working with components or for those who are migrating a project and want to
-approach it one step at a time.
-
-This class acts as a thin wrapper for an entity and for all its components. It's
-constructed with a registry to be used behind the scenes and is in charge of the
-destruction of the entity when it goes out of the scope.<br/>
-An actor offers all the functionalities required to work with components, such
-as the `assign` and` remove` member functions, but also `has`,` get`, `try_get`
-and so on.
-
-My advice isn't to use the `actor` class to hide entities and components behind
-a more object-oriented interface. Instead, users should rely on it only where
-strictly necessary. In all other cases, it's highly advisable to become familiar
-with the model of `EnTT` and work directly with the registry, the views and the
-groups, rather than with a tool that could introduce a performance degradation.
-
-## Helpers
-
-The so called _helpers_ are small classes and functions mainly designed to offer
-built-in support for the most basic functionalities.<br/>
-The list of helpers will grow longer as time passes and new ideas come out.
-
-### Dependency function
-
-A _dependency function_ is a predefined listener, actually a function template
-to use to automatically assign components to an entity when a type has a
-dependency on some other types.<br/>
-The following adds components `a_type` and `another_type` whenever `my_type` is
-assigned to an entity:
-
-```cpp
-entt::connect<a_type, another_type>(registry.on_construct<my_type>());
-```
-
-A component is assigned to an entity and thus default initialized only in case
-the entity itself hasn't it yet. It means that already existent components won't
-be overriden.<br/>
-A dependency can easily be broken by means of the following function template:
-
-```cpp
-entt::disconnect<a_type, another_type>(registry.on_construct<my_type>());
-```
-
-### Tags
-
-There's nothing magical about the way tags can be assigned to entities while
-avoiding a performance hit at runtime. Nonetheless, the syntax can be annoying
-and that's why a more user-friendly shortcut is provided to do it.<br/>
-This shortcut is the alias template `entt::tag`.
-
-If used in combination with hashed strings, it helps to use tags where types
-would be required otherwise. As an example:
-
-```cpp
-registry.assign<entt::tag<"enemy"_hs>>(entity);
-```
-
-## Null entity
-
-In `EnTT`, there exists a sort of _null entity_ made available to users that is
-accessible via the `entt::null` variable.<br/>
-The library guarantees that the following expression always returns false:
-
-```cpp
-registry.valid(entt::null);
-```
-
-In other terms, a registry will reject the null entity in all cases because it
-isn't considered valid. It means that the null entity cannot own components for
-obvious reasons.<br/>
-The type of the null entity is internal and should not be used for any purpose
-other than defining the null entity itself. However, there exist implicit
-conversions from the null entity to identifiers of any allowed type:
-
-```cpp
-entt::entity null = entt::null;
-```
-
-Similarly, the null entity can be compared to any other identifier:
-
-```cpp
-const auto entity = registry.create();
-const bool null = (entity == entt::null);
-```
-
-## Context variables
-
-It is often convenient to assign context variables to a registry, so as to make
-it the only _source of truth_ of an application.<br/>
-This is possible by means of a member function named `set` to use to create a
-context variable from a given type. Later on, either `ctx` or `try_ctx` can be
-used to retrieve the newly created instance and `unset` is there to literally
-reset it if needed.
-
-Example of use:
-
-```cpp
-// creates a new context variable initialized with the given values
-registry.set<my_type>(42, 'c');
-
-// gets the context variable
-const auto &var = registry.ctx<my_type>();
-
-// if in doubts, probe the registry to avoid assertions in case of errors
-if(auto *ptr = registry.try_ctx<my_type>(); ptr) {
-    // uses the context variable associated with the registry, if any
-}
-
-// unsets the context variable
-registry.unset<my_type>();
-```
-
-The type of a context variable must be such that it's default constructible and
-can be moved. The `set` member function either creates a new instance of the
-context variable or overwrites an already existing one if any. The `try_ctx`
-member function returns a pointer to the context variable if it exists,
-otherwise it returns a null pointer. This fits well with the `if` statement with
-initializer.
-
 # Views and Groups
 
 First of all, it is worth answering an obvious question: why views and

+ 19 - 4
src/entt/entity/helper.hpp

@@ -112,6 +112,11 @@ as_group(const basic_registry<Entity> &) ENTT_NOEXCEPT -> as_group<true, Entity>
 /**
  * @brief Dependency function prototype.
  *
+ * @deprecated
+ * This function will be wiped out in a future version of the library.<br/>
+ * Use shortcuts and cross links between registries to achieve the same result
+ * in a more idiomatic way.
+ *
  * A _dependency function_ is a built-in listener to use to automatically assign
  * components to an entity when a type has a dependency on some other types.
  *
@@ -120,11 +125,11 @@ as_group(const basic_registry<Entity> &) ENTT_NOEXCEPT -> as_group<true, Entity>
  *
  * @tparam Entity A valid entity type (see entt_traits for more details).
  * @tparam Dependency Types of components to assign to an entity if triggered.
- * @param reg A valid reference to a registry.
  * @param entt A valid entity identifier.
+ * @param reg A valid reference to a registry.
  */
 template<typename Entity, typename... Dependency>
-void dependency(basic_registry<Entity> &reg, const Entity entt) {
+void dependency(const Entity entt, basic_registry<Entity> &reg) {
     ((reg.template has<Dependency>(entt) ? void() : (reg.template assign<Dependency>(entt), void())), ...);
 }
 
@@ -132,6 +137,11 @@ void dependency(basic_registry<Entity> &reg, const Entity entt) {
 /**
  * @brief Connects a dependency function to the given sink.
  *
+ * @deprecated
+ * This function will be wiped out in a future version of the library.<br/>
+ * Use shortcuts and cross links between registries to achieve the same result
+ * in a more idiomatic way.
+ *
  * A _dependency function_ is a built-in listener to use to automatically assign
  * components to an entity when a type has a dependency on some other types.
  *
@@ -148,7 +158,7 @@ void dependency(basic_registry<Entity> &reg, const Entity entt) {
  * @param sink A sink object properly initialized.
  */
 template<typename... Dependency, typename Component, typename Entity>
-void connect(sink<void(basic_registry<Entity> &, const Entity, Component &)> sink) {
+void connect(sink<void(const Entity, basic_registry<Entity> &, Component &)> sink) {
     constexpr auto function = &dependency<Entity, Dependency...>;
     sink.template connect<function>();
 }
@@ -157,6 +167,11 @@ void connect(sink<void(basic_registry<Entity> &, const Entity, Component &)> sin
 /**
  * @brief Disconnects a dependency function from the given sink.
  *
+ * @deprecated
+ * This function will be wiped out in a future version of the library.<br/>
+ * Use shortcuts and cross links between registries to achieve the same result
+ * in a more idiomatic way.
+ *
  * A _dependency function_ is a built-in listener to use to automatically assign
  * components to an entity when a type has a dependency on some other types.
  *
@@ -173,7 +188,7 @@ void connect(sink<void(basic_registry<Entity> &, const Entity, Component &)> sin
  * @param sink A sink object properly initialized.
  */
 template<typename... Dependency, typename Component, typename Entity>
-void disconnect(sink<void(basic_registry<Entity> &, const Entity, Component &)> sink) {
+void disconnect(sink<void(const Entity, basic_registry<Entity> &, Component &)> sink) {
     constexpr auto function = &dependency<Entity, Dependency...>;
     sink.template disconnect<function>();
 }

+ 4 - 4
src/entt/entity/observer.hpp

@@ -173,7 +173,7 @@ class basic_observer {
     template<typename... Reject, typename... Require, typename AnyOf>
     struct matcher_handler<matcher<matcher<type_list<Reject...>, type_list<Require...>>, AnyOf>> {
         template<std::size_t Index>
-        static void maybe_valid_if(basic_observer &obs, const basic_registry<Entity> &reg, const Entity entt) {
+        static void maybe_valid_if(basic_observer &obs, const Entity entt, const basic_registry<Entity> &reg) {
             if(reg.template has<Require...>(entt) && !(reg.template has<Reject>(entt) || ...)) {
                 auto *comp = obs.view.try_get(entt);
                 (comp ? *comp : obs.view.construct(entt)) |= (1 << Index);
@@ -181,7 +181,7 @@ class basic_observer {
         }
 
         template<std::size_t Index>
-        static void discard_if(basic_observer &obs, const basic_registry<Entity> &, const Entity entt) {
+        static void discard_if(basic_observer &obs, const Entity entt) {
             if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) {
                 obs.view.destroy(entt);
             }
@@ -206,7 +206,7 @@ class basic_observer {
     template<typename... Reject, typename... Require, typename... NoneOf, typename... AllOf>
     struct matcher_handler<matcher<matcher<type_list<Reject...>, type_list<Require...>>, type_list<NoneOf...>, type_list<AllOf...>>> {
         template<std::size_t Index>
-        static void maybe_valid_if(basic_observer &obs, const basic_registry<Entity> &reg, const Entity entt) {
+        static void maybe_valid_if(basic_observer &obs, const Entity entt, const basic_registry<Entity> &reg) {
             if(reg.template has<AllOf...>(entt) && !(reg.template has<NoneOf>(entt) || ...)
                     && reg.template has<Require...>(entt) && !(reg.template has<Reject>(entt) || ...))
             {
@@ -216,7 +216,7 @@ class basic_observer {
         }
 
         template<std::size_t Index>
-        static void discard_if(basic_observer &obs, const basic_registry<Entity> &, const Entity entt) {
+        static void discard_if(basic_observer &obs, const Entity entt) {
             if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) {
                 obs.view.destroy(entt);
             }

+ 2 - 2
src/entt/entity/prototype.hpp

@@ -21,8 +21,8 @@ namespace entt {
  *
  * @deprecated
  * This class will be wiped out in a future version of the library.<br/>
- * Use a shadow registry and the new `registry::stomp` functionality to achieve
- * the same result in a more idiomatic way.
+ * Use a prototype registry and the new `registry::stomp` functionality to
+ * achieve the same result in a more idiomatic way.
  *
  * A prototype is used to define a _concept_ in terms of components.<br/>
  * Prototypes act as templates for those specific types of an application which

+ 17 - 17
src/entt/entity/registry.hpp

@@ -76,11 +76,11 @@ class basic_registry {
         decltype(auto) assign(basic_registry &registry, const Entity entt, Args &&... args) {
             if constexpr(std::is_empty_v<Component>) {
                 storage<Entity, Component>::construct(entt);
-                construction.publish(registry, entt, Component{});
+                construction.publish(entt, registry, Component{});
                 return Component{std::forward<Args>(args)...};
             } else {
                 auto &component = storage<Entity, Component>::construct(entt, std::forward<Args>(args)...);
-                construction.publish(registry, entt, component);
+                construction.publish(entt, registry, component);
                 return component;
             }
         }
@@ -94,7 +94,7 @@ class basic_registry {
 
                 if(!construction.empty()) {
                     std::for_each(first, last, [this, &registry](const auto entt) {
-                        construction.publish(registry, entt, Component{});
+                        construction.publish(entt, registry, Component{});
                     });
                 }
             } else {
@@ -102,7 +102,7 @@ class basic_registry {
 
                 if(!construction.empty()) {
                     std::for_each(first, last, [this, &registry, component](const auto entt) mutable {
-                        construction.publish(registry, entt, *(component++));
+                        construction.publish(entt, registry, *(component++));
                     });
                 }
             }
@@ -111,7 +111,7 @@ class basic_registry {
         }
 
         void remove(basic_registry &registry, const Entity entt) {
-            destruction.publish(registry, entt);
+            destruction.publish(entt, registry);
             storage<Entity, Component>::destroy(entt);
         }
 
@@ -119,20 +119,20 @@ class basic_registry {
         decltype(auto) replace(basic_registry &registry, const Entity entt, Args &&... args) {
             if constexpr(std::is_empty_v<Component>) {
                 ENTT_ASSERT((storage<Entity, Component>::has(entt)));
-                update.publish(registry, entt, Component{});
+                update.publish(entt, registry, Component{});
                 return Component{std::forward<Args>(args)...};
             } else {
                 Component component{std::forward<Args>(args)...};
-                update.publish(registry, entt, component);
+                update.publish(entt, registry, component);
                 return (storage<Entity, Component>::get(entt) = std::move(component));
             }
         }
 
     private:
         using reference_type = std::conditional_t<std::is_empty_v<Component>, const Component &, Component &>;
-        sigh<void(basic_registry &, const Entity, reference_type)> construction{};
-        sigh<void(basic_registry &, const Entity, reference_type)> update{};
-        sigh<void(basic_registry &, const Entity)> destruction{};
+        sigh<void(const Entity, basic_registry &, reference_type)> construction{};
+        sigh<void(const Entity, basic_registry &, reference_type)> update{};
+        sigh<void(const Entity, basic_registry &)> destruction{};
     };
 
     template<typename Component>
@@ -146,7 +146,7 @@ class basic_registry {
         std::tuple<pool_type<Get> *..., pool_type<Exclude> *...> cpools{};
 
         template<typename Component>
-        void maybe_valid_if(const basic_registry &, const Entity entt) {
+        void maybe_valid_if(const Entity entt) {
             if constexpr(std::disjunction_v<std::is_same<Get, Component>...>) {
                 if(((std::is_same_v<Component, Get> || std::get<pool_type<Get> *>(cpools)->has(entt)) && ...)
                         && !(std::get<pool_type<Exclude> *>(cpools)->has(entt) || ...))
@@ -161,7 +161,7 @@ class basic_registry {
             }
         }
 
-        void discard_if(const basic_registry &, const Entity entt) {
+        void discard_if(const Entity entt) {
             if(this->has(entt)) {
                 this->destroy(entt);
             }
@@ -173,7 +173,7 @@ class basic_registry {
         std::tuple<pool_type<Owned> *..., pool_type<Get> *..., pool_type<Exclude> *...> cpools{};
 
         template<typename Component>
-        void maybe_valid_if(const basic_registry &, const Entity entt) {
+        void maybe_valid_if(const Entity entt) {
             if constexpr(std::disjunction_v<std::is_same<Owned, Component>..., std::is_same<Get, Component>...>) {
                 if(((std::is_same_v<Component, Owned> || std::get<pool_type<Owned> *>(cpools)->has(entt)) && ...)
                         && ((std::is_same_v<Component, Get> || std::get<pool_type<Get> *>(cpools)->has(entt)) && ...)
@@ -193,7 +193,7 @@ class basic_registry {
             }
         }
 
-        void discard_if(const basic_registry &, const Entity entt) {
+        void discard_if(const Entity entt) {
             if(std::get<0>(cpools)->has(entt) && std::get<0>(cpools)->sparse_set<Entity>::get(entt) < this->owned) {
                 const auto pos = --this->owned;
                 (std::get<pool_type<Owned> *>(cpools)->swap(std::get<pool_type<Owned> *>(cpools)->sparse_set<Entity>::get(entt), pos), ...);
@@ -906,7 +906,7 @@ public:
      * The function type for a listener is equivalent to:
      *
      * @code{.cpp}
-     * void(registry<Entity> &, Entity, Component &);
+     * void(Entity, registry<Entity> &, Component &);
      * @endcode
      *
      * Listeners are invoked **after** the component has been assigned to the
@@ -937,7 +937,7 @@ public:
      * The function type for a listener is equivalent to:
      *
      * @code{.cpp}
-     * void(registry<Entity> &, Entity, Component &);
+     * void(Entity, registry<Entity> &, Component &);
      * @endcode
      *
      * Listeners are invoked **before** the component has been replaced. The
@@ -969,7 +969,7 @@ public:
      * The function type for a listener is equivalent to:
      *
      * @code{.cpp}
-     * void(registry<Entity> &, Entity);
+     * void(Entity, registry<Entity> &);
      * @endcode
      *
      * Listeners are invoked **before** the component has been removed from the

+ 31 - 2
test/entt/entity/registry.cpp

@@ -15,7 +15,7 @@ struct empty_type {};
 
 struct listener {
     template<typename Component>
-    void incr(entt::registry &registry, entt::entity entity, const Component &) {
+    void incr(entt::entity entity, entt::registry &registry, const Component &) {
         ASSERT_TRUE(registry.valid(entity));
         ASSERT_TRUE(registry.has<Component>(entity));
         last = entity;
@@ -23,7 +23,7 @@ struct listener {
     }
 
     template<typename Component>
-    void decr(entt::registry &registry, entt::entity entity) {
+    void decr(entt::entity entity, entt::registry &registry) {
         ASSERT_TRUE(registry.valid(entity));
         ASSERT_TRUE(registry.has<Component>(entity));
         last = entity;
@@ -1524,3 +1524,32 @@ TEST(Registry, MoveOnlyComponent) {
     const auto entity = registry.create();
     registry.assign<std::unique_ptr<int>>(entity);
 }
+
+TEST(Registry, Dependencies) {
+    entt::registry registry;
+    const auto entity = registry.create();
+
+    registry.on_construct<int>().connect<&entt::registry::assign_or_replace<double>>(registry);
+    registry.on_destroy<int>().connect<&entt::registry::remove<double>>(registry);
+    registry.assign<double>(entity, .3);
+
+    ASSERT_FALSE(registry.has<int>(entity));
+    ASSERT_EQ(registry.get<double>(entity), .3);
+
+    registry.assign<int>(entity);
+
+    ASSERT_TRUE(registry.has<int>(entity));
+    ASSERT_EQ(registry.get<double>(entity), .0);
+
+    registry.remove<int>(entity);
+
+    ASSERT_FALSE(registry.has<int>(entity));
+    ASSERT_FALSE(registry.has<double>(entity));
+
+    registry.on_construct<int>().disconnect<&entt::registry::assign_or_replace<double>>(registry);
+    registry.on_destroy<int>().disconnect<&entt::registry::remove<double>>(registry);
+    registry.assign<int>(entity);
+
+    ASSERT_TRUE(registry.has<int>(entity));
+    ASSERT_FALSE(registry.has<double>(entity));
+}