Ver Fonte

registry: updated signatures for callbacks, deprecated dependency

Michele Caini há 6 anos atrás
pai
commit
a06d5dd7bb

+ 1 - 3
TODO

@@ -19,6 +19,7 @@
 * allow to "merge" registries easily
 * allow to "merge" registries easily
 * allow for custom stomp functions
 * allow for custom stomp functions
 * deprecate/replace snapshot
 * deprecate/replace snapshot
+* remove dependency
 * remove prototype
 * remove prototype
 
 
 TODO
 TODO
@@ -27,6 +28,3 @@ TODO
 * make meta work across boundaries
 * make meta work across boundaries
   - inline variables are fine here, only the head represents a problem
   - 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
   - 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)
   * [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)
-  * [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: complete vs continuous](#snapshot-complete-vs-continuous)
     * [Snapshot loader](#snapshot-loader)
     * [Snapshot loader](#snapshot-loader)
     * [Continuous loader](#continuous-loader)
     * [Continuous loader](#continuous-loader)
     * [Archives](#archives)
     * [Archives](#archives)
     * [One example to rule them all](#one-example-to-rule-them-all)
     * [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 and Groups](#views-and-groups)
   * [Views](#views)
   * [Views](#views)
   * [Runtime views](#runtime-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:
 to the following:
 
 
 ```cpp
 ```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
 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:
 the destruction signal is also similar, except for the `Component` parameter:
 
 
 ```cpp
 ```cpp
-void(entt::registry &, entt::entity);
+void(entt::entity, entt::registry &);
 ```
 ```
 
 
 This is mainly due to performance reasons. While the component is made available
 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
 effectively generating two partitions, both of which can be ordered
 independently of each other.
 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
 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
 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
 Once there are multiple registries available, however, a method is needed to
 transfer information from one container to another and this results in the
 transfer information from one container to another and this results in the
 `stomp` member function of the `registry` class.<br/>
 `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
 ## 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
 The basic idea is to store everything in a group of queues in memory, then bring
 everything back to the registry with different loaders.
 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
 # Views and Groups
 
 
 First of all, it is worth answering an obvious question: why views and
 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.
  * @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
  * 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.
  * 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 Entity A valid entity type (see entt_traits for more details).
  * @tparam Dependency Types of components to assign to an entity if triggered.
  * @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 entt A valid entity identifier.
+ * @param reg A valid reference to a registry.
  */
  */
 template<typename Entity, typename... Dependency>
 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())), ...);
     ((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.
  * @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
  * 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.
  * 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.
  * @param sink A sink object properly initialized.
  */
  */
 template<typename... Dependency, typename Component, typename Entity>
 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...>;
     constexpr auto function = &dependency<Entity, Dependency...>;
     sink.template connect<function>();
     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.
  * @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
  * 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.
  * 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.
  * @param sink A sink object properly initialized.
  */
  */
 template<typename... Dependency, typename Component, typename Entity>
 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...>;
     constexpr auto function = &dependency<Entity, Dependency...>;
     sink.template disconnect<function>();
     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>
     template<typename... Reject, typename... Require, typename AnyOf>
     struct matcher_handler<matcher<matcher<type_list<Reject...>, type_list<Require...>>, AnyOf>> {
     struct matcher_handler<matcher<matcher<type_list<Reject...>, type_list<Require...>>, AnyOf>> {
         template<std::size_t Index>
         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) || ...)) {
             if(reg.template has<Require...>(entt) && !(reg.template has<Reject>(entt) || ...)) {
                 auto *comp = obs.view.try_get(entt);
                 auto *comp = obs.view.try_get(entt);
                 (comp ? *comp : obs.view.construct(entt)) |= (1 << Index);
                 (comp ? *comp : obs.view.construct(entt)) |= (1 << Index);
@@ -181,7 +181,7 @@ class basic_observer {
         }
         }
 
 
         template<std::size_t Index>
         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)))) {
             if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) {
                 obs.view.destroy(entt);
                 obs.view.destroy(entt);
             }
             }
@@ -206,7 +206,7 @@ class basic_observer {
     template<typename... Reject, typename... Require, typename... NoneOf, typename... AllOf>
     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...>>> {
     struct matcher_handler<matcher<matcher<type_list<Reject...>, type_list<Require...>>, type_list<NoneOf...>, type_list<AllOf...>>> {
         template<std::size_t Index>
         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) || ...)
             if(reg.template has<AllOf...>(entt) && !(reg.template has<NoneOf>(entt) || ...)
                     && reg.template has<Require...>(entt) && !(reg.template has<Reject>(entt) || ...))
                     && reg.template has<Require...>(entt) && !(reg.template has<Reject>(entt) || ...))
             {
             {
@@ -216,7 +216,7 @@ class basic_observer {
         }
         }
 
 
         template<std::size_t Index>
         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)))) {
             if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) {
                 obs.view.destroy(entt);
                 obs.view.destroy(entt);
             }
             }

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

@@ -21,8 +21,8 @@ namespace entt {
  *
  *
  * @deprecated
  * @deprecated
  * This class will be wiped out in a future version of the library.<br/>
  * 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/>
  * 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
  * 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) {
         decltype(auto) assign(basic_registry &registry, const Entity entt, Args &&... args) {
             if constexpr(std::is_empty_v<Component>) {
             if constexpr(std::is_empty_v<Component>) {
                 storage<Entity, Component>::construct(entt);
                 storage<Entity, Component>::construct(entt);
-                construction.publish(registry, entt, Component{});
+                construction.publish(entt, registry, Component{});
                 return Component{std::forward<Args>(args)...};
                 return Component{std::forward<Args>(args)...};
             } else {
             } else {
                 auto &component = storage<Entity, Component>::construct(entt, std::forward<Args>(args)...);
                 auto &component = storage<Entity, Component>::construct(entt, std::forward<Args>(args)...);
-                construction.publish(registry, entt, component);
+                construction.publish(entt, registry, component);
                 return component;
                 return component;
             }
             }
         }
         }
@@ -94,7 +94,7 @@ class basic_registry {
 
 
                 if(!construction.empty()) {
                 if(!construction.empty()) {
                     std::for_each(first, last, [this, &registry](const auto entt) {
                     std::for_each(first, last, [this, &registry](const auto entt) {
-                        construction.publish(registry, entt, Component{});
+                        construction.publish(entt, registry, Component{});
                     });
                     });
                 }
                 }
             } else {
             } else {
@@ -102,7 +102,7 @@ class basic_registry {
 
 
                 if(!construction.empty()) {
                 if(!construction.empty()) {
                     std::for_each(first, last, [this, &registry, component](const auto entt) mutable {
                     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) {
         void remove(basic_registry &registry, const Entity entt) {
-            destruction.publish(registry, entt);
+            destruction.publish(entt, registry);
             storage<Entity, Component>::destroy(entt);
             storage<Entity, Component>::destroy(entt);
         }
         }
 
 
@@ -119,20 +119,20 @@ class basic_registry {
         decltype(auto) replace(basic_registry &registry, const Entity entt, Args &&... args) {
         decltype(auto) replace(basic_registry &registry, const Entity entt, Args &&... args) {
             if constexpr(std::is_empty_v<Component>) {
             if constexpr(std::is_empty_v<Component>) {
                 ENTT_ASSERT((storage<Entity, Component>::has(entt)));
                 ENTT_ASSERT((storage<Entity, Component>::has(entt)));
-                update.publish(registry, entt, Component{});
+                update.publish(entt, registry, Component{});
                 return Component{std::forward<Args>(args)...};
                 return Component{std::forward<Args>(args)...};
             } else {
             } else {
                 Component component{std::forward<Args>(args)...};
                 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));
                 return (storage<Entity, Component>::get(entt) = std::move(component));
             }
             }
         }
         }
 
 
     private:
     private:
         using reference_type = std::conditional_t<std::is_empty_v<Component>, const Component &, Component &>;
         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>
     template<typename Component>
@@ -146,7 +146,7 @@ class basic_registry {
         std::tuple<pool_type<Get> *..., pool_type<Exclude> *...> cpools{};
         std::tuple<pool_type<Get> *..., pool_type<Exclude> *...> cpools{};
 
 
         template<typename Component>
         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 constexpr(std::disjunction_v<std::is_same<Get, Component>...>) {
                 if(((std::is_same_v<Component, Get> || std::get<pool_type<Get> *>(cpools)->has(entt)) && ...)
                 if(((std::is_same_v<Component, Get> || std::get<pool_type<Get> *>(cpools)->has(entt)) && ...)
                         && !(std::get<pool_type<Exclude> *>(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)) {
             if(this->has(entt)) {
                 this->destroy(entt);
                 this->destroy(entt);
             }
             }
@@ -173,7 +173,7 @@ class basic_registry {
         std::tuple<pool_type<Owned> *..., pool_type<Get> *..., pool_type<Exclude> *...> cpools{};
         std::tuple<pool_type<Owned> *..., pool_type<Get> *..., pool_type<Exclude> *...> cpools{};
 
 
         template<typename Component>
         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 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)) && ...)
                 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)) && ...)
                         && ((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) {
             if(std::get<0>(cpools)->has(entt) && std::get<0>(cpools)->sparse_set<Entity>::get(entt) < this->owned) {
                 const auto pos = --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), ...);
                 (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:
      * The function type for a listener is equivalent to:
      *
      *
      * @code{.cpp}
      * @code{.cpp}
-     * void(registry<Entity> &, Entity, Component &);
+     * void(Entity, registry<Entity> &, Component &);
      * @endcode
      * @endcode
      *
      *
      * Listeners are invoked **after** the component has been assigned to the
      * 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:
      * The function type for a listener is equivalent to:
      *
      *
      * @code{.cpp}
      * @code{.cpp}
-     * void(registry<Entity> &, Entity, Component &);
+     * void(Entity, registry<Entity> &, Component &);
      * @endcode
      * @endcode
      *
      *
      * Listeners are invoked **before** the component has been replaced. The
      * Listeners are invoked **before** the component has been replaced. The
@@ -969,7 +969,7 @@ public:
      * The function type for a listener is equivalent to:
      * The function type for a listener is equivalent to:
      *
      *
      * @code{.cpp}
      * @code{.cpp}
-     * void(registry<Entity> &, Entity);
+     * void(Entity, registry<Entity> &);
      * @endcode
      * @endcode
      *
      *
      * Listeners are invoked **before** the component has been removed from the
      * 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 {
 struct listener {
     template<typename Component>
     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.valid(entity));
         ASSERT_TRUE(registry.has<Component>(entity));
         ASSERT_TRUE(registry.has<Component>(entity));
         last = entity;
         last = entity;
@@ -23,7 +23,7 @@ struct listener {
     }
     }
 
 
     template<typename Component>
     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.valid(entity));
         ASSERT_TRUE(registry.has<Component>(entity));
         ASSERT_TRUE(registry.has<Component>(entity));
         last = entity;
         last = entity;
@@ -1524,3 +1524,32 @@ TEST(Registry, MoveOnlyComponent) {
     const auto entity = registry.create();
     const auto entity = registry.create();
     registry.assign<std::unique_ptr<int>>(entity);
     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));
+}