registry: ::reset doesn't exist anymore, use ::remove and ::clear instead

This commit is contained in:
Michele Caini
2020-01-10 23:29:01 +01:00
parent 210eebc0dc
commit 352e4576fc
7 changed files with 158 additions and 168 deletions

3
TODO
View File

@@ -15,6 +15,7 @@
* use underlying_type as entity type within pools and registry? it would make different registries work together flawlessy
* can we write a bool conv func for entt::entity that silently compares it to null?
* stamp makes sense only when the list of components is empty
* remove registry::reset, add renage-destroy to sparse set and improve range-remove in the registry
* reset... reset everywhere...
* document undocumented parts (entt::overload and a few others)
* any-of rule for views/groups (eg entity has A and any of B/C/D)
- get -> all, exclude -> none

View File

@@ -258,28 +258,26 @@ If the goal is to delete a single component from an entity that owns it, the
registry.remove<position>(entity);
```
When in doubt whether the entity owns the component or not, use the `reset`
When in doubt whether the entity owns the component, use the `remove_if_exists`
member function instead. It behaves similarly to `remove` but it discards the
component if and only if it exists, otherwise it returns safely to the caller:
```cpp
registry.reset<position>(entity);
registry.remove_if_exists<position>(entity);
```
There exist also two other _versions_ of the `reset` member function:
The `clear` member function works similarly and can be used to either:
* If no entity is passed to it, `reset` will remove the given component from
each entity that has it:
* Remove all instances of the given components from the entities that own them:
```cpp
registry.reset<position>();
registry.clear<position>();
```
* If neither the entity nor the component are specified, all the entities still
in use and their components are destroyed:
* Or destroy all entities in a registry at once:
```cpp
registry.reset();
registry.clear();
```
Finally, references to components can be retrieved simply as:
@@ -614,11 +612,9 @@ 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::overload<void(entt::entity)>(&entt::registry::reset<a_type>)>(registry);
registry.on_construct<my_type>().connect<&entt::registry::remove<a_type>>(registry);
```
In this case, to prevent the _wrong_ overload for `reset` being selected,
`entt::overload` is used to help the compiler make the right choice.<br/>
A dependency can also be easily broken as follows:
```cpp
@@ -667,8 +663,8 @@ 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. Either `ctx` or `try_ctx` can be used to
retrieve the newly created instance, while `unset` is meant to literally reset
the variable if needed.
retrieve the newly created instance, while `unset` is meant to clear the
variable if needed.
Example of use:

View File

@@ -77,11 +77,7 @@ class basic_registry {
assign(basic_registry &owner, It first, It last, Args &&... args) {
auto it = this->construct(first, last, std::forward<Args>(args)...);
const auto end = it + (!construction.empty() * std::distance(first, last));
std::for_each(it, end, [this, &owner, &first](decltype(*it) component) {
construction.publish(*(first++), owner, component);
});
std::for_each(it, end, [this, &owner, &first](decltype(*it) component) { construction.publish(*(first++), owner, component); });
return it;
}
@@ -90,6 +86,20 @@ class basic_registry {
this->destroy(entt);
}
template<typename It>
void remove(basic_registry &owner, It first, It last) {
if(std::distance(first, last) == this->size()) {
if(!destruction.empty()) {
std::for_each(first, last, [this, &owner](const auto entt) { destruction.publish(entt, owner); });
}
this->clear();
} else {
// useless this-> used to suppress a warning with clang
std::for_each(first, last, [this, &owner](const auto entt) { this->remove(owner, entt); });
}
}
template<typename... Args>
decltype(auto) replace(basic_registry &owner, const Entity entt, Args &&... args) {
Component component{std::forward<Args>(args)...};
@@ -146,7 +156,7 @@ class basic_registry {
current.destroy(entt);
}
} else {
if(const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...); std::get<0>(cpools).has(entt) && std::get<0>(cpools).index(entt) < current) {
if(const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...); std::get<0>(cpools).has(entt) && (std::get<0>(cpools).index(entt) < current)) {
const auto pos = --current;
(std::get<pool_handler<Owned> &>(cpools).swap(std::get<pool_handler<Owned> &>(cpools).data()[pos], entt), ...);
}
@@ -624,6 +634,61 @@ public:
return assure<Component>().assign(*this, first, last, std::forward<Args>(args)...);
}
/**
* @brief Assigns or replaces the given component for an entity.
*
* Equivalent to the following snippet (pseudocode):
*
* @code{.cpp}
* auto &component = registry.has<Component>(entity) ? registry.replace<Component>(entity, args...) : registry.assign<Component>(entity, args...);
* @endcode
*
* Prefer this function anyway because it has slightly better performance.
*
* @warning
* Attempting to use an invalid entity results in undefined behavior.<br/>
* An assertion will abort the execution at runtime in debug mode in case of
* invalid entity.
*
* @tparam Component Type of component to assign or replace.
* @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.
*/
template<typename Component, typename... Args>
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)...);
}
/**
* @brief Replaces the given component for an entity.
*
* 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.
*
* @warning
* Attempting to use an invalid entity or to replace a component of an
* entity that doesn't own it 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 own an instance of the given
* component.
*
* @tparam Component Type of component to replace.
* @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.
*/
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)...);
}
/**
* @brief Removes the given components from an entity.
*
@@ -656,8 +721,32 @@ public:
template<typename... Component, typename It>
void remove(It first, It last) {
ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }));
// useless this-> used to suppress a warning with clang
std::for_each(first, last, [this](const auto entity) { this->remove<Component...>(entity); });
(assure<Component>().remove(*this, first, last), ...);
}
/**
* @brief Removes the given components from an entity.
*
* Equivalent to the following snippet (pseudocode):
*
* @code{.cpp}
* if(registry.has<Component>(entity)) { registry.remove<Component>(entity) }
* @endcode
*
* Prefer this function anyway because it has slightly better performance.
*
* @warning
* Attempting to use an invalid entity results in undefined behavior.<br/>
* An assertion will abort the execution at runtime in debug mode in case of
* invalid entity.
*
* @tparam Component Types of components to remove.
* @param entity A valid entity identifier.
*/
template<typename... Component>
void remove_if_exists(const entity_type entity) {
ENTT_ASSERT(valid(entity));
([this, entity]() { if(auto &cpool = assure<Component>(); cpool.has(entity)) { cpool.remove(*this, entity); } }(), ...);
}
/**
@@ -780,116 +869,20 @@ public:
}
/**
* @brief Replaces the given component for an entity.
*
* 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.
*
* @warning
* Attempting to use an invalid entity or to replace a component of an
* entity that doesn't own it 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 own an instance of the given
* component.
*
* @tparam Component Type of component to replace.
* @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.
*/
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)...);
}
/**
* @brief Assigns or replaces the given component for an entity.
*
* Equivalent to the following snippet (pseudocode):
*
* @code{.cpp}
* auto &component = registry.has<Component>(entity) ? registry.replace<Component>(entity, args...) : registry.assign<Component>(entity, args...);
* @endcode
*
* Prefer this function anyway because it has slightly better performance.
*
* @warning
* Attempting to use an invalid entity results in undefined behavior.<br/>
* An assertion will abort the execution at runtime in debug mode in case of
* invalid entity.
*
* @tparam Component Type of component to assign or replace.
* @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.
* @brief Clears a whole registry or the pools for the given components.
* @tparam Component Types of components to remove from their entities.
*/
template<typename Component, typename... Args>
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)...);
}
/**
* @brief Resets the given component for an entity.
*
* If the entity has an instance of the component, this function removes the
* component from the entity. Otherwise it does nothing.
*
* @warning
* Attempting to use an invalid entity results in undefined behavior.<br/>
* An assertion will abort the execution at runtime in debug mode in case of
* invalid entity.
*
* @tparam Component Type of component to reset.
* @param entity A valid entity identifier.
*/
template<typename Component>
void reset(const entity_type entity) {
ENTT_ASSERT(valid(entity));
if(auto &cpool = assure<Component>(); cpool.has(entity)) {
cpool.remove(*this, entity);
}
}
/**
* @brief Resets the pool of the given component.
*
* For each entity that has an instance of the given component, the
* component itself is removed and thus destroyed.
*
* @tparam Component Type of component whose pool must be reset.
*/
template<typename Component>
void reset() {
if(auto &cpool = assure<Component>(); cpool.on_destroy().empty()) {
// no group set, otherwise the signal wouldn't be empty
cpool.clear();
template<typename... Component>
void clear() {
if constexpr(sizeof...(Component) == 0) {
// useless this-> used to suppress a warning with clang
each([this](const auto entity) { this->destroy(entity); });
} else {
for(const auto entity: static_cast<const sparse_set<entity_type> &>(cpool)) {
cpool.remove(*this, entity);
}
([this](auto &&cpool) { cpool.remove(*this, cpool.sparse_set<entity_type>::begin(), cpool.sparse_set<entity_type>::end()); }(assure<Component>()), ...);
}
}
/**
* @brief Resets a whole registry.
*
* Destroys all the entities. After a call to `reset`, all the entities
* still in use are recycled with a new version number. In case entity
* identifers are stored around, the `valid` member function can be used
* to know if they are still valid.
*/
void reset() {
// useless this-> used to suppress a warning with clang
each([this](const auto entity) { this->destroy(entity); });
}
/**
* @brief Iterates all the entities that are still in use.
*
@@ -1569,7 +1562,7 @@ public:
}
};
reset();
clear();
entities.clear();
destroyed = null;

View File

@@ -417,7 +417,7 @@ class basic_continuous_loader {
const auto local = ref.second.first;
if(reg->valid(local)) {
reg->template reset<Component>(local);
reg->template remove_if_exists<Component>(local);
}
}
}

View File

@@ -81,7 +81,7 @@ TEST(Observer, AllOf) {
observer.disconnect();
registry.assign_or_replace<int>(entity);
registry.assign_or_replace<char>(entity);
registry.reset<float>(entity);
registry.remove_if_exists<float>(entity);
ASSERT_TRUE(observer.empty());
}

View File

@@ -195,7 +195,7 @@ TEST(Registry, Functionalities) {
ASSERT_EQ(registry.alive(), entt::registry::size_type{2});
ASSERT_FALSE(registry.empty());
ASSERT_NO_THROW(registry.reset());
ASSERT_NO_THROW(registry.clear());
ASSERT_EQ(registry.size(), entt::registry::size_type{3});
ASSERT_EQ(registry.alive(), entt::registry::size_type{0});
@@ -215,14 +215,14 @@ TEST(Registry, Functionalities) {
ASSERT_EQ(registry.get<int>(e3), 3);
ASSERT_EQ(registry.get<char>(e3), 'c');
ASSERT_NO_THROW(registry.reset<int>());
ASSERT_NO_THROW(registry.clear<int>());
ASSERT_EQ(registry.size<int>(), entt::registry::size_type{0});
ASSERT_EQ(registry.size<char>(), entt::registry::size_type{1});
ASSERT_TRUE(registry.empty<int>());
ASSERT_FALSE(registry.empty<char>());
ASSERT_NO_THROW(registry.reset());
ASSERT_NO_THROW(registry.clear());
ASSERT_EQ(registry.size<int>(), entt::registry::size_type{0});
ASSERT_EQ(registry.size<char>(), entt::registry::size_type{0});
@@ -233,8 +233,8 @@ TEST(Registry, Functionalities) {
registry.assign<int>(e4);
ASSERT_NO_THROW(registry.reset<int>(e4));
ASSERT_NO_THROW(registry.reset<int>(e5));
ASSERT_NO_THROW(registry.remove_if_exists<int>(e4));
ASSERT_NO_THROW(registry.remove_if_exists<int>(e5));
ASSERT_EQ(registry.size<int>(), entt::registry::size_type{0});
ASSERT_EQ(registry.size<char>(), entt::registry::size_type{0});
@@ -370,7 +370,7 @@ TEST(Registry, CreateDestroyEntities) {
registry.assign<double>(entity);
}
registry.reset();
registry.clear();
for(int i = 0; i < 7; ++i) {
const auto entity = registry.create();
@@ -378,7 +378,7 @@ TEST(Registry, CreateDestroyEntities) {
if(i == 3) { pre = entity; }
}
registry.reset();
registry.clear();
for(int i = 0; i < 5; ++i) {
const auto entity = registry.create();
@@ -488,10 +488,10 @@ TEST(Registry, Orphans) {
ASSERT_EQ(tot, 1u);
tot = {};
registry.each([&](auto entity) { registry.reset<int>(entity); });
registry.each([&](auto entity) { registry.remove_if_exists<int>(entity); });
registry.orphans([&](auto) { ++tot; });
ASSERT_EQ(tot, 3u);
registry.reset();
registry.clear();
tot = {};
registry.orphans([&](auto) { ++tot; });
@@ -647,7 +647,7 @@ TEST(Registry, PartialOwningGroupInitOnAssign) {
ASSERT_EQ(cnt, 2u);
}
TEST(Registry, CleanViewAfterReset) {
TEST(Registry, CleanViewAfterRemoveAndClear) {
entt::registry registry;
auto view = registry.view<int, char>();
@@ -657,7 +657,7 @@ TEST(Registry, CleanViewAfterReset) {
ASSERT_EQ(view.size(), entt::registry::size_type{1});
registry.reset<char>(entity);
registry.remove<char>(entity);
ASSERT_EQ(view.size(), entt::registry::size_type{0});
@@ -665,7 +665,7 @@ TEST(Registry, CleanViewAfterReset) {
ASSERT_EQ(view.size(), entt::registry::size_type{1});
registry.reset<int>();
registry.clear<int>();
ASSERT_EQ(view.size(), entt::registry::size_type{0});
@@ -673,12 +673,12 @@ TEST(Registry, CleanViewAfterReset) {
ASSERT_EQ(view.size(), entt::registry::size_type{1});
registry.reset();
registry.clear();
ASSERT_EQ(view.size(), entt::registry::size_type{0});
}
TEST(Registry, CleanNonOwningGroupViewAfterReset) {
TEST(Registry, CleanNonOwningGroupViewAfterRemoveAndClear) {
entt::registry registry;
auto group = registry.group<>(entt::get<int, char>);
@@ -688,7 +688,7 @@ TEST(Registry, CleanNonOwningGroupViewAfterReset) {
ASSERT_EQ(group.size(), entt::registry::size_type{1});
registry.reset<char>(entity);
registry.remove<char>(entity);
ASSERT_EQ(group.size(), entt::registry::size_type{0});
@@ -696,7 +696,7 @@ TEST(Registry, CleanNonOwningGroupViewAfterReset) {
ASSERT_EQ(group.size(), entt::registry::size_type{1});
registry.reset<int>();
registry.clear<int>();
ASSERT_EQ(group.size(), entt::registry::size_type{0});
@@ -704,12 +704,12 @@ TEST(Registry, CleanNonOwningGroupViewAfterReset) {
ASSERT_EQ(group.size(), entt::registry::size_type{1});
registry.reset();
registry.clear();
ASSERT_EQ(group.size(), entt::registry::size_type{0});
}
TEST(Registry, CleanFullOwningGroupViewAfterReset) {
TEST(Registry, CleanFullOwningGroupViewAfterRemoveAndClear) {
entt::registry registry;
auto group = registry.group<int, char>();
@@ -719,7 +719,7 @@ TEST(Registry, CleanFullOwningGroupViewAfterReset) {
ASSERT_EQ(group.size(), entt::registry::size_type{1});
registry.reset<char>(entity);
registry.remove<char>(entity);
ASSERT_EQ(group.size(), entt::registry::size_type{0});
@@ -727,7 +727,7 @@ TEST(Registry, CleanFullOwningGroupViewAfterReset) {
ASSERT_EQ(group.size(), entt::registry::size_type{1});
registry.reset<int>();
registry.clear<int>();
ASSERT_EQ(group.size(), entt::registry::size_type{0});
@@ -735,12 +735,12 @@ TEST(Registry, CleanFullOwningGroupViewAfterReset) {
ASSERT_EQ(group.size(), entt::registry::size_type{1});
registry.reset();
registry.clear();
ASSERT_EQ(group.size(), entt::registry::size_type{0});
}
TEST(Registry, CleanPartialOwningGroupViewAfterReset) {
TEST(Registry, CleanPartialOwningGroupViewAfterRemoveAndClear) {
entt::registry registry;
auto group = registry.group<int>(entt::get<char>);
@@ -750,7 +750,7 @@ TEST(Registry, CleanPartialOwningGroupViewAfterReset) {
ASSERT_EQ(group.size(), entt::registry::size_type{1});
registry.reset<char>(entity);
registry.remove<char>(entity);
ASSERT_EQ(group.size(), entt::registry::size_type{0});
@@ -758,7 +758,7 @@ TEST(Registry, CleanPartialOwningGroupViewAfterReset) {
ASSERT_EQ(group.size(), entt::registry::size_type{1});
registry.reset<int>();
registry.clear<int>();
ASSERT_EQ(group.size(), entt::registry::size_type{0});
@@ -766,7 +766,7 @@ TEST(Registry, CleanPartialOwningGroupViewAfterReset) {
ASSERT_EQ(group.size(), entt::registry::size_type{1});
registry.reset();
registry.clear();
ASSERT_EQ(group.size(), entt::registry::size_type{0});
}
@@ -1062,7 +1062,7 @@ TEST(Registry, Signals) {
registry.on_destroy<int>().connect<&listener::decr<int>>(listener);
registry.assign<int>(e0);
registry.reset<int>(e1);
registry.remove_if_exists<int>(e1);
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, e1);
@@ -1070,14 +1070,14 @@ TEST(Registry, Signals) {
registry.on_construct<empty_type>().connect<&listener::incr<empty_type>>(listener);
registry.on_destroy<empty_type>().connect<&listener::decr<empty_type>>(listener);
registry.reset<empty_type>(e1);
registry.remove_if_exists<empty_type>(e1);
registry.assign<empty_type>(e0);
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, e0);
registry.reset<empty_type>();
registry.reset<int>();
registry.clear<empty_type>();
registry.clear<int>();
ASSERT_EQ(listener.counter, 0);
ASSERT_EQ(listener.last, e0);
@@ -1409,7 +1409,7 @@ TEST(Registry, Clone) {
ASSERT_EQ(other.get<char>(e2), '2');
// the remove erased function must be available after cloning
other.reset();
other.clear();
}
TEST(Registry, CloneExclude) {

View File

@@ -102,7 +102,7 @@ TEST(Snapshot, Dump) {
.destroyed(output)
.component<int, char, double, a_component, another_component>(output);
registry.reset();
registry.clear();
ASSERT_FALSE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
@@ -173,7 +173,7 @@ TEST(Snapshot, Partial) {
.destroyed(output)
.component<char, int>(output);
registry.reset();
registry.clear();
ASSERT_FALSE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
@@ -201,7 +201,7 @@ TEST(Snapshot, Partial) {
.entities(output)
.destroyed(output);
registry.reset();
registry.clear();
ASSERT_FALSE(registry.valid(e0));
ASSERT_FALSE(registry.valid(e1));
@@ -247,7 +247,7 @@ TEST(Snapshot, Iterator) {
const auto size = view.size();
registry.snapshot().component<another_component>(output, view.begin(), view.end());
registry.reset();
registry.clear();
registry.loader().component<another_component>(input);
ASSERT_EQ(registry.view<another_component>().size(), size);
@@ -285,7 +285,7 @@ TEST(Snapshot, Continuous) {
src.create();
}
src.reset();
src.clear();
for(int i = 0; i < 5; ++i) {
entity = src.create();
@@ -470,7 +470,7 @@ TEST(Snapshot, Continuous) {
component.bar = entity;
});
dst.reset<a_component>();
dst.clear<a_component>();
a_component_cnt = src.size<a_component>();
src.snapshot()
@@ -491,7 +491,7 @@ TEST(Snapshot, Continuous) {
ASSERT_EQ(dst.size<a_component>(), a_component_cnt);
src.reset<a_component>();
src.clear<a_component>();
a_component_cnt = {};
src.snapshot()
@@ -563,7 +563,7 @@ TEST(Snapshot, SyncDataMembers) {
src.create();
src.create();
src.reset();
src.clear();
auto parent = src.create();
auto child = src.create();