storage: uniform interface to simplify mixin implementation

This commit is contained in:
Michele Caini
2023-02-20 09:34:11 +01:00
parent 47ea16f17c
commit 6e4946b682
6 changed files with 78 additions and 94 deletions

1
TODO
View File

@@ -15,7 +15,6 @@ TODO (high prio):
* check natvis files (periodically :)
* remove the static storage from the const assure in the registry
* pop_if to improve further destroying entities (drastically)
* improve sigh mixin for entity storage (split basic + entt/entt spec)
* doc: exclude only views, storage entity (and diff between iterator and each based iteration), bump entities, signals on entity creation/destruction
* registry::destroy avoid pack if entity storage iterators already
* registry: replace destroy with a drop-all method that doesn't care about validity

View File

@@ -192,18 +192,46 @@ public:
}
/**
* @brief Assigns entities to a storage.
* @tparam Args Types of arguments to use to construct the object.
* @param entt A valid identifier.
* @param args Parameters to use to initialize the object.
* @return A reference to the newly created object.
* @brief Emplace elements into a storage.
*
* The behavior of this operation depends on the underlying storage type
* (for example, components vs entities).<br/>
* Refer to the specific documentation for more details.
*
* @return A return value as returned by the underlying storage.
*/
auto emplace() {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
const auto entt = Type::emplace();
construction.publish(*owner, entt);
return entt;
}
/**
* @brief Emplace elements into a storage.
*
* The behavior of this operation depends on the underlying storage type
* (for example, components vs entities).<br/>
* Refer to the specific documentation for more details.
*
* @tparam Args Types of arguments to forward to the underlying storage.
* @param hint A valid identifier.
* @param args Parameters to forward to the underlying storage.
* @return A return value as returned by the underlying storage.
*/
template<typename... Args>
decltype(auto) emplace(const entity_type entt, Args &&...args) {
decltype(auto) emplace(const entity_type hint, Args &&...args) {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
Type::emplace(entt, std::forward<Args>(args)...);
construction.publish(*owner, entt);
return this->get(entt);
if constexpr(std::is_same_v<typename Type::value_type, typename Type::entity_type>) {
const auto entt = Type::emplace(hint, std::forward<Args>(args)...);
construction.publish(*owner, entt);
return entt;
} else {
Type::emplace(hint, std::forward<Args>(args)...);
construction.publish(*owner, hint);
return this->get(hint);
}
}
/**
@@ -222,14 +250,17 @@ public:
}
/**
* @brief Assigns entities to a storage.
* @tparam It Type of input iterator.
* @tparam Args Types of arguments to use to construct the objects assigned
* to the entities.
* @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.
* @param args Parameters to use to initialize the objects assigned to the
* entities.
* @brief Emplace elements into a storage.
*
* The behavior of this operation depends on the underlying storage type
* (for example, components vs entities).<br/>
* Refer to the specific documentation for more details.
*
* @tparam It Iterator type (as required by the underlying storage type).
* @tparam Args Types of arguments to forward to the underlying storage.
* @param first An iterator to the first element of the range.
* @param last An iterator past the last element of the range.
* @param args Parameters to use to forward to the underlying storage.
*/
template<typename It, typename... Args>
void insert(It first, It last, Args &&...args) {
@@ -244,52 +275,6 @@ public:
}
}
/**
* @brief Creates a new identifier or recycles a destroyed one.
* @return A valid identifier.
*/
entity_type spawn() {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
const auto entt = Type::spawn();
construction.publish(*owner, entt);
return entt;
}
/**
* @brief Creates a new identifier or recycles a destroyed one.
*
* If the requested identifier isn't in use, the suggested one is used.
* Otherwise, a new identifier is returned.
*
* @param hint Required identifier.
* @return A valid identifier.
*/
entity_type spawn(const entity_type hint) {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
const auto entt = Type::spawn(hint);
construction.publish(*owner, entt);
return entt;
}
/**
* @brief Assigns each element in a range an identifier.
* @tparam It Type of mutable forward iterator.
* @param first An iterator to the first element of the range to generate.
* @param last An iterator past the last element of the range to generate.
*/
template<typename It>
void spawn(It first, It last) {
Type::spawn(first, last);
if(!construction.empty()) {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
for(; first != last; ++first) {
construction.publish(*owner, *first);
}
}
}
/**
* @brief Forwards variables to derived classes, if any.
* @param value A variable wrapped in an opaque container.

View File

@@ -588,7 +588,7 @@ public:
* @return A valid identifier.
*/
[[nodiscard]] entity_type create() {
return shortcut->spawn();
return shortcut->emplace();
}
/**
@@ -601,7 +601,7 @@ public:
* @return A valid identifier.
*/
[[nodiscard]] entity_type create(const entity_type hint) {
return shortcut->spawn(hint);
return shortcut->emplace(hint);
}
/**
@@ -615,7 +615,7 @@ public:
*/
template<typename It>
void create(It first, It last) {
shortcut->spawn(std::move(first), std::move(last));
shortcut->insert(std::move(first), std::move(last));
}
/**

View File

@@ -964,7 +964,7 @@ protected:
* @return Iterator pointing to the emplaced element.
*/
underlying_iterator try_emplace(const Entity hint, const bool, const void *) override {
return base_type::find(spawn(hint));
return base_type::find(emplace(hint));
}
public:
@@ -1068,7 +1068,7 @@ public:
* @brief Creates a new identifier or recycles a destroyed one.
* @return A valid identifier.
*/
entity_type spawn() {
entity_type emplace() {
if(length == base_type::size()) {
return *base_type::try_emplace(entity_at(length++), true);
}
@@ -1085,9 +1085,9 @@ public:
* @param hint Required identifier.
* @return A valid identifier.
*/
entity_type spawn(const entity_type hint) {
entity_type emplace(const entity_type hint) {
if(hint == null || hint == tombstone) {
return spawn();
return emplace();
} else if(const auto curr = local_traits_type::construct(local_traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone) {
const auto pos = static_cast<size_type>(local_traits_type::to_entity(hint));
@@ -1097,7 +1097,7 @@ public:
base_type::swap_at(pos, length++);
} else if(const auto idx = base_type::index(curr); idx < length) {
return spawn();
return emplace();
} else {
base_type::swap_at(idx, length++);
}
@@ -1114,7 +1114,7 @@ public:
* @param last An iterator past the last element of the range to generate.
*/
template<typename It>
void spawn(It first, It last) {
void insert(It first, It last) {
for(const auto sz = base_type::size(); first != last && length != sz; ++first, ++length) {
*first = base_type::operator[](length);
}

View File

@@ -510,11 +510,11 @@ TEST(SighMixin, StorageEntity) {
ASSERT_EQ(on_construct.value, 3);
ASSERT_EQ(on_destroy.value, 3);
pool.spawn();
pool.spawn(entt::entity{0});
pool.emplace();
pool.emplace(entt::entity{0});
entt::entity entities[1u]{};
pool.spawn(entities, entities + 1u);
pool.insert(entities, entities + 1u);
ASSERT_EQ(on_construct.value, 6);
ASSERT_EQ(on_destroy.value, 3);

View File

@@ -45,7 +45,7 @@ TEST(StorageEntity, Functionalities) {
ENTT_DEBUG_TEST(StorageEntityDeathTest, Get) {
entt::storage<entt::entity> pool;
pool.spawn(entt::entity{99});
pool.emplace(entt::entity{99});
ASSERT_DEATH(pool.get(entt::entity{3}), "");
ASSERT_DEATH([[maybe_unused]] auto tup = pool.get_as_tuple(entt::entity{3}), "");
@@ -187,18 +187,18 @@ TEST(StorageEntity, Push) {
ASSERT_EQ(*pool.push(entt::null), entt::entity{7});
}
TEST(StorageEntity, Spawn) {
TEST(StorageEntity, Emplace) {
using traits_type = entt::entt_traits<entt::entity>;
entt::storage<entt::entity> pool;
entt::entity entities[2u]{};
ASSERT_EQ(pool.spawn(), entt::entity{0});
ASSERT_EQ(pool.spawn(entt::null), entt::entity{1});
ASSERT_EQ(pool.spawn(entt::tombstone), entt::entity{2});
ASSERT_EQ(pool.spawn(entt::entity{0}), entt::entity{3});
ASSERT_EQ(pool.spawn(traits_type::construct(1, 1)), entt::entity{4});
ASSERT_EQ(pool.spawn(traits_type::construct(6, 3)), traits_type::construct(6, 3));
ASSERT_EQ(pool.emplace(), entt::entity{0});
ASSERT_EQ(pool.emplace(entt::null), entt::entity{1});
ASSERT_EQ(pool.emplace(entt::tombstone), entt::entity{2});
ASSERT_EQ(pool.emplace(entt::entity{0}), entt::entity{3});
ASSERT_EQ(pool.emplace(traits_type::construct(1, 1)), entt::entity{4});
ASSERT_EQ(pool.emplace(traits_type::construct(6, 3)), traits_type::construct(6, 3));
ASSERT_LT(pool.index(entt::entity{0}), pool.in_use());
ASSERT_LT(pool.index(entt::entity{1}), pool.in_use());
@@ -208,15 +208,15 @@ TEST(StorageEntity, Spawn) {
ASSERT_GE(pool.index(entt::entity{5}), pool.in_use());
ASSERT_LT(pool.index(traits_type::construct(6, 3)), pool.in_use());
ASSERT_EQ(pool.spawn(traits_type::construct(5, 42)), traits_type::construct(5, 42));
ASSERT_EQ(pool.spawn(traits_type::construct(5, 43)), entt::entity{7});
ASSERT_EQ(pool.emplace(traits_type::construct(5, 42)), traits_type::construct(5, 42));
ASSERT_EQ(pool.emplace(traits_type::construct(5, 43)), entt::entity{7});
pool.erase(entt::entity{2});
ASSERT_EQ(pool.spawn(), traits_type::construct(2, 1));
ASSERT_EQ(pool.emplace(), traits_type::construct(2, 1));
pool.erase(traits_type::construct(2, 1));
pool.spawn(entities, entities + 2u);
pool.insert(entities, entities + 2u);
ASSERT_EQ(entities[0u], traits_type::construct(2, 2));
ASSERT_EQ(entities[1u], entt::entity{8});
@@ -256,9 +256,9 @@ TEST(StorageEntity, Iterable) {
entt::storage<entt::entity> pool;
pool.spawn(entt::entity{1});
pool.spawn(entt::entity{3});
pool.spawn(entt::entity{42});
pool.emplace(entt::entity{1});
pool.emplace(entt::entity{3});
pool.emplace(entt::entity{42});
pool.erase(entt::entity{3});
@@ -300,9 +300,9 @@ TEST(StorageEntity, ConstIterable) {
entt::storage<entt::entity> pool;
pool.spawn(entt::entity{1});
pool.spawn(entt::entity{3});
pool.spawn(entt::entity{42});
pool.emplace(entt::entity{1});
pool.emplace(entt::entity{3});
pool.emplace(entt::entity{42});
pool.erase(entt::entity{3});