doc: updated documentation about signals from a registry

This commit is contained in:
Michele Caini
2020-09-10 16:45:47 +02:00
parent 23f0fcd024
commit 2d69e5f6ff
3 changed files with 186 additions and 39 deletions

View File

@@ -364,7 +364,7 @@ registry.on_construct<position>().disconnect<&my_free_function>();
registry.on_construct<position>().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.<br/>
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.<br/>
operations and registries.<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::emplace_or_replace<a_type>>();
registry.on_construct<my_type>().connect<&entt::registry::emplace_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::remove<a_type>>();
registry.on_construct<my_type>().connect<&entt::registry::remove<a_type>>(registry);
```
A dependency can also be easily broken as follows:
```cpp
registry.on_construct<my_type>().disconnect<&entt::registry::emplace_or_replace<a_type>>();
registry.on_construct<my_type>().disconnect<&entt::registry::emplace_or_replace<a_type>>(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.<br/>
The `invoke` helper allows to _propagate_ the signal in these cases:
```cpp
registry.on_construct<clazz>().connect<entt::invoke<&clazz::func>>();
registry.on_construct<clazz>().connect<entt::invoke<&clazz::func>>(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.<br/>
The registry is also supplied by the invoker directly to the function invoked as
the first argument.
### Handle

View File

@@ -14,35 +14,126 @@
namespace entt {
template<typename Entity, typename Component>
struct default_pool final: storage<Entity, Component> {
static_assert(std::is_same_v<Component, std::decay_t<Component>>, "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<typename Entity, typename Type>
struct default_pool final: storage<Entity, Type> {
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "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.<br/>
* 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.<br/>
* 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.<br/>
* 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.<br/>
* 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<typename... Args>
decltype(auto) emplace(const Entity entt, Args &&... args) {
storage<entity_type, Component>::emplace(entt, std::forward<Args>(args)...);
decltype(auto) emplace(const entity_type entt, Args &&... args) {
storage<entity_type, Type>::emplace(entt, std::forward<Args>(args)...);
construction.publish(entt);
if constexpr(!is_eto_eligible_v<Component>) {
if constexpr(!is_eto_eligible_v<object_type>) {
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<typename It, typename... Args>
void insert(It first, It last, Args &&... args) {
storage<entity_type, Component>::insert(first, last, std::forward<Args>(args)...);
storage<entity_type, object_type>::insert(first, last, std::forward<Args>(args)...);
if(!construction.empty()) {
for(; first != last; ++first) {
@@ -51,11 +142,31 @@ struct default_pool final: storage<Entity, Component> {
}
}
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.<br/>
* 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<entity_type, Component>::erase(entt);
storage<entity_type, object_type>::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<typename It>
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<Entity, Component> {
}
}
/**
* @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.<br/>
* 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<typename... Func>
decltype(auto) patch(const Entity entt, [[maybe_unused]] Func &&... func) {
if constexpr(is_eto_eligible_v<Component>) {
decltype(auto) patch(const entity_type entt, [[maybe_unused]] Func &&... func) {
if constexpr(is_eto_eligible_v<object_type>) {
update.publish(entt);
} else {
(std::forward<Func>(func)(this->get(entt)), ...);
@@ -84,14 +220,32 @@ struct default_pool final: storage<Entity, Component> {
}
}
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.<br/>
* 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<void(const Entity)> construction{};
sigh<void(const Entity)> destruction{};
sigh<void(const Entity)> update{};
sigh<void(const entity_type)> construction{};
sigh<void(const entity_type)> destruction{};
sigh<void(const entity_type)> update{};
};

View File

@@ -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.<br/>
* 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.<br/>
* The function type for a listener is equivalent to:
*
* @code{.cpp}
* void(registry<Entity> &, 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.<br/>
* 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.<br/>
* The function type for a listener is equivalent to:
*
* @code{.cpp}
* void(registry<Entity> &, 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.<br/>
* 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.<br/>
* The function type for a listener is equivalent to:
*
* @code{.cpp}
* void(registry<Entity> &, Entity);
* void(Entity);
* @endcode
*
* Listeners are invoked **before** the component has been removed from the