sparse_set/storage/sigh_storage_mixin:

* prepare to support non-movable, non-copyable types
* improve destroying entities
This commit is contained in:
Michele Caini
2022-07-29 09:26:28 +02:00
parent b3fde98020
commit e0ee35da61
3 changed files with 50 additions and 59 deletions

View File

@@ -27,26 +27,17 @@ class sigh_storage_mixin final: public Type {
using sigh_type = sigh<void(basic_registry<typename Type::entity_type> &, const typename Type::entity_type), typename Type::allocator_type>;
using basic_iterator = typename Type::basic_iterator;
template<typename Func>
void notify_destruction(basic_iterator first, basic_iterator last, Func func) {
void pop(basic_iterator first, basic_iterator last) override {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
for(; first != last; ++first) {
const auto entt = *first;
destruction.publish(*owner, entt);
const auto it = Type::find(entt);
func(it, it + 1u);
Type::pop(it, it + 1u);
}
}
void swap_and_pop(basic_iterator first, basic_iterator last) final {
notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::swap_and_pop(args...); });
}
void in_place_pop(basic_iterator first, basic_iterator last) final {
notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::in_place_pop(args...); });
}
basic_iterator try_emplace(const typename Type::entity_type entt, const bool force_back, const void *value) final {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
Type::try_emplace(entt, force_back, value);

View File

@@ -234,31 +234,43 @@ protected:
using basic_iterator = internal::sparse_set_iterator<packed_container_type>;
/**
* @brief Erases entities from a sparse set.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @brief Erases an entity from a sparse set.
* @param it An iterator to the element to pop.
*/
virtual void swap_and_pop(basic_iterator first, basic_iterator last) {
for(; first != last; ++first) {
sparse_ref(packed.back()) = entity_traits::combine(static_cast<typename entity_traits::entity_type>(first.index()), entity_traits::to_integral(packed.back()));
const auto entt = std::exchange(packed[first.index()], packed.back());
// unnecessary but it helps to detect nasty bugs
ENTT_ASSERT((packed.back() = null, true), "");
// lazy self-assignment guard
sparse_ref(entt) = null;
packed.pop_back();
}
void swap_and_pop(const basic_iterator it) {
sparse_ref(packed.back()) = entity_traits::combine(static_cast<typename entity_traits::entity_type>(it.index()), entity_traits::to_integral(packed.back()));
const auto entt = std::exchange(packed[it.index()], packed.back());
// unnecessary but it helps to detect nasty bugs
ENTT_ASSERT((packed.back() = null, true), "");
// lazy self-assignment guard
sparse_ref(entt) = null;
packed.pop_back();
}
/**
* @brief Erases an entity from a sparse set.
* @param it An iterator to the element to pop.
*/
void in_place_pop(const basic_iterator it) {
sparse_ref(*it) = null;
packed[it.index()] = std::exchange(free_list, entity_traits::combine(static_cast<typename entity_traits::entity_type>(it.index()), entity_traits::reserved));
}
protected:
/**
* @brief Erases entities from a sparse set.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
*/
virtual void in_place_pop(basic_iterator first, basic_iterator last) {
for(; first != last; ++first) {
sparse_ref(*first) = null;
packed[first.index()] = std::exchange(free_list, entity_traits::combine(static_cast<typename entity_traits::entity_type>(first.index()), entity_traits::reserved));
virtual void pop(basic_iterator first, basic_iterator last) {
if(mode == deletion_policy::in_place) {
for(; first != last; ++first) {
in_place_pop(first);
}
} else {
for(; first != last; ++first) {
swap_and_pop(first);
}
}
}
@@ -710,7 +722,7 @@ public:
*/
void erase(const entity_type entt) {
const auto it = --(end() - index(entt));
(mode == deletion_policy::in_place) ? in_place_pop(it, it + 1u) : swap_and_pop(it, it + 1u);
pop(it, it + 1u);
}
/**
@@ -725,7 +737,7 @@ public:
template<typename It>
void erase(It first, It last) {
if constexpr(std::is_same_v<It, basic_iterator>) {
(mode == deletion_policy::in_place) ? in_place_pop(first, last) : swap_and_pop(first, last);
pop(first, last);
} else {
for(; first != last; ++first) {
erase(*first);
@@ -922,12 +934,12 @@ public:
/*! @brief Clears a sparse set. */
void clear() {
if(const auto last = end(); free_list == null) {
in_place_pop(begin(), last);
pop(begin(), last);
} else {
for(auto &&entity: *this) {
// tombstone filter on itself
if(const auto it = find(entity); it != last) {
in_place_pop(it, it + 1u);
pop(it, it + 1u);
}
}
}

View File

@@ -275,12 +275,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
entt::uninitialized_construct_using_allocator(to_address(elem), packed.second(), std::forward<Args>(args)...);
}
ENTT_CATCH {
if constexpr(comp_traits::in_place_delete) {
base_type::in_place_pop(it, it + 1u);
} else {
base_type::swap_and_pop(it, it + 1u);
}
base_type::pop(it, it + 1u);
ENTT_THROW;
}
@@ -330,29 +325,22 @@ protected:
using basic_iterator = typename underlying_type::basic_iterator;
/**
* @brief Erases elements from a storage.
* @param first An iterator to the first element to erase.
* @param last An iterator past the last element to erase.
* @brief Erases entities from a sparse set.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
*/
void swap_and_pop(basic_iterator first, basic_iterator last) override {
void pop(basic_iterator first, basic_iterator last) override {
for(; first != last; ++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));
std::destroy_at(std::addressof(elem));
base_type::swap_and_pop(first, first + 1u);
}
}
/**
* @brief Erases elements from a storage.
* @param first An iterator to the first element to erase.
* @param last An iterator past the last element to erase.
*/
void in_place_pop(basic_iterator first, basic_iterator last) override {
for(; first != last; ++first) {
base_type::in_place_pop(first, first + 1u);
std::destroy_at(std::addressof(element_at(static_cast<size_type>(first.index()))));
if constexpr(comp_traits::in_place_delete) {
base_type::in_place_pop(first);
std::destroy_at(std::addressof(element_at(static_cast<size_type>(first.index()))));
} else {
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));
std::destroy_at(std::addressof(elem));
base_type::swap_and_pop(first);
}
}
}