From 2d69e5f6ffd182810b776a3acdb7ed7349663239 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Thu, 10 Sep 2020 16:45:47 +0200 Subject: [PATCH] doc: updated documentation about signals from a registry --- docs/md/entity.md | 21 ++-- src/entt/entity/pool.hpp | 186 ++++++++++++++++++++++++++++++++--- src/entt/entity/registry.hpp | 18 ++-- 3 files changed, 186 insertions(+), 39 deletions(-) diff --git a/docs/md/entity.md b/docs/md/entity.md index cb9768680..22e8dd520 100644 --- a/docs/md/entity.md +++ b/docs/md/entity.md @@ -364,7 +364,7 @@ registry.on_construct().disconnect<&my_free_function>(); registry.on_construct().disconnect<&my_class::member>(instance); ``` -To be notified when components are destroyed, use the `on_destroy` member +To be notified when a component is destroyed, use the `on_destroy` member function instead. Finally, the `on_update` member function will return a sink to which to connect listeners to observe changes.
In the last case, given the way C++ works, it's also necessary to use specific @@ -375,12 +375,9 @@ attached to `on_update` will only be invoked following a call to `replace` or The function type of a listener should be equivalent to the following: ```cpp -void(entt::registry &, entt::entity); +void(entt::entity); ``` -In all cases, listeners are provided with the registry that triggered the -notification and the involved entity. - Note also that: * Listeners for the construction signals are invoked **after** components have @@ -633,25 +630,25 @@ that isn't associated with the given registry can result in undefined behavior. The `registry` class is designed to be able to create short circuits between its functions. This simplifies the definition of _dependencies_ between different -operations.
+operations and registries.
For example, the following adds (or replaces) the component `a_type` whenever `my_type` is assigned to an entity: ```cpp -registry.on_construct().connect<&entt::registry::emplace_or_replace>(); +registry.on_construct().connect<&entt::registry::emplace_or_replace>(registry); ``` Similarly, the code shown below removes `a_type` from an entity whenever `my_type` is assigned to it: ```cpp -registry.on_construct().connect<&entt::registry::remove>(); +registry.on_construct().connect<&entt::registry::remove>(registry); ``` A dependency can also be easily broken as follows: ```cpp -registry.on_construct().disconnect<&entt::registry::emplace_or_replace>(); +registry.on_construct().disconnect<&entt::registry::emplace_or_replace>(registry); ``` There are many other types of dependencies. In general, most of the functions @@ -666,11 +663,13 @@ _extend_ their classes and this may not always be possible.
The `invoke` helper allows to _propagate_ the signal in these cases: ```cpp -registry.on_construct().connect>(); +registry.on_construct().connect>(registry); ``` All it does is pick up the _right_ component for the received entity and invoke -the requested method, passing on the arguments if necessary. +the requested method, passing on the arguments if necessary.
+The registry is also supplied by the invoker directly to the function invoked as +the first argument. ### Handle diff --git a/src/entt/entity/pool.hpp b/src/entt/entity/pool.hpp index 6e26cd3d9..e5603c72a 100644 --- a/src/entt/entity/pool.hpp +++ b/src/entt/entity/pool.hpp @@ -14,35 +14,126 @@ namespace entt { -template -struct default_pool final: storage { - static_assert(std::is_same_v>, "Invalid component type"); +/** + * @brief Default pool implementation. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects assigned to the entities. + */ +template +struct default_pool final: storage { + static_assert(std::is_same_v>, "Invalid object type"); + /*! @brief Type of the objects associated with the entities. */ + using object_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever a new instance is created and assigned to an entity.
+ * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(Entity); + * @endcode + * + * Listeners are invoked **after** the object has been assigned to the + * entity. + * + * @sa sink + * + * @return A temporary sink object. + */ [[nodiscard]] auto on_construct() ENTT_NOEXCEPT { return sink{construction}; } + /** + * @brief Returns a sink object for the given type. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.
+ * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(Entity); + * @endcode + * + * Listeners are invoked **after** the object has been updated. + * + * @sa sink + * + * @return A temporary sink object. + */ [[nodiscard]] auto on_update() ENTT_NOEXCEPT { return sink{update}; } + /** + * @brief Returns a sink object for the given type. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.
+ * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(Entity); + * @endcode + * + * Listeners are invoked **before** the object has been removed from the + * entity. + * + * @sa sink + * + * @return A temporary sink object. + */ [[nodiscard]] auto on_destroy() ENTT_NOEXCEPT { return sink{destruction}; } + /** + * @brief Assigns an entity to a pool. + * + * A new object is created and initialized with the arguments provided (the + * object type must have a proper constructor or be of aggregate type). Then + * the instance is assigned to the given entity. + * + * @warning + * Attempting to use an invalid entity or to assign an entity that already + * belongs to the pool results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity already belongs to the pool. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the object. + * @return A reference to the newly created object. + */ template - decltype(auto) emplace(const Entity entt, Args &&... args) { - storage::emplace(entt, std::forward(args)...); + decltype(auto) emplace(const entity_type entt, Args &&... args) { + storage::emplace(entt, std::forward(args)...); construction.publish(entt); - if constexpr(!is_eto_eligible_v) { + if constexpr(!is_eto_eligible_v) { return this->get(entt); } } + /** + * @brief Assigns multiple entities to a pool. + * + * @sa emplace + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the type to assign. + */ template void insert(It first, It last, Args &&... args) { - storage::insert(first, last, std::forward(args)...); + storage::insert(first, last, std::forward(args)...); if(!construction.empty()) { for(; first != last; ++first) { @@ -51,11 +142,31 @@ struct default_pool final: storage { } } - void erase(const Entity entt) override { + /** + * @brief Removes an entity from a pool. + * + * @warning + * Attempting to use an invalid entity or to remove an entity that doesn't + * belong to the pool results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't belong to the pool. + * + * @param entity A valid entity identifier. + */ + void erase(const entity_type entt) override { destruction.publish(entt); - storage::erase(entt); + storage::erase(entt); } + /** + * @brief Removes multiple entities from a pool. + * + * @see remove + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ template void erase(It first, It last) { if(std::distance(first, last) == std::distance(this->begin(), this->end())) { @@ -73,9 +184,34 @@ struct default_pool final: storage { } } + /** + * @brief Patches the given instance for an entity. + * + * The signature of the functions should be equivalent to the following: + * + * @code{.cpp} + * void(Type &); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned. However, this function can be used to trigger an update signal + * for them. + * + * @warning + * Attempting to use an invalid entity or to patch an object of an entity + * that doesn't belong to the pool results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't belong to the pool. + * + * @tparam Func Types of the function objects to invoke. + * @param entity A valid entity identifier. + * @param func Valid function objects. + * @return A reference to the patched instance. + */ template - decltype(auto) patch(const Entity entt, [[maybe_unused]] Func &&... func) { - if constexpr(is_eto_eligible_v) { + decltype(auto) patch(const entity_type entt, [[maybe_unused]] Func &&... func) { + if constexpr(is_eto_eligible_v) { update.publish(entt); } else { (std::forward(func)(this->get(entt)), ...); @@ -84,14 +220,32 @@ struct default_pool final: storage { } } - decltype(auto) replace(const Entity entt, Component component) { - return patch(entt, [&component](auto &&curr) { curr = std::move(component); }); + /** + * @brief Replaces the object associated with an entity in a pool. + * + * A new object is created and initialized with the arguments provided (the + * object type must have a proper constructor or be of aggregate type). Then + * the instance is assigned to the given entity. + * + * @warning + * Attempting to use an invalid entity or to replace an object of an entity + * that doesn't belong to the pool results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't belong to the pool. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the object. + * @return A reference to the object being replaced. + */ + decltype(auto) replace(const entity_type entt, object_type instance) { + return patch(entt, [&instance](auto &&curr) { curr = std::move(instance); }); } private: - sigh construction{}; - sigh destruction{}; - sigh update{}; + sigh construction{}; + sigh destruction{}; + sigh update{}; }; diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index cd673e6c1..0f7cf4751 100644 --- a/src/entt/entity/registry.hpp +++ b/src/entt/entity/registry.hpp @@ -1015,15 +1015,13 @@ public: /** * @brief Returns a sink object for the given component. * - * A sink is an opaque object used to connect listeners to components.
* The sink returned by this function can be used to receive notifications * whenever a new instance of the given component is created and assigned to - * an entity. - * + * an entity.
* The function type for a listener is equivalent to: * * @code{.cpp} - * void(registry &, Entity); + * void(Entity); * @endcode * * Listeners are invoked **after** the component has been assigned to the @@ -1042,14 +1040,12 @@ public: /** * @brief Returns a sink object for the given component. * - * A sink is an opaque object used to connect listeners to components.
* The sink returned by this function can be used to receive notifications - * whenever an instance of the given component is explicitly updated. - * + * whenever an instance of the given component is explicitly updated.
* The function type for a listener is equivalent to: * * @code{.cpp} - * void(registry &, Entity); + * void(Entity); * @endcode * * Listeners are invoked **after** the component has been updated. @@ -1067,15 +1063,13 @@ public: /** * @brief Returns a sink object for the given component. * - * A sink is an opaque object used to connect listeners to components.
* The sink returned by this function can be used to receive notifications * whenever an instance of the given component is removed from an entity and - * thus destroyed. - * + * thus destroyed.
* The function type for a listener is equivalent to: * * @code{.cpp} - * void(registry &, Entity); + * void(Entity); * @endcode * * Listeners are invoked **before** the component has been removed from the