registry: in-place ::replace, the signal no longer receives an extra argument (close #406)
This commit is contained in:
@@ -228,16 +228,10 @@ This function is overloaded and accepts also a couple of iterators in order to:
|
||||
```
|
||||
|
||||
If an entity already has the given component, the `replace` member function
|
||||
template can be used to replace it:
|
||||
template can be used to edit it in-place:
|
||||
|
||||
```cpp
|
||||
registry.replace<position>(entity, 0., 0.);
|
||||
|
||||
// ...
|
||||
|
||||
auto &velocity = registry.replace<velocity>(entity);
|
||||
vel.dx = 0.;
|
||||
vel.dy = 0.;
|
||||
registry.replace<position>(entity, [](auto &pos) { pos.x = pos.y = 0.; });
|
||||
```
|
||||
|
||||
When it's unknown whether an entity already owns an instance of a component,
|
||||
@@ -245,19 +239,13 @@ When it's unknown whether an entity already owns an instance of a component,
|
||||
|
||||
```cpp
|
||||
registry.assign_or_replace<position>(entity, 0., 0.);
|
||||
|
||||
// ...
|
||||
|
||||
auto &velocity = registry.assign_or_replace<velocity>(entity);
|
||||
vel.dx = 0.;
|
||||
vel.dy = 0.;
|
||||
```
|
||||
|
||||
This is a slightly faster alternative for the following snippet:
|
||||
|
||||
```cpp
|
||||
if(registry.has<comp>(entity)) {
|
||||
registry.replace<velocity>(entity, 0., 0.);
|
||||
registry.replace<velocity>(entity, [](auto &vel) { vel.dx = vel.dy = 0.; });
|
||||
} else {
|
||||
registry.assign<velocity>(entity, 0., 0.);
|
||||
}
|
||||
@@ -352,36 +340,24 @@ To be notified when components are destroyed, use the `on_destroy` member
|
||||
function instead. Finally, the `on_replace` member function will return a sink
|
||||
to which to connect listeners to observe changes.
|
||||
|
||||
The function type of a listener for the construction and destruction signals
|
||||
should be equivalent to the following:
|
||||
The function type of a listener should be equivalent to the following:
|
||||
|
||||
```cpp
|
||||
void(entt::registry &, entt::entity);
|
||||
```
|
||||
|
||||
In both cases, listeners are provided with the registry that triggered the
|
||||
notification and the entity affected by the change.<br/>
|
||||
The function type of a listener that observes changes to components is slightly
|
||||
different instead:
|
||||
|
||||
```cpp
|
||||
void(entt::registry &, entt::entity, Component &);
|
||||
```
|
||||
|
||||
In this case, `Component` is intuitively the type of component of interest. The
|
||||
extra argument is required because the registry cannot store and therefore
|
||||
return both the instances of the given type for an 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 signal are invoked **after** components have
|
||||
* Listeners for the construction signals are invoked **after** components have
|
||||
been assigned to entities.
|
||||
|
||||
* Listeners designed to observe changes are invoked **before** components have
|
||||
been replaced and therefore before newly created instances have been assigned
|
||||
to entities.
|
||||
* Listeners designed to observe changes are invoked **after** components have
|
||||
been updated.
|
||||
|
||||
* Listeners for the destruction signal are invoked **before** components have
|
||||
* Listeners for the destruction signals are invoked **before** components have
|
||||
been removed from entities.
|
||||
|
||||
There are also some limitations on what a listener can and cannot do:
|
||||
@@ -390,7 +366,7 @@ There are also some limitations on what a listener can and cannot do:
|
||||
listener should be avoided. It can lead to undefined behavior in some cases.
|
||||
|
||||
* Removing the component from within the body of a listener that observes the
|
||||
construction or replacement of instances of a given type isn't allowed.
|
||||
construction or update of instances of a given type isn't allowed.
|
||||
|
||||
* Assigning and removing components from within the body of a listener that
|
||||
observes the destruction of instances of a given type should be avoided. It
|
||||
@@ -646,8 +622,9 @@ A dependency can also be easily broken as follows:
|
||||
registry.on_construct<my_type>().disconnect<&entt::registry::assign_or_replace<a_type>>();
|
||||
```
|
||||
|
||||
There are many other types of dependencies. In general, all functions that
|
||||
accept an entity as the first argument are good candidates for this purpose.
|
||||
There are many other types of dependencies. In general, most of the functions
|
||||
that accept an entity as the first argument are good candidates for this
|
||||
purpose.
|
||||
|
||||
### Tags
|
||||
|
||||
|
||||
@@ -98,17 +98,16 @@ class basic_registry {
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
auto replace(basic_registry &owner, const Entity entt, Args &&... args) -> decltype(this->get(entt)) {
|
||||
Component component{std::forward<Args>(args)...};
|
||||
update.publish(owner, entt, component);
|
||||
return (this->get(entt) = std::move(component));
|
||||
template<typename... Func>
|
||||
void replace(basic_registry &owner, const Entity entt, Func &&... func) {
|
||||
(std::forward<Func>(func)(this->get(entt)), ...);
|
||||
update.publish(owner, entt);
|
||||
}
|
||||
|
||||
private:
|
||||
sigh<void(basic_registry &, const Entity)> construction{};
|
||||
sigh<void(basic_registry &, const Entity)> destruction{};
|
||||
sigh<void(basic_registry &, const Entity, decltype(std::declval<storage<Entity, Component>>().get({})))> update{};
|
||||
sigh<void(basic_registry &, const Entity)> update{};
|
||||
};
|
||||
|
||||
struct pool_data {
|
||||
@@ -717,12 +716,39 @@ public:
|
||||
decltype(auto) assign_or_replace(const entity_type entity, Args &&... args) {
|
||||
ENTT_ASSERT(valid(entity));
|
||||
auto &cpool = assure<Component>();
|
||||
return cpool.has(entity) ? cpool.replace(*this, entity, std::forward<Args>(args)...) : cpool.assign(*this, entity, std::forward<Args>(args)...);
|
||||
|
||||
return cpool.has(entity)
|
||||
? (cpool.replace(*this, entity, [args = std::forward_as_tuple(std::forward<Args>(args)...)](auto &&component) { component = std::make_from_tuple<Component>(std::move(args)); }), cpool.get(entity))
|
||||
: cpool.assign(*this, entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Replaces the given component for an entity.
|
||||
*
|
||||
* The signature of the functions should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(Component &);
|
||||
* @endcode
|
||||
*
|
||||
* Temporary objects are returned for empty types though. Capture them by
|
||||
* copy or by const reference if needed.
|
||||
*
|
||||
* @tparam Component Type of component to replace.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param entity A valid entity identifier.
|
||||
* @param func Valid function objects.
|
||||
*/
|
||||
template<typename Component, typename... Func>
|
||||
auto replace(const entity_type entity, Func &&... func)
|
||||
-> decltype(std::enable_if_t<sizeof...(Func) != 0>(), (func(std::declval<Component &>()), ...), void()) {
|
||||
ENTT_ASSERT(valid(entity));
|
||||
assure<Component>().replace(*this, entity, std::forward<Func>(func)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copybrief replace
|
||||
*
|
||||
* A new instance of the given component is created and initialized with the
|
||||
* arguments provided (the component must have a proper constructor or be of
|
||||
* aggregate type). Then the component is assigned to the given entity.
|
||||
@@ -738,12 +764,17 @@ public:
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param entity A valid entity identifier.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return A reference to the newly created component.
|
||||
* @return A reference to the component being replaced.
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
decltype(auto) replace(const entity_type entity, Args &&... args) {
|
||||
ENTT_ASSERT(valid(entity));
|
||||
return assure<Component>().replace(*this, entity, std::forward<Args>(args)...);
|
||||
[[deprecated("use in-place replace instead")]]
|
||||
auto replace(const entity_type entity, Args &&... args)
|
||||
-> decltype(Component{std::forward<Args>(args)...}, assure<Component>().get(entity)) {
|
||||
replace<Component>(entity, [args = std::forward_as_tuple(std::forward<Args>(args)...)](auto &&component) {
|
||||
component = std::make_from_tuple<Component>(std::move(args));
|
||||
});
|
||||
|
||||
return assure<Component>().get(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -177,7 +177,7 @@ TEST(Registry, Functionalities) {
|
||||
ASSERT_NE(®istry.get<int>(e0), ®istry.get<int>(e2));
|
||||
ASSERT_NE(®istry.get<char>(e0), ®istry.get<char>(e2));
|
||||
|
||||
ASSERT_NO_THROW(registry.replace<int>(e0, 0));
|
||||
ASSERT_NO_THROW(registry.replace<int>(e0, [](auto &instance) { instance = 0; }));
|
||||
ASSERT_EQ(registry.get<int>(e0), 0);
|
||||
|
||||
ASSERT_NO_THROW(registry.assign_or_replace<int>(e0, 1));
|
||||
@@ -1431,8 +1431,8 @@ TEST(Registry, Stamp) {
|
||||
ASSERT_EQ(registry.get<int>(entity), 3);
|
||||
ASSERT_EQ(registry.get<char>(entity), 'c');
|
||||
|
||||
registry.replace<int>(prototype, 42);
|
||||
registry.replace<char>(prototype, 'a');
|
||||
registry.replace<int>(prototype, [](auto &instance) { instance = 42; });
|
||||
registry.replace<char>(prototype, [](auto &instance) { instance = 'a'; });
|
||||
registry.stamp(entity, registry, prototype);
|
||||
|
||||
ASSERT_EQ(registry.get<int>(entity), 42);
|
||||
@@ -1454,7 +1454,7 @@ TEST(Registry, StampExclude) {
|
||||
ASSERT_FALSE(registry.has<char>(entity));
|
||||
ASSERT_EQ(registry.get<int>(entity), 3);
|
||||
|
||||
registry.replace<int>(prototype, 42);
|
||||
registry.replace<int>(prototype, [](auto &instance) { instance = 42; });
|
||||
registry.stamp(entity, registry, prototype, entt::exclude<int>);
|
||||
|
||||
ASSERT_TRUE((registry.has<int, char, empty_type>(entity)));
|
||||
|
||||
Reference in New Issue
Block a user