sparse_set/storage: rollback ::policy(pol), incompatible with views
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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), ...);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user