entity:
* custom move ctor/op for the registry class * no registry argument from storage emplace, remove, ... * added sparse set context to forward variables to mixins * removed sparse set user data
This commit is contained in:
1
TODO
1
TODO
@@ -4,6 +4,7 @@
|
||||
* add examples (and credits) from @alanjfs :)
|
||||
|
||||
WIP:
|
||||
* remove the const_cast within registry::assure (view<const E, ...> vs view<E, ...>, thread safe const registry)
|
||||
* fast-contains for sparse sets (low prio but nice-to-have)
|
||||
* runtime components (registry), runtime events (dispatcher/emitter), runtime context variables ...
|
||||
* runtime_view/registry, remove reference to basic_sparse_set<E>
|
||||
|
||||
@@ -110,6 +110,7 @@ class basic_registry {
|
||||
if(auto &&pdata = pools[type_id<Component>().hash()]; !pdata.pool) {
|
||||
auto *cpool = new storage_type<Component>{};
|
||||
pdata.pool.reset(cpool);
|
||||
pdata.pool->context(forward_as_any(const_cast<basic_registry &>(*this)));
|
||||
pdata.poly.template emplace<storage_type<Component> &>(*static_cast<storage_type<Component> *>(pdata.pool.get()));
|
||||
return cpool;
|
||||
} else {
|
||||
@@ -156,11 +157,39 @@ public:
|
||||
/*! @brief Default constructor. */
|
||||
basic_registry() = default;
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_registry(basic_registry &&) = default;
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_registry(basic_registry &&other) ENTT_NOEXCEPT
|
||||
: pools{std::move(other.pools)},
|
||||
vars{std::move(other.vars)},
|
||||
groups{std::move(other.groups)},
|
||||
entities{std::move(other.entities)},
|
||||
free_list{other.free_list} {
|
||||
for(auto &&pdata: pools) {
|
||||
pdata.second.pool->context(forward_as_any(*this));
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Default move assignment operator. @return This registry. */
|
||||
basic_registry &operator=(basic_registry &&) = default;
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This registry.
|
||||
*/
|
||||
basic_registry &operator=(basic_registry &&other) ENTT_NOEXCEPT {
|
||||
pools = std::move(other.pools);
|
||||
vars = std::move(other.vars);
|
||||
groups = std::move(other.groups);
|
||||
entities = std::move(other.entities);
|
||||
free_list = other.free_list;
|
||||
|
||||
for(auto &&pdata: pools) {
|
||||
pdata.second.pool->context(forward_as_any(*this));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepares a pool for the given type if required.
|
||||
@@ -519,7 +548,7 @@ public:
|
||||
ENTT_ASSERT(valid(entity), "Invalid entity");
|
||||
|
||||
for(auto &&pdata: pools) {
|
||||
pdata.second.pool->remove(entity, this);
|
||||
pdata.second.pool->remove(entity);
|
||||
}
|
||||
|
||||
return release_entity(entity, version);
|
||||
@@ -542,7 +571,7 @@ public:
|
||||
}
|
||||
} else {
|
||||
for(auto &&pdata: pools) {
|
||||
pdata.second.pool->remove(first, last, this);
|
||||
pdata.second.pool->remove(first, last);
|
||||
}
|
||||
|
||||
release(first, last);
|
||||
@@ -569,7 +598,7 @@ public:
|
||||
template<typename Component, typename... Args>
|
||||
decltype(auto) emplace(const entity_type entity, Args &&...args) {
|
||||
ENTT_ASSERT(valid(entity), "Invalid entity");
|
||||
return assure<Component>()->emplace(*this, entity, std::forward<Args>(args)...);
|
||||
return assure<Component>()->emplace(entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -586,7 +615,7 @@ public:
|
||||
template<typename Component, typename It>
|
||||
void insert(It first, It last, const Component &value = {}) {
|
||||
ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
|
||||
assure<Component>()->insert(*this, first, last, value);
|
||||
assure<Component>()->insert(first, last, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -605,7 +634,7 @@ public:
|
||||
void insert(EIt first, EIt last, CIt from) {
|
||||
static_assert(std::is_constructible_v<Component, typename std::iterator_traits<CIt>::value_type>, "Invalid value type");
|
||||
ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
|
||||
assure<Component>()->insert(*this, first, last, from);
|
||||
assure<Component>()->insert(first, last, from);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -626,8 +655,8 @@ public:
|
||||
auto *cpool = assure<Component>();
|
||||
|
||||
return cpool->contains(entity)
|
||||
? cpool->patch(*this, entity, [&args...](auto &...curr) { ((curr = Component{std::forward<Args>(args)...}), ...); })
|
||||
: cpool->emplace(*this, entity, std::forward<Args>(args)...);
|
||||
? cpool->patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward<Args>(args)...}), ...); })
|
||||
: cpool->emplace(entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -657,7 +686,7 @@ public:
|
||||
template<typename Component, typename... Func>
|
||||
decltype(auto) patch(const entity_type entity, Func &&...func) {
|
||||
ENTT_ASSERT(valid(entity), "Invalid entity");
|
||||
return assure<Component>()->patch(*this, entity, std::forward<Func>(func)...);
|
||||
return assure<Component>()->patch(entity, std::forward<Func>(func)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -679,7 +708,7 @@ public:
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
decltype(auto) replace(const entity_type entity, Args &&...args) {
|
||||
return assure<Component>()->patch(*this, entity, [&args...](auto &...curr) { ((curr = Component{std::forward<Args>(args)...}), ...); });
|
||||
return assure<Component>()->patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward<Args>(args)...}), ...); });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -696,7 +725,7 @@ public:
|
||||
size_type remove(const entity_type entity) {
|
||||
ENTT_ASSERT(valid(entity), "Invalid entity");
|
||||
static_assert(sizeof...(Component) > 0, "Provide one or more component types");
|
||||
return (assure<Component>()->remove(entity, this) + ... + size_type{});
|
||||
return (assure<Component>()->remove(entity) + ... + size_type{});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -719,7 +748,7 @@ public:
|
||||
for(; first != last; ++first) {
|
||||
const auto entity = *first;
|
||||
ENTT_ASSERT(valid(entity), "Invalid entity");
|
||||
count += (std::get<storage_type<Component> *>(cpools)->remove(entity, this) + ...);
|
||||
count += (std::get<storage_type<Component> *>(cpools)->remove(entity) + ...);
|
||||
}
|
||||
|
||||
return count;
|
||||
@@ -739,7 +768,7 @@ public:
|
||||
void erase(const entity_type entity) {
|
||||
ENTT_ASSERT(valid(entity), "Invalid entity");
|
||||
static_assert(sizeof...(Component) > 0, "Provide one or more component types");
|
||||
(assure<Component>()->erase(entity, this), ...);
|
||||
(assure<Component>()->erase(entity), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -760,7 +789,7 @@ public:
|
||||
for(; first != last; ++first) {
|
||||
const auto entity = *first;
|
||||
ENTT_ASSERT(valid(entity), "Invalid entity");
|
||||
(std::get<storage_type<Component> *>(cpools)->erase(entity, this), ...);
|
||||
(std::get<storage_type<Component> *>(cpools)->erase(entity), ...);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -868,7 +897,7 @@ public:
|
||||
[[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&...args) {
|
||||
ENTT_ASSERT(valid(entity), "Invalid entity");
|
||||
auto *cpool = assure<Component>();
|
||||
return cpool->contains(entity) ? cpool->get(entity) : cpool->emplace(*this, entity, std::forward<Args>(args)...);
|
||||
return cpool->contains(entity) ? cpool->get(entity) : cpool->emplace(entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -916,12 +945,12 @@ public:
|
||||
void clear() {
|
||||
if constexpr(sizeof...(Component) == 0) {
|
||||
for(auto &&pdata: pools) {
|
||||
pdata.second.pool->clear(this);
|
||||
pdata.second.pool->clear();
|
||||
}
|
||||
|
||||
each([this](const auto entity) { release_entity(entity, entity_traits::to_version(entity) + 1u); });
|
||||
} else {
|
||||
(assure<Component>()->clear(this), ...);
|
||||
(assure<Component>()->clear(), ...);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/algorithm.hpp"
|
||||
#include "../core/any.hpp"
|
||||
#include "../core/memory.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
@@ -207,7 +208,7 @@ protected:
|
||||
* @brief Erase an entity from a sparse set.
|
||||
* @param entt A valid identifier.
|
||||
*/
|
||||
virtual void swap_and_pop(const Entity entt, void *) {
|
||||
virtual void swap_and_pop(const Entity entt) {
|
||||
auto &ref = sparse_ref(entt);
|
||||
const auto pos = static_cast<size_type>(entity_traits::to_entity(ref));
|
||||
ENTT_ASSERT(packed[pos] == entt, "Invalid identifier");
|
||||
@@ -226,7 +227,7 @@ protected:
|
||||
* @brief Erase an entity from a sparse set.
|
||||
* @param entt A valid identifier.
|
||||
*/
|
||||
virtual void in_place_pop(const Entity entt, void *) {
|
||||
virtual void in_place_pop(const Entity entt) {
|
||||
auto &ref = sparse_ref(entt);
|
||||
const auto pos = static_cast<size_type>(entity_traits::to_entity(ref));
|
||||
ENTT_ASSERT(packed[pos] == entt, "Invalid identifier");
|
||||
@@ -240,7 +241,7 @@ protected:
|
||||
* @brief Assigns an entity to a sparse set.
|
||||
* @param entt A valid identifier.
|
||||
*/
|
||||
virtual void try_emplace(const Entity entt, void *) {
|
||||
virtual void try_emplace(const Entity entt) {
|
||||
const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
|
||||
const auto page = pos / sparse_page_v;
|
||||
|
||||
@@ -301,7 +302,6 @@ public:
|
||||
explicit basic_sparse_set(deletion_policy pol, const allocator_type &allocator = {})
|
||||
: sparse{allocator},
|
||||
packed{allocator},
|
||||
udata{},
|
||||
free_list{tombstone},
|
||||
mode{pol} {}
|
||||
|
||||
@@ -312,7 +312,6 @@ public:
|
||||
basic_sparse_set(basic_sparse_set &&other) ENTT_NOEXCEPT
|
||||
: sparse{std::move(other.sparse)},
|
||||
packed{std::move(other.packed)},
|
||||
udata{std::exchange(other.udata, nullptr)},
|
||||
free_list{std::exchange(other.free_list, tombstone)},
|
||||
mode{other.mode} {}
|
||||
|
||||
@@ -324,7 +323,6 @@ public:
|
||||
basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) ENTT_NOEXCEPT
|
||||
: sparse{std::move(other.sparse), allocator},
|
||||
packed{std::move(other.packed), allocator},
|
||||
udata{std::exchange(other.udata, nullptr)},
|
||||
free_list{std::exchange(other.free_list, tombstone)},
|
||||
mode{other.mode} {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
|
||||
@@ -346,7 +344,6 @@ public:
|
||||
release_sparse_pages();
|
||||
sparse = std::move(other.sparse);
|
||||
packed = std::move(other.packed);
|
||||
udata = std::exchange(other.udata, nullptr);
|
||||
free_list = std::exchange(other.free_list, tombstone);
|
||||
mode = other.mode;
|
||||
return *this;
|
||||
@@ -361,7 +358,6 @@ public:
|
||||
swap_contents(other);
|
||||
swap(sparse, other.sparse);
|
||||
swap(packed, other.packed);
|
||||
swap(udata, other.udata);
|
||||
swap(free_list, other.free_list);
|
||||
swap(mode, other.mode);
|
||||
}
|
||||
@@ -592,10 +588,9 @@ public:
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @param entt A valid identifier.
|
||||
* @param ud Optional user data that are forwarded as-is to derived classes.
|
||||
*/
|
||||
void emplace(const entity_type entt, void *ud = nullptr) {
|
||||
try_emplace(entt, ud);
|
||||
void emplace(const entity_type entt) {
|
||||
try_emplace(entt);
|
||||
ENTT_ASSERT(contains(entt), "Emplace did not take place");
|
||||
}
|
||||
|
||||
@@ -609,18 +604,17 @@ public:
|
||||
* @tparam It Type of input iterator.
|
||||
* @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 ud Optional user data that are forwarded as-is to derived classes.
|
||||
*/
|
||||
template<typename It>
|
||||
void insert(It first, It last, void *ud = nullptr) {
|
||||
void insert(It first, It last) {
|
||||
for(; first != last && free_list != null; ++first) {
|
||||
emplace(*first, ud);
|
||||
emplace(*first);
|
||||
}
|
||||
|
||||
reserve(packed.size() + std::distance(first, last));
|
||||
|
||||
for(; first != last; ++first) {
|
||||
emplace(*first, ud);
|
||||
emplace(*first);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,11 +626,10 @@ public:
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @param entt A valid identifier.
|
||||
* @param ud Optional user data that are forwarded as-is to derived classes.
|
||||
*/
|
||||
void erase(const entity_type entt, void *ud = nullptr) {
|
||||
void erase(const entity_type entt) {
|
||||
ENTT_ASSERT(contains(entt), "Set does not contain entity");
|
||||
(mode == deletion_policy::in_place) ? in_place_pop(entt, ud) : swap_and_pop(entt, ud);
|
||||
(mode == deletion_policy::in_place) ? in_place_pop(entt) : swap_and_pop(entt);
|
||||
ENTT_ASSERT(!contains(entt), "Destruction did not take place");
|
||||
}
|
||||
|
||||
@@ -648,23 +641,21 @@ public:
|
||||
* @tparam It Type of input iterator.
|
||||
* @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 ud Optional user data that are forwarded as-is to derived classes.
|
||||
*/
|
||||
template<typename It>
|
||||
void erase(It first, It last, void *ud = nullptr) {
|
||||
void erase(It first, It last) {
|
||||
for(; first != last; ++first) {
|
||||
erase(*first, ud);
|
||||
erase(*first);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes an entity from a sparse set if it exists.
|
||||
* @param entt A valid identifier.
|
||||
* @param ud Optional user data that are forwarded as-is to derived classes.
|
||||
* @return True if the entity is actually removed, false otherwise.
|
||||
*/
|
||||
bool remove(const entity_type entt, void *ud = nullptr) {
|
||||
return contains(entt) && (erase(entt, ud), true);
|
||||
bool remove(const entity_type entt) {
|
||||
return contains(entt) && (erase(entt), true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -672,15 +663,14 @@ public:
|
||||
* @tparam It Type of input iterator.
|
||||
* @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 ud Optional user data that are forwarded as-is to derived classes.
|
||||
* @return The number of entities actually removed.
|
||||
*/
|
||||
template<typename It>
|
||||
size_type remove(It first, It last, void *ud = nullptr) {
|
||||
size_type remove(It first, It last) {
|
||||
size_type found{};
|
||||
|
||||
for(; first != last; ++first) {
|
||||
found += remove(*first, ud);
|
||||
found += remove(*first);
|
||||
}
|
||||
|
||||
return found;
|
||||
@@ -840,42 +830,20 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears a sparse set.
|
||||
* @param ud Optional user data that are forwarded as-is to derived classes.
|
||||
*/
|
||||
void clear(void *ud = nullptr) {
|
||||
/*! @brief Clears a sparse set. */
|
||||
void clear() {
|
||||
for(auto &&entity: *this) {
|
||||
// honor the modality and filter all tombstones
|
||||
remove(entity, ud);
|
||||
remove(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief User defined arbitrary data.
|
||||
* @return An opaque pointer to user defined arbitrary data.
|
||||
*/
|
||||
void *user_data() ENTT_NOEXCEPT {
|
||||
return udata;
|
||||
}
|
||||
|
||||
/*! @copydoc user_data */
|
||||
const void *user_data() const ENTT_NOEXCEPT {
|
||||
return udata;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief User defined arbitrary data.
|
||||
* @param ptr An opaque pointer to user defined arbitrary data.
|
||||
*/
|
||||
void user_data(void *ptr) ENTT_NOEXCEPT {
|
||||
udata = ptr;
|
||||
}
|
||||
/*! @brief Forwards context variables to mixins, if any. */
|
||||
virtual void context(any) ENTT_NOEXCEPT {}
|
||||
|
||||
private:
|
||||
sparse_container_type sparse;
|
||||
packed_container_type packed;
|
||||
void *udata;
|
||||
entity_type free_list;
|
||||
deletion_policy mode;
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/algorithm.hpp"
|
||||
#include "../core/any.hpp"
|
||||
#include "../core/compressed_pair.hpp"
|
||||
#include "../core/memory.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
@@ -316,9 +317,8 @@ protected:
|
||||
/**
|
||||
* @brief Erase an element from a storage.
|
||||
* @param entt A valid identifier.
|
||||
* @param ud Optional user data that are forwarded as-is to derived classes.
|
||||
*/
|
||||
void swap_and_pop(const Entity entt, void *ud) override {
|
||||
void swap_and_pop(const Entity entt) override {
|
||||
const auto pos = base_type::index(entt);
|
||||
const auto last = base_type::size() - 1u;
|
||||
|
||||
@@ -330,17 +330,16 @@ protected:
|
||||
target = std::move(elem);
|
||||
std::destroy_at(std::addressof(elem));
|
||||
|
||||
base_type::swap_and_pop(entt, ud);
|
||||
base_type::swap_and_pop(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases an element from a storage.
|
||||
* @param entt A valid identifier.
|
||||
* @param ud Optional user data that are forwarded as-is to derived classes.
|
||||
*/
|
||||
void in_place_pop(const Entity entt, void *ud) override {
|
||||
void in_place_pop(const Entity entt) override {
|
||||
const auto pos = base_type::index(entt);
|
||||
base_type::in_place_pop(entt, ud);
|
||||
base_type::in_place_pop(entt);
|
||||
// support for nosy destructors
|
||||
std::destroy_at(std::addressof(element_at(pos)));
|
||||
}
|
||||
@@ -348,15 +347,14 @@ protected:
|
||||
/**
|
||||
* @brief Assigns an entity to a storage.
|
||||
* @param entt A valid identifier.
|
||||
* @param ud Optional user data that are forwarded as-is to derived classes.
|
||||
*/
|
||||
void try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] void *ud) override {
|
||||
void try_emplace([[maybe_unused]] const Entity entt) override {
|
||||
if constexpr(std::is_default_constructible_v<value_type>) {
|
||||
const auto pos = base_type::slot();
|
||||
construct(assure_at_least(pos));
|
||||
|
||||
ENTT_TRY {
|
||||
base_type::try_emplace(entt, ud);
|
||||
base_type::try_emplace(entt);
|
||||
ENTT_ASSERT(pos == base_type::index(entt), "Misplaced component");
|
||||
}
|
||||
ENTT_CATCH {
|
||||
@@ -648,7 +646,7 @@ public:
|
||||
construct(elem, std::forward<Args>(args)...);
|
||||
|
||||
ENTT_TRY {
|
||||
base_type::try_emplace(entt, nullptr);
|
||||
base_type::try_emplace(entt);
|
||||
ENTT_ASSERT(pos == base_type::index(entt), "Misplaced component");
|
||||
}
|
||||
ENTT_CATCH {
|
||||
@@ -814,7 +812,7 @@ public:
|
||||
template<typename... Args>
|
||||
void emplace(const entity_type entt, Args &&...args) {
|
||||
[[maybe_unused]] const value_type elem{std::forward<Args>(args)...};
|
||||
base_type::try_emplace(entt, nullptr);
|
||||
base_type::try_emplace(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -850,62 +848,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Mixin type to use to wrap basic storage classes.
|
||||
* @tparam Type The type of the underlying storage.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct storage_adapter_mixin: Type {
|
||||
static_assert(std::is_same_v<typename Type::value_type, std::decay_t<typename Type::value_type>>, "Invalid object type");
|
||||
|
||||
/*! @brief Type of the objects assigned to entities. */
|
||||
using value_type = typename Type::value_type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename Type::entity_type;
|
||||
|
||||
/*! @brief Inherited constructors. */
|
||||
using Type::Type;
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
template<typename... Args>
|
||||
decltype(auto) emplace(basic_registry<entity_type> &, const entity_type entt, Args &&...args) {
|
||||
return Type::emplace(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Patches the given instance for an entity.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param entt A valid identifier.
|
||||
* @param func Valid function objects.
|
||||
* @return A reference to the patched instance.
|
||||
*/
|
||||
template<typename... Func>
|
||||
decltype(auto) patch(basic_registry<entity_type> &, const entity_type entt, Func &&...func) {
|
||||
return Type::patch(entt, std::forward<Func>(func)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
template<typename It, typename... Args>
|
||||
void insert(basic_registry<entity_type> &, It first, It last, Args &&...args) {
|
||||
Type::insert(std::move(first), std::move(last), std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Mixin type to use to add signal support to storage types.
|
||||
* @tparam Type The type of the underlying storage.
|
||||
@@ -913,24 +855,24 @@ struct storage_adapter_mixin: Type {
|
||||
template<typename Type>
|
||||
class sigh_storage_mixin final: public Type {
|
||||
/*! @copydoc basic_sparse_set::swap_and_pop */
|
||||
void swap_and_pop(const typename Type::entity_type entt, void *ud) final {
|
||||
ENTT_ASSERT(ud != nullptr, "Invalid pointer to registry");
|
||||
destruction.publish(*static_cast<basic_registry<typename Type::entity_type> *>(ud), entt);
|
||||
Type::swap_and_pop(entt, ud);
|
||||
void swap_and_pop(const typename Type::entity_type entt) final {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
destruction.publish(*owner, entt);
|
||||
Type::swap_and_pop(entt);
|
||||
}
|
||||
|
||||
/*! @copydoc basic_sparse_set::in_place_pop */
|
||||
void in_place_pop(const typename Type::entity_type entt, void *ud) final {
|
||||
ENTT_ASSERT(ud != nullptr, "Invalid pointer to registry");
|
||||
destruction.publish(*static_cast<basic_registry<typename Type::entity_type> *>(ud), entt);
|
||||
Type::in_place_pop(entt, ud);
|
||||
void in_place_pop(const typename Type::entity_type entt) final {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
destruction.publish(*owner, entt);
|
||||
Type::in_place_pop(entt);
|
||||
}
|
||||
|
||||
/*! @copydoc basic_sparse_set::try_emplace */
|
||||
void try_emplace(const typename Type::entity_type entt, void *ud) final {
|
||||
ENTT_ASSERT(ud != nullptr, "Invalid pointer to registry");
|
||||
Type::try_emplace(entt, ud);
|
||||
construction.publish(*static_cast<basic_registry<typename Type::entity_type> *>(ud), entt);
|
||||
void try_emplace(const typename Type::entity_type entt) final {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
Type::try_emplace(entt);
|
||||
construction.publish(*owner, entt);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -1010,30 +952,28 @@ public:
|
||||
/**
|
||||
* @brief Assigns entities to a storage.
|
||||
* @tparam Args Types of arguments to use to construct the object.
|
||||
* @param owner The registry that issued the request.
|
||||
* @param entt A valid identifier.
|
||||
* @param args Parameters to use to initialize the object.
|
||||
* @return A reference to the newly created object.
|
||||
*/
|
||||
template<typename... Args>
|
||||
decltype(auto) emplace(basic_registry<entity_type> &owner, const entity_type entt, Args &&...args) {
|
||||
decltype(auto) emplace(const entity_type entt, Args &&...args) {
|
||||
Type::emplace(entt, std::forward<Args>(args)...);
|
||||
construction.publish(owner, entt);
|
||||
construction.publish(*owner, entt);
|
||||
return this->get(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Patches the given instance for an entity.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param owner The registry that issued the request.
|
||||
* @param entt A valid identifier.
|
||||
* @param func Valid function objects.
|
||||
* @return A reference to the patched instance.
|
||||
*/
|
||||
template<typename... Func>
|
||||
decltype(auto) patch(basic_registry<entity_type> &owner, const entity_type entt, Func &&...func) {
|
||||
decltype(auto) patch(const entity_type entt, Func &&...func) {
|
||||
Type::patch(entt, std::forward<Func>(func)...);
|
||||
update.publish(owner, entt);
|
||||
update.publish(*owner, entt);
|
||||
return this->get(entt);
|
||||
}
|
||||
|
||||
@@ -1042,27 +982,37 @@ public:
|
||||
* @tparam It Type of input iterator.
|
||||
* @tparam Args Types of arguments to use to construct the objects assigned
|
||||
* to the entities.
|
||||
* @param owner The registry that issued the request.
|
||||
* @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.
|
||||
*/
|
||||
template<typename It, typename... Args>
|
||||
void insert(basic_registry<entity_type> &owner, It first, It last, Args &&...args) {
|
||||
void insert(It first, It last, Args &&...args) {
|
||||
Type::insert(first, last, std::forward<Args>(args)...);
|
||||
|
||||
if(!construction.empty()) {
|
||||
for(; first != last; ++first) {
|
||||
construction.publish(owner, *first);
|
||||
construction.publish(*owner, *first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forwards context variables to mixins, if any.
|
||||
* @param value A context variable wrapped in an opaque container.
|
||||
*/
|
||||
void context(any value) ENTT_NOEXCEPT final {
|
||||
auto *reg = any_cast<basic_registry<entity_type>>(&value);
|
||||
owner = reg ? reg : owner;
|
||||
Type::context(std::move(value));
|
||||
}
|
||||
|
||||
private:
|
||||
sigh<void(basic_registry<entity_type> &, const entity_type)> construction{};
|
||||
sigh<void(basic_registry<entity_type> &, const entity_type)> destruction{};
|
||||
sigh<void(basic_registry<entity_type> &, const entity_type)> update{};
|
||||
basic_registry<entity_type> *owner{};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,8 +12,8 @@ struct PolyStorage
|
||||
: entt::type_list_cat_t<
|
||||
decltype(as_type_list(std::declval<entt::Storage<Entity>>())),
|
||||
entt::type_list<
|
||||
void(const Entity *, const Entity *, void *),
|
||||
void(entt::basic_registry<Entity> &, const Entity, const void *),
|
||||
void(const Entity *, const Entity *),
|
||||
void(const Entity, const void *),
|
||||
const void *(const Entity) const,
|
||||
void(entt::basic_registry<Entity> &) const>> {
|
||||
using entity_type = Entity;
|
||||
@@ -23,12 +23,12 @@ struct PolyStorage
|
||||
struct type: entt::Storage<Entity>::template type<Base> {
|
||||
static constexpr auto base = decltype(as_type_list(std::declval<entt::Storage<Entity>>()))::size;
|
||||
|
||||
void erase(entt::basic_registry<Entity> &owner, const entity_type *first, const entity_type *last) {
|
||||
entt::poly_call<base + 0>(*this, first, last, &owner);
|
||||
void erase(const entity_type *first, const entity_type *last) {
|
||||
entt::poly_call<base + 0>(*this, first, last);
|
||||
}
|
||||
|
||||
void emplace(entt::basic_registry<Entity> &owner, const entity_type entity, const void *instance) {
|
||||
entt::poly_call<base + 1>(*this, owner, entity, instance);
|
||||
void emplace(const entity_type entity, const void *instance) {
|
||||
entt::poly_call<base + 1>(*this, entity, instance);
|
||||
}
|
||||
|
||||
const void *get(const entity_type entity) const {
|
||||
@@ -42,8 +42,8 @@ struct PolyStorage
|
||||
|
||||
template<typename Type>
|
||||
struct members {
|
||||
static void emplace(Type &self, entt::basic_registry<Entity> &owner, const entity_type entity, const void *instance) {
|
||||
self.emplace(owner, entity, *static_cast<const typename Type::value_type *>(instance));
|
||||
static void emplace(Type &self, const entity_type entity, const void *instance) {
|
||||
self.emplace(entity, *static_cast<const typename Type::value_type *>(instance));
|
||||
}
|
||||
|
||||
static const typename Type::value_type *get(const Type &self, const entity_type entity) {
|
||||
@@ -84,7 +84,7 @@ TEST(PolyStorage, CopyEntity) {
|
||||
|
||||
registry.visit(entity, [&](const auto &info) {
|
||||
auto &&storage = registry.storage(info);
|
||||
storage->emplace(registry, other, storage->get(entity));
|
||||
storage->emplace(other, storage->get(entity));
|
||||
});
|
||||
|
||||
ASSERT_TRUE((registry.all_of<int, char>(entity)));
|
||||
@@ -129,11 +129,11 @@ TEST(PolyStorage, Constness) {
|
||||
// cannot invoke erase on a const storage, let's copy the returned value
|
||||
auto cstorage = cregistry.storage(entt::type_id<int>());
|
||||
|
||||
ASSERT_DEATH(cstorage->erase(registry, std::begin(entity), std::end(entity)), "");
|
||||
ASSERT_DEATH(cstorage->erase(std::begin(entity), std::end(entity)), "");
|
||||
ASSERT_TRUE(registry.all_of<int>(entity[0]));
|
||||
|
||||
auto &&storage = registry.storage(entt::type_id<int>());
|
||||
storage->erase(registry, std::begin(entity), std::end(entity));
|
||||
storage->erase(std::begin(entity), std::end(entity));
|
||||
|
||||
ASSERT_FALSE(registry.all_of<int>(entity[0]));
|
||||
}
|
||||
|
||||
@@ -39,14 +39,16 @@ TEST(SighStorageMixin, GenericType) {
|
||||
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
|
||||
entt::registry registry{};
|
||||
|
||||
pool.context(entt::forward_as_any(registry));
|
||||
|
||||
counter on_construct{};
|
||||
counter on_destroy{};
|
||||
|
||||
pool.on_construct().connect<&listener>(on_construct);
|
||||
pool.on_destroy().connect<&listener>(on_destroy);
|
||||
|
||||
base.emplace(entities[0u], ®istry);
|
||||
pool.emplace(registry, entities[1u]);
|
||||
base.emplace(entities[0u]);
|
||||
pool.emplace(entities[1u]);
|
||||
|
||||
ASSERT_EQ(on_construct.value, 2);
|
||||
ASSERT_EQ(on_destroy.value, 0);
|
||||
@@ -55,32 +57,32 @@ TEST(SighStorageMixin, GenericType) {
|
||||
ASSERT_EQ(pool.get(entities[0u]), 0);
|
||||
ASSERT_EQ(pool.get(entities[1u]), 0);
|
||||
|
||||
base.erase(entities[0u], ®istry);
|
||||
pool.erase(entities[1u], ®istry);
|
||||
base.erase(entities[0u]);
|
||||
pool.erase(entities[1u]);
|
||||
|
||||
ASSERT_EQ(on_construct.value, 2);
|
||||
ASSERT_EQ(on_destroy.value, 2);
|
||||
ASSERT_TRUE(pool.empty());
|
||||
|
||||
base.insert(std::begin(entities), std::end(entities), ®istry);
|
||||
base.insert(std::begin(entities), std::end(entities));
|
||||
|
||||
ASSERT_EQ(pool.get(entities[0u]), 0);
|
||||
ASSERT_EQ(pool.get(entities[1u]), 0);
|
||||
ASSERT_FALSE(pool.empty());
|
||||
|
||||
base.erase(entities[1u], ®istry);
|
||||
base.erase(entities[1u]);
|
||||
|
||||
ASSERT_EQ(on_construct.value, 4);
|
||||
ASSERT_EQ(on_destroy.value, 3);
|
||||
ASSERT_FALSE(pool.empty());
|
||||
|
||||
base.erase(entities[0u], ®istry);
|
||||
base.erase(entities[0u]);
|
||||
|
||||
ASSERT_EQ(on_construct.value, 4);
|
||||
ASSERT_EQ(on_destroy.value, 4);
|
||||
ASSERT_TRUE(pool.empty());
|
||||
|
||||
pool.insert(registry, std::begin(entities), std::end(entities), 3);
|
||||
pool.insert(std::begin(entities), std::end(entities), 3);
|
||||
|
||||
ASSERT_EQ(on_construct.value, 6);
|
||||
ASSERT_EQ(on_destroy.value, 4);
|
||||
@@ -89,7 +91,7 @@ TEST(SighStorageMixin, GenericType) {
|
||||
ASSERT_EQ(pool.get(entities[0u]), 3);
|
||||
ASSERT_EQ(pool.get(entities[1u]), 3);
|
||||
|
||||
pool.erase(std::begin(entities), std::end(entities), ®istry);
|
||||
pool.erase(std::begin(entities), std::end(entities));
|
||||
|
||||
ASSERT_EQ(on_construct.value, 6);
|
||||
ASSERT_EQ(on_destroy.value, 6);
|
||||
@@ -102,14 +104,16 @@ TEST(SighStorageMixin, EmptyType) {
|
||||
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
|
||||
entt::registry registry{};
|
||||
|
||||
pool.context(entt::forward_as_any(registry));
|
||||
|
||||
counter on_construct{};
|
||||
counter on_destroy{};
|
||||
|
||||
pool.on_construct().connect<&listener>(on_construct);
|
||||
pool.on_destroy().connect<&listener>(on_destroy);
|
||||
|
||||
base.emplace(entities[0u], ®istry);
|
||||
pool.emplace(registry, entities[1u]);
|
||||
base.emplace(entities[0u]);
|
||||
pool.emplace(entities[1u]);
|
||||
|
||||
ASSERT_EQ(on_construct.value, 2);
|
||||
ASSERT_EQ(on_destroy.value, 0);
|
||||
@@ -118,32 +122,32 @@ TEST(SighStorageMixin, EmptyType) {
|
||||
ASSERT_TRUE(pool.contains(entities[0u]));
|
||||
ASSERT_TRUE(pool.contains(entities[1u]));
|
||||
|
||||
base.erase(entities[0u], ®istry);
|
||||
pool.erase(entities[1u], ®istry);
|
||||
base.erase(entities[0u]);
|
||||
pool.erase(entities[1u]);
|
||||
|
||||
ASSERT_EQ(on_construct.value, 2);
|
||||
ASSERT_EQ(on_destroy.value, 2);
|
||||
ASSERT_TRUE(pool.empty());
|
||||
|
||||
base.insert(std::begin(entities), std::end(entities), ®istry);
|
||||
base.insert(std::begin(entities), std::end(entities));
|
||||
|
||||
ASSERT_TRUE(pool.contains(entities[0u]));
|
||||
ASSERT_TRUE(pool.contains(entities[1u]));
|
||||
ASSERT_FALSE(pool.empty());
|
||||
|
||||
base.erase(entities[1u], ®istry);
|
||||
base.erase(entities[1u]);
|
||||
|
||||
ASSERT_EQ(on_construct.value, 4);
|
||||
ASSERT_EQ(on_destroy.value, 3);
|
||||
ASSERT_FALSE(pool.empty());
|
||||
|
||||
base.erase(entities[0u], ®istry);
|
||||
base.erase(entities[0u]);
|
||||
|
||||
ASSERT_EQ(on_construct.value, 4);
|
||||
ASSERT_EQ(on_destroy.value, 4);
|
||||
ASSERT_TRUE(pool.empty());
|
||||
|
||||
pool.insert(registry, std::begin(entities), std::end(entities));
|
||||
pool.insert(std::begin(entities), std::end(entities));
|
||||
|
||||
ASSERT_EQ(on_construct.value, 6);
|
||||
ASSERT_EQ(on_destroy.value, 4);
|
||||
@@ -152,7 +156,7 @@ TEST(SighStorageMixin, EmptyType) {
|
||||
ASSERT_TRUE(pool.contains(entities[0u]));
|
||||
ASSERT_TRUE(pool.contains(entities[1u]));
|
||||
|
||||
pool.erase(std::begin(entities), std::end(entities), ®istry);
|
||||
pool.erase(std::begin(entities), std::end(entities));
|
||||
|
||||
ASSERT_EQ(on_construct.value, 6);
|
||||
ASSERT_EQ(on_destroy.value, 6);
|
||||
@@ -165,15 +169,17 @@ TEST(SighStorageMixin, NonDefaultConstructibleType) {
|
||||
entt::entity entities[2u]{entt::entity{3}, entt::entity{42}};
|
||||
entt::registry registry{};
|
||||
|
||||
pool.context(entt::forward_as_any(registry));
|
||||
|
||||
counter on_construct{};
|
||||
counter on_destroy{};
|
||||
|
||||
pool.on_construct().connect<&listener>(on_construct);
|
||||
pool.on_destroy().connect<&listener>(on_destroy);
|
||||
|
||||
ASSERT_DEATH(base.emplace(entities[0u], ®istry), "");
|
||||
ASSERT_DEATH(base.emplace(entities[0u]), "");
|
||||
|
||||
pool.emplace(registry, entities[1u], 3);
|
||||
pool.emplace(entities[1u], 3);
|
||||
|
||||
ASSERT_EQ(on_construct.value, 1);
|
||||
ASSERT_EQ(on_destroy.value, 0);
|
||||
@@ -182,19 +188,19 @@ TEST(SighStorageMixin, NonDefaultConstructibleType) {
|
||||
ASSERT_FALSE(pool.contains(entities[0u]));
|
||||
ASSERT_EQ(pool.get(entities[1u]).value, 3);
|
||||
|
||||
base.erase(entities[1u], ®istry);
|
||||
base.erase(entities[1u]);
|
||||
|
||||
ASSERT_EQ(on_construct.value, 1);
|
||||
ASSERT_EQ(on_destroy.value, 1);
|
||||
ASSERT_TRUE(pool.empty());
|
||||
|
||||
ASSERT_DEATH(base.insert(std::begin(entities), std::end(entities), ®istry), "");
|
||||
ASSERT_DEATH(base.insert(std::begin(entities), std::end(entities)), "");
|
||||
|
||||
ASSERT_FALSE(pool.contains(entities[0u]));
|
||||
ASSERT_FALSE(pool.contains(entities[1u]));
|
||||
ASSERT_TRUE(pool.empty());
|
||||
|
||||
pool.insert(registry, std::begin(entities), std::end(entities), 3);
|
||||
pool.insert(std::begin(entities), std::end(entities), 3);
|
||||
|
||||
ASSERT_EQ(on_construct.value, 3);
|
||||
ASSERT_EQ(on_destroy.value, 1);
|
||||
@@ -203,7 +209,7 @@ TEST(SighStorageMixin, NonDefaultConstructibleType) {
|
||||
ASSERT_EQ(pool.get(entities[0u]).value, 3);
|
||||
ASSERT_EQ(pool.get(entities[1u]).value, 3);
|
||||
|
||||
pool.erase(std::begin(entities), std::end(entities), ®istry);
|
||||
pool.erase(std::begin(entities), std::end(entities));
|
||||
|
||||
ASSERT_EQ(on_construct.value, 3);
|
||||
ASSERT_EQ(on_destroy.value, 3);
|
||||
|
||||
@@ -75,6 +75,8 @@ TEST(SparseSet, Functionalities) {
|
||||
ASSERT_EQ(set.begin(), set.end());
|
||||
ASSERT_FALSE(set.contains(entt::entity{0}));
|
||||
ASSERT_FALSE(set.contains(entt::entity{42}));
|
||||
|
||||
ASSERT_NO_THROW(set.context(entt::any{}));
|
||||
}
|
||||
|
||||
TEST(SparseSet, Contains) {
|
||||
@@ -1169,35 +1171,6 @@ TEST(SparseSet, CanModifyDuringIteration) {
|
||||
[[maybe_unused]] const auto entity = *it;
|
||||
}
|
||||
|
||||
TEST(SparseSet, UserData) {
|
||||
entt::sparse_set set;
|
||||
int value = 42;
|
||||
|
||||
ASSERT_EQ(set.user_data(), nullptr);
|
||||
|
||||
set.user_data(&value);
|
||||
entt::sparse_set other{std::move(set)};
|
||||
|
||||
ASSERT_EQ(std::as_const(set).user_data(), nullptr);
|
||||
ASSERT_EQ(other.user_data(), &value);
|
||||
|
||||
std::swap(set, other);
|
||||
|
||||
ASSERT_EQ(set.user_data(), &value);
|
||||
ASSERT_EQ(std::as_const(other).user_data(), nullptr);
|
||||
|
||||
other = std::move(set);
|
||||
|
||||
ASSERT_EQ(set.user_data(), nullptr);
|
||||
ASSERT_EQ(other.user_data(), &value);
|
||||
|
||||
entt::sparse_set last{std::move(other), std::allocator<entt::entity>{}};
|
||||
|
||||
ASSERT_EQ(set.user_data(), nullptr);
|
||||
ASSERT_EQ(other.user_data(), nullptr);
|
||||
ASSERT_EQ(last.user_data(), &value);
|
||||
}
|
||||
|
||||
TEST(SparseSet, CustomAllocator) {
|
||||
test::throwing_allocator<entt::entity> allocator{};
|
||||
entt::basic_sparse_set<entt::entity, test::throwing_allocator<entt::entity>> set{allocator};
|
||||
|
||||
@@ -1213,9 +1213,9 @@ TEST(MultiComponentView, SameComponentTypes) {
|
||||
const entt::entity e0{42u};
|
||||
const entt::entity e1{3u};
|
||||
|
||||
storage.emplace(registry, e0, 7);
|
||||
other.emplace(registry, e0, 9);
|
||||
other.emplace(registry, e1, 1);
|
||||
storage.emplace(e0, 7);
|
||||
other.emplace(e0, 9);
|
||||
other.emplace(e1, 1);
|
||||
|
||||
ASSERT_TRUE(view.contains(e0));
|
||||
ASSERT_FALSE(view.contains(e1));
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
template<typename Entity, typename Type>
|
||||
struct entt::storage_traits<Entity, Type> {
|
||||
// no signal regardless of component type ...
|
||||
using storage_type = storage_adapter_mixin<basic_storage<Entity, Type>>;
|
||||
using storage_type = basic_storage<Entity, Type>;
|
||||
};
|
||||
|
||||
template<typename Entity>
|
||||
|
||||
Reference in New Issue
Block a user