sparse_set/storage: rollback ::policy(pol), incompatible with views

This commit is contained in:
Michele Caini
2022-01-09 16:15:47 +01:00
parent 48dfac2588
commit 9fb1970ac0
5 changed files with 9 additions and 116 deletions

View File

@@ -1004,18 +1004,11 @@ within the storage, making direct access almost impossible (be it via pointer or
index).
However, the underlying model with its independent pools helps introduce storage
with different deletion policies, so that users can best choose type by
type.<br/>
In particular, the library offers out of the box support for in-place deletion,
thus offering storage with completely stable pointers. There are two options to
achieve it:
* A compile-time method, which is to specialize the `component_traits` class.
* A runtime method, which is to set the deletion policy for a pool manually.
Also, there is no problem changing the deletion policy at runtime, even when a
compile-time policy exists.<br/>
The compile-time definition common to all components is the following:
with different deletion policies. In particular, the library offers out of the
box support for in-place deletion, thus offering storage with completely stable
pointers.<br/>
This is done by specializing the `component_traits` class. The compile-time
definition common to all components is the following:
```cpp
struct basic_component_traits {
@@ -1040,18 +1033,11 @@ struct entt::component_traits<position>: basic_component_traits {
This will ensure in-place deletion for the `position` component without further
user intervention.<br/>
Changing the deletion policy at runtime is instead reduced to a direct call on
the pool itself:
```cpp
registry.storage<position>().policy(entt::deletion_policy::in_place);
```
Views and groups adapt accordingly when they detect a storage with a different
deletion policy than the default. No specific action is required from the user
once in-place deletion is enabled. In particular:
* Groups are incompatible with stable storage and will trigger a runtime error.
* Groups are incompatible with stable storage and will even refuse to compile.
* Multi type views are completely transparent to storage policies.
* Single type views for stable storage types offer the same interface of multi
type views. For example, only `size_hint` is available.

View File

@@ -182,12 +182,13 @@ class basic_registry {
template<typename... Exclude, typename... Get, typename... Owned>
struct group_handler<exclude_t<Exclude...>, get_t<Get...>, Owned...> {
// nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here
static_assert(!std::disjunction_v<std::bool_constant<component_traits<Owned>::in_place_delete>...>, "Groups do not support in-place delete");
std::conditional_t<sizeof...(Owned) == 0, basic_common_type, std::size_t> current{};
template<typename Component>
void maybe_valid_if(basic_registry &owner, const Entity entt) {
[[maybe_unused]] const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...);
ENTT_ASSERT(((std::get<storage_type<Owned> &>(cpools).policy() == deletion_policy::swap_and_pop) && ...), "Groups do not support in-place delete");
const auto is_valid = ((std::is_same_v<Component, Owned> || std::get<storage_type<Owned> &>(cpools).contains(entt)) && ...)
&& ((std::is_same_v<Component, Get> || owner.assure<Get>().contains(entt)) && ...)
@@ -209,10 +210,7 @@ class basic_registry {
if constexpr(sizeof...(Owned) == 0) {
current.remove(entt);
} else {
const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...);
ENTT_ASSERT(((std::get<storage_type<Owned> &>(cpools).policy() == deletion_policy::swap_and_pop) && ...), "Groups do not support in-place delete");
if(std::get<0>(cpools).contains(entt) && (std::get<0>(cpools).index(entt) < current)) {
if(const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...); std::get<0>(cpools).contains(entt) && (std::get<0>(cpools).index(entt) < current)) {
const auto pos = --current;
(std::get<storage_type<Owned> &>(cpools).swap_elements(std::get<storage_type<Owned> &>(cpools).data()[pos], entt), ...);
}

View File

@@ -398,18 +398,6 @@ public:
return mode;
}
/**
* @brief Sets the deletion policy of a sparse set.
* @param pol The deletion policy of the sparse set.
*/
void policy(const deletion_policy pol) ENTT_NOEXCEPT {
if(pol != mode && mode == deletion_policy::in_place) {
compact();
}
mode = pol;
}
/**
* @brief Returns the next slot available for insertion.
* @return The next slot available for insertion.

View File

@@ -753,42 +753,6 @@ TEST(SparseSet, Compact) {
ASSERT_TRUE(set.empty());
}
TEST(SparseSet, Policy) {
entt::sparse_set set{entt::deletion_policy::in_place};
entt::entity entities[3u]{entt::entity{0}, entt::entity{1}, entt::entity{2}};
set.insert(std::begin(entities), std::end(entities));
set.erase(entities[1u]);
ASSERT_EQ(set.size(), 3u);
ASSERT_EQ(set.index(entities[0u]), 0u);
ASSERT_FALSE(set.contains(entities[1u]));
ASSERT_EQ(set.index(entities[2u]), 2u);
set.policy(entt::deletion_policy::swap_and_pop);
ASSERT_EQ(set.size(), 2u);
ASSERT_EQ(set.index(entities[0u]), 0u);
ASSERT_EQ(set.index(entities[2u]), 1u);
set.erase(entities[0u]);
ASSERT_EQ(set.size(), 1u);
ASSERT_FALSE(set.contains(entities[0u]));
ASSERT_EQ(set.index(entities[2u]), 0u);
set.policy(entt::deletion_policy::in_place);
set.erase(entities[2u]);
ASSERT_EQ(set.size(), 1u);
ASSERT_FALSE(set.contains(entities[2u]));
set.policy(entt::deletion_policy::swap_and_pop);
ASSERT_EQ(set.size(), 0u);
ASSERT_TRUE(set.empty());
}
TEST(SparseSet, SwapEntity) {
using traits_type = entt::entt_traits<entt::entity>;

View File

@@ -772,49 +772,6 @@ TEST(Storage, Compact) {
ASSERT_TRUE(pool.empty());
}
TEST(Storage, Policy) {
entt::storage<stable_type> pool{};
entt::entity entities[3u]{entt::entity{0}, entt::entity{1}, entt::entity{2}};
pool.emplace(entities[0u], stable_type{0});
pool.emplace(entities[1u], stable_type{1});
pool.emplace(entities[2u], stable_type{2});
pool.erase(entities[1u]);
ASSERT_EQ(pool.size(), 3u);
ASSERT_EQ(pool.index(entities[0u]), 0u);
ASSERT_FALSE(pool.contains(entities[1u]));
ASSERT_EQ(pool.index(entities[2u]), 2u);
ASSERT_EQ(pool.get(entities[0u]).value, 0);
ASSERT_EQ(pool.get(entities[2u]).value, 2);
pool.policy(entt::deletion_policy::swap_and_pop);
ASSERT_EQ(pool.size(), 2u);
ASSERT_EQ(pool.index(entities[0u]), 0u);
ASSERT_EQ(pool.index(entities[2u]), 1u);
ASSERT_EQ(pool.get(entities[0u]).value, 0);
ASSERT_EQ(pool.get(entities[2u]).value, 2);
pool.erase(entities[0u]);
ASSERT_EQ(pool.size(), 1u);
ASSERT_FALSE(pool.contains(entities[0u]));
ASSERT_EQ(pool.index(entities[2u]), 0u);
ASSERT_EQ(pool.get(entities[2u]).value, 2);
pool.policy(entt::deletion_policy::in_place);
pool.erase(entities[2u]);
ASSERT_EQ(pool.size(), 1u);
ASSERT_FALSE(pool.contains(entities[2u]));
pool.policy(entt::deletion_policy::swap_and_pop);
ASSERT_EQ(pool.size(), 0u);
ASSERT_TRUE(pool.empty());
}
TEST(Storage, ShrinkToFit) {
entt::storage<int> pool;