storage: fix cross range-erase can break when using built-in iterators (close #914)

This commit is contained in:
Michele Caini
2022-08-02 09:10:10 +02:00
parent f8de85b961
commit c09661536b
2 changed files with 66 additions and 2 deletions

View File

@@ -333,9 +333,11 @@ protected:
*/
void swap_and_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override {
for(; first != last; ++first) {
// cannot use first::index() because it would break with cross iterators
const auto pos = index(*first);
auto &elem = element_at(base_type::size() - 1u);
// destroying on exit allows reentrant destructors
[[maybe_unused]] auto unused = std::exchange(element_at(static_cast<size_type>(first.index())), std::move(elem));
[[maybe_unused]] auto unused = std::exchange(element_at(pos), std::move(elem));
std::destroy_at(std::addressof(elem));
base_type::swap_and_pop(first, first + 1u);
}
@@ -348,8 +350,10 @@ protected:
*/
void in_place_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override {
for(; first != last; ++first) {
// cannot use first::index() because it would break with cross iterators
const auto pos = index(*first);
base_type::in_place_pop(first, first + 1u);
std::destroy_at(std::addressof(element_at(static_cast<size_type>(first.index()))));
std::destroy_at(std::addressof(element_at(pos)));
}
}

View File

@@ -345,6 +345,21 @@ TEST(Storage, Erase) {
ASSERT_EQ(*pool.begin(), 1);
}
TEST(Storage, CrossErase) {
entt::sparse_set set;
entt::storage<int> pool;
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
pool.emplace(entities[0u], 3);
pool.emplace(entities[1u], 42);
set.emplace(entities[1u]);
pool.erase(set.begin(), set.end());
ASSERT_TRUE(pool.contains(entities[0u]));
ASSERT_FALSE(pool.contains(entities[1u]));
ASSERT_EQ(pool.raw()[0u][0u], 3);
}
TEST(Storage, StableErase) {
entt::storage<stable_type> pool;
entt::entity entities[3u]{entt::entity{3}, entt::entity{42}, entt::entity{9}};
@@ -439,6 +454,21 @@ TEST(Storage, StableErase) {
ASSERT_EQ(pool.get(entities[2u]).value, 1);
}
TEST(Storage, CrossStableErase) {
entt::sparse_set set;
entt::storage<stable_type> pool;
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
pool.emplace(entities[0u], 3);
pool.emplace(entities[1u], 42);
set.emplace(entities[1u]);
pool.erase(set.begin(), set.end());
ASSERT_TRUE(pool.contains(entities[0u]));
ASSERT_FALSE(pool.contains(entities[1u]));
ASSERT_EQ(pool.raw()[0u][0u].value, 3);
}
TEST(Storage, Remove) {
entt::storage<int> pool;
entt::entity entities[3u]{entt::entity{3}, entt::entity{42}, entt::entity{9}};
@@ -473,6 +503,21 @@ TEST(Storage, Remove) {
ASSERT_EQ(*pool.begin(), 1);
}
TEST(Storage, CrossRemove) {
entt::sparse_set set;
entt::storage<int> pool;
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
pool.emplace(entities[0u], 3);
pool.emplace(entities[1u], 42);
set.emplace(entities[1u]);
pool.remove(set.begin(), set.end());
ASSERT_TRUE(pool.contains(entities[0u]));
ASSERT_FALSE(pool.contains(entities[1u]));
ASSERT_EQ(pool.raw()[0u][0u], 3);
}
TEST(Storage, StableRemove) {
entt::storage<stable_type> pool;
entt::entity entities[3u]{entt::entity{3}, entt::entity{42}, entt::entity{9}};
@@ -570,6 +615,21 @@ TEST(Storage, StableRemove) {
ASSERT_EQ(pool.get(entities[2u]).value, 1);
}
TEST(Storage, CrossStableRemove) {
entt::sparse_set set;
entt::storage<stable_type> pool;
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
pool.emplace(entities[0u], 3);
pool.emplace(entities[1u], 42);
set.emplace(entities[1u]);
pool.remove(set.begin(), set.end());
ASSERT_TRUE(pool.contains(entities[0u]));
ASSERT_FALSE(pool.contains(entities[1u]));
ASSERT_EQ(pool.raw()[0u][0u].value, 3);
}
TEST(Storage, TypeFromBase) {
entt::storage<int> pool;
entt::sparse_set &base = pool;