diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index 56635c805..e7dc317a2 100644 --- a/src/entt/entity/registry.hpp +++ b/src/entt/entity/registry.hpp @@ -115,7 +115,6 @@ class basic_registry { if(auto &&pdata = pools[index]; !pdata.pool) { pdata.pool.reset(new storage_type()); pdata.poly = std::ref(*static_cast *>(pdata.pool.get())); - pdata.pool->payload(this); } return static_cast *>(pools[index].pool.get()); @@ -186,38 +185,11 @@ public: /*! @brief Default constructor. */ basic_registry() = default; - /** - * @brief Move constructor. - * @param other The instance to move from. - */ - basic_registry(basic_registry &&other) ENTT_NOEXCEPT - : vars(std::move(other.vars)), // we can't use {} here, thanks to GCC - pools{std::move(other.pools)}, - groups{std::move(other.groups)}, - entities{std::move(other.entities)}, - available{other.available} - { - rebind_pools(); - } + /*! @brief Default move constructor. */ + basic_registry(basic_registry &&) = default; - /** - * @brief Move assignment operator. - * @param other The instance to assign from. - * @return This registry. - */ - basic_registry & operator=(basic_registry &&other) ENTT_NOEXCEPT { - if(this != &other) { - vars = std::move(other.vars); - pools = std::move(other.pools); - groups = std::move(other.groups); - entities = std::move(other.entities); - available = other.available; - - rebind_pools(); - } - - return *this; - } + /*! @brief Default move assignment operator. @return This registry. */ + basic_registry & operator=(basic_registry &&) = default; /** * @brief Prepares a pool for the given type if required. @@ -573,7 +545,7 @@ public: template decltype(auto) emplace(const entity_type entity, Args &&... args) { ENTT_ASSERT(valid(entity)); - return assure()->emplace(entity, std::forward(args)...); + return assure()->emplace(*this, entity, std::forward(args)...); } /** @@ -590,7 +562,7 @@ public: template void insert(It first, It last, const Component &value = {}) { ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); - assure()->insert(first, last, value); + assure()->insert(*this, first, last, value); } /** @@ -610,7 +582,7 @@ public: void insert(EIt first, EIt last, CIt from, CIt to) { static_assert(std::is_constructible_v::value_type>, "Invalid value type"); ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); - assure()->insert(first, last, from, to); + assure()->insert(*this, first, last, from, to); } /** @@ -639,8 +611,8 @@ public: auto *cpool = assure(); return cpool->contains(entity) - ? cpool->patch(entity, [&args...](auto &... curr) { ((curr = Component{std::forward(args)...}), ...); }) - : cpool->emplace(entity, std::forward(args)...); + ? cpool->patch(*this, entity, [&args...](auto &... curr) { ((curr = Component{std::forward(args)...}), ...); }) + : cpool->emplace(*this, entity, std::forward(args)...); } /** @@ -670,7 +642,7 @@ public: template decltype(auto) patch(const entity_type entity, Func &&... func) { ENTT_ASSERT(valid(entity)); - return assure()->patch(entity, std::forward(func)...); + return assure()->patch(*this, entity, std::forward(func)...); } /** @@ -692,7 +664,7 @@ public: */ template decltype(auto) replace(const entity_type entity, Args &&... args) { - return assure()->patch(entity, [&args...](auto &... curr) { ((curr = Component{std::forward(args)...}), ...); }); + return assure()->patch(*this, entity, [&args...](auto &... curr) { ((curr = Component{std::forward(args)...}), ...); }); } /** @@ -709,7 +681,7 @@ public: void remove(const entity_type entity) { ENTT_ASSERT(valid(entity)); static_assert(sizeof...(Component) > 0, "Provide one or more component types"); - (assure()->remove(entity), ...); + (assure()->remove(entity, this), ...); } /** @@ -726,7 +698,7 @@ public: void remove(It first, It last) { ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); static_assert(sizeof...(Component) > 0, "Provide one or more component types"); - (assure()->remove(first, last), ...); + (assure()->remove(first, last, this), ...); } /** @@ -751,8 +723,8 @@ public: size_type remove_if_exists(const entity_type entity) { ENTT_ASSERT(valid(entity)); - return ([entity](auto *cpool) { - return cpool->contains(entity) ? (cpool->remove(entity), true) : false; + return ([this, entity](auto *cpool) { + return cpool->contains(entity) ? (cpool->remove(entity, this), true) : false; }(assure()) + ... + size_type{}); } @@ -776,7 +748,7 @@ public: for(auto pos = pools.size(); pos; --pos) { if(auto &pdata = pools[pos-1]; pdata.pool && pdata.pool->contains(entity)) { - pdata.pool->remove(std::begin(wrap), std::end(wrap)); + pdata.pool->remove(std::begin(wrap), std::end(wrap), this); } } } @@ -874,7 +846,7 @@ public: [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&... args) { ENTT_ASSERT(valid(entity)); auto *cpool = assure(); - return cpool->contains(entity) ? cpool->get(entity) : cpool->emplace(entity, std::forward(args)...); + return cpool->contains(entity) ? cpool->get(entity) : cpool->emplace(*this, entity, std::forward(args)...); } /** @@ -924,14 +896,14 @@ public: if constexpr(sizeof...(Component) == 0) { for(auto pos = pools.size(); pos; --pos) { if(auto &pdata = pools[pos-1]; pdata.pool) { - pdata.pool->clear(); + pdata.pool->clear(this); } } each([this](const auto entity) { release_entity(entity, version(entity) + 1u); }); } else { - ([](auto *cpool) { - cpool->remove(cpool->basic_sparse_set::begin(), cpool->basic_sparse_set::end()); + ([this](auto *cpool) { + cpool->remove(cpool->basic_sparse_set::begin(), cpool->basic_sparse_set::end(), this); }(assure()), ...); } } diff --git a/src/entt/entity/sparse_set.hpp b/src/entt/entity/sparse_set.hpp index d8a288fb4..0b356a0b5 100644 --- a/src/entt/entity/sparse_set.hpp +++ b/src/entt/entity/sparse_set.hpp @@ -178,7 +178,7 @@ protected: virtual void swap_at(const std::size_t, const std::size_t) {} /*! @brief Attempts to remove an entity from the internal packed array. */ - virtual void swap_and_pop(const std::size_t) {} + virtual void swap_and_pop(const std::size_t, void *) {} public: /*! @brief Underlying entity identifier. */ @@ -432,13 +432,14 @@ public: * results in undefined behavior. * * @param entt A valid entity identifier. + * @param ud Optional user data that are forwarded as-is to derived classes. */ - void remove(const entity_type entt) { + void remove(const entity_type entt, void *ud = nullptr) { ENTT_ASSERT(contains(entt)); auto &ref = sparse[page(entt)][offset(entt)]; // last chance to use the entity for derived classes and mixins, if any - swap_and_pop(size_type{to_integral(ref)}); + swap_and_pop(size_type{to_integral(ref)}, ud); const auto other = packed.back(); sparse[page(other)][offset(other)] = ref; @@ -455,11 +456,12 @@ 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 - void remove(It first, It last) { + void remove(It first, It last, void *ud = nullptr) { for(; first != last; ++first) { - remove(*first); + remove(*first, ud); } } @@ -588,32 +590,17 @@ public: } } - /*! @brief Clears a sparse set. */ - void clear() ENTT_NOEXCEPT { - remove(begin(), end()); - } - /** - * @brief Returns the opaque payload associated with the sparse set, if any. - * @return The opaque payload associated with the sparse set, if any. + * @brief Clears a sparse set. + * @param ud Optional user data that are forwarded as-is to derived classes. */ - void * payload() const ENTT_NOEXCEPT { - return user_data; - } - - /** - * @brief Set an opaque payload, typically used to attach information that - * are useful to storage mixins. - * @param ud Opaque payload, a sparse set won't use this data in any case. - */ - void payload(void *ud) ENTT_NOEXCEPT { - user_data = ud; + void clear(void *ud = nullptr) ENTT_NOEXCEPT { + remove(begin(), end(), ud); } private: std::vector sparse; std::vector packed; - void *user_data{}; }; diff --git a/src/entt/entity/storage.hpp b/src/entt/entity/storage.hpp index 38d31bd20..8bd9012ac 100644 --- a/src/entt/entity/storage.hpp +++ b/src/entt/entity/storage.hpp @@ -178,7 +178,7 @@ protected: * @copybrief basic_sparse_set::swap_and_pop * @param pos A valid position of an entity within storage. */ - void swap_and_pop(const std::size_t pos) { + void swap_and_pop(const std::size_t pos, void *) { auto other = std::move(instances.back()); instances[pos] = std::move(other); instances.pop_back(); @@ -393,7 +393,7 @@ public: * @return A reference to the updated instance. */ template - decltype(auto) patch(const entity_type entity, [[maybe_unused]] Func &&... func) { + decltype(auto) patch(const entity_type entity, Func &&... func) { auto &&instance = instances[this->index(entity)]; (std::forward(func)(instance), ...); return instance; @@ -559,7 +559,7 @@ public: * @param func Valid function objects. */ template - void patch([[maybe_unused]] const entity_type entity, [[maybe_unused]] Func &&... func) { + void patch([[maybe_unused]] const entity_type entity, Func &&... func) { ENTT_ASSERT(this->contains(entity)); (std::forward(func)(), ...); } @@ -583,21 +583,76 @@ public: /** - * @brief Mixin type to use to add signal support to storage types. + * @brief Mixin type to use to wrap basic storage classes. * @tparam Type The type of the underlying storage. - * @tparam Owner Expected owner type. */ -template -class sigh_storage_mixin: public Type { - Owner & owner() ENTT_NOEXCEPT { - return *static_cast(this->payload()); +template +struct storage_adapter_mixin: Type { + static_assert(std::is_same_v>, "Invalid object type"); + + /*! @brief Type of the objects associated with the entities. */ + using value_type = typename Type::value_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + /*! @brief Storage category. */ + using storage_category = typename Type::storage_category; + + /** + * @brief Assigns entities to a storage. + * @tparam Args Types of arguments to use to construct the object. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the object. + * @return A reference to the newly created object. + */ + template + decltype(auto) emplace(basic_registry &, const entity_type entity, Args &&... args) { + return Type::emplace(entity, std::forward(args)...); } -protected: - /*! @copydoc basic_storage::swap_and_pop */ - void swap_and_pop(const std::size_t pos) { - destruction.publish(owner(), this->data()[pos]); - Type::swap_and_pop(pos); + /** + * @brief Assigns entities to a storage. + * @tparam It Type of input iterator. + * @tparam Args Types of arguments to use to construct the objects + * associated with 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 associated with + * the entities. + */ + template + void insert(basic_registry &, It first, It last, Args &&... args) { + Type::insert(first, last, std::forward(args)...); + } + + /** + * @brief Patches the given instance for an entity. + * @tparam Func Types of the function objects to invoke. + * @param entity A valid entity identifier. + * @param func Valid function objects. + * @return A reference to the patched instance. + */ + template + decltype(auto) patch(basic_registry &, const entity_type entity, Func &&... func) { + return Type::patch(entity, std::forward(func)...); + } +}; + + +/** + * @brief Mixin type to use to add signal support to storage types. + * @tparam Type The type of the underlying storage. + */ +template +class sigh_storage_mixin final: public Type { + /** + * @copybrief basic_sparse_set::swap_and_pop + * @param pos A valid position of an entity within storage. + * @param ud Optional user data that are forwarded as-is to derived classes. + */ + void swap_and_pop(const std::size_t pos, void *ud) final { + ENTT_ASSERT(ud != nullptr); + destruction.publish(*static_cast *>(ud), this->data()[pos]); + Type::swap_and_pop(pos, ud); } public: @@ -676,14 +731,15 @@ 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 entity A valid entity identifier. * @param args Parameters to use to initialize the object. * @return A reference to the newly created object. */ template - decltype(auto) emplace(const entity_type entity, Args &&... args) { + decltype(auto) emplace(basic_registry &owner, const entity_type entity, Args &&... args) { Type::emplace(entity, std::forward(args)...); - construction.publish(owner(), entity); + construction.publish(owner, entity); return this->get(entity); } @@ -692,18 +748,19 @@ public: * @tparam It Type of input iterator. * @tparam Args Types of arguments to use to construct the objects * associated with 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 associated with * the entities. */ template - void insert(It first, It last, Args &&... args) { + void insert(basic_registry &owner, It first, It last, Args &&... args) { Type::insert(first, last, std::forward(args)...); if(!construction.empty()) { for(; first != last; ++first) { - construction.publish(owner(), *first); + construction.publish(owner, *first); } } } @@ -711,14 +768,15 @@ public: /** * @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 entity A valid entity identifier. * @param func Valid function objects. * @return A reference to the patched instance. */ template - decltype(auto) patch(const entity_type entity, [[maybe_unused]] Func &&... func) { + decltype(auto) patch(basic_registry &owner, const entity_type entity, Func &&... func) { Type::patch(entity, std::forward(func)...); - update.publish(owner(), entity); + update.publish(owner, entity); return this->get(entity); } @@ -745,7 +803,7 @@ private: template struct storage_traits { /*! @brief Resulting type after component-to-storage conversion. */ - using storage_type = sigh_storage_mixin, entt::basic_registry>; + using storage_type = sigh_storage_mixin>; }; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 922d98410..7e326436a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -97,6 +97,7 @@ endif() if(ENTT_BUILD_EXAMPLE) SETUP_BASIC_TEST(custom_identifier example/custom_identifier.cpp) + SETUP_BASIC_TEST(signal_less example/signal_less.cpp) endif() # Test lib diff --git a/test/entt/entity/poly_storage.cpp b/test/entt/entity/poly_storage.cpp index b668385fe..977abe67f 100644 --- a/test/entt/entity/poly_storage.cpp +++ b/test/entt/entity/poly_storage.cpp @@ -11,8 +11,8 @@ template struct PolyStorage: entt::type_list_cat_t< decltype(as_type_list(std::declval>())), entt::type_list< - void(const Entity *, const Entity *), - void(const Entity, const void *), + void(const Entity *, const Entity *, void *), + void(entt::basic_registry &, const Entity, const void *), const void *(const Entity) const, void(entt::basic_registry &) const > @@ -24,12 +24,12 @@ struct PolyStorage: entt::type_list_cat_t< struct type: entt::Storage::template type { static constexpr auto base = std::tuple_size_v>::type>; - void remove(const entity_type *first, const entity_type *last) { - entt::poly_call(*this, first, last); + void remove(entt::basic_registry &owner, const entity_type *first, const entity_type *last) { + entt::poly_call(*this, first, last, &owner); } - void emplace(const entity_type entity, const void *instance) { - entt::poly_call(*this, entity, instance); + void emplace(entt::basic_registry &owner, const entity_type entity, const void *instance) { + entt::poly_call(*this, owner, entity, instance); } const void * get(const entity_type entity) const { @@ -43,8 +43,8 @@ struct PolyStorage: entt::type_list_cat_t< template struct members { - static void emplace(Type &self, const entity_type entity, const void *instance) { - self.emplace(entity, *static_cast(instance)); + static void emplace(Type &self, entt::basic_registry &owner, const entity_type entity, const void *instance) { + self.emplace(owner, entity, *static_cast(instance)); } static const typename Type::value_type * get(const Type &self, const entity_type entity) { @@ -86,7 +86,7 @@ TEST(PolyStorage, CopyEntity) { registry.visit(entity, [&](const auto info) { auto &&storage = registry.storage(info); - storage->emplace(other, storage->get(entity)); + storage->emplace(registry, other, storage->get(entity)); }); ASSERT_TRUE((registry.all_of(entity))); @@ -131,11 +131,11 @@ TEST(PolyStorage, Constness) { // cannot invoke remove on a const storage, let's copy the returned value auto cstorage = cregistry.storage(entt::type_id()); - ASSERT_DEATH(cstorage->remove(std::begin(entity), std::end(entity)), ".*"); + ASSERT_DEATH(cstorage->remove(registry, std::begin(entity), std::end(entity)), ".*"); ASSERT_TRUE(registry.all_of(entity[0])); auto &&storage = registry.storage(entt::type_id()); - storage->remove(std::begin(entity), std::end(entity)); + storage->remove(registry, std::begin(entity), std::end(entity)); ASSERT_FALSE(registry.all_of(entity[0])); } diff --git a/test/entt/entity/sparse_set.cpp b/test/entt/entity/sparse_set.cpp index 92b2059a5..b1e34bcd3 100644 --- a/test/entt/entity/sparse_set.cpp +++ b/test/entt/entity/sparse_set.cpp @@ -14,12 +14,6 @@ struct boxed_int { int value; }; TEST(SparseSet, Functionalities) { entt::sparse_set set; - ASSERT_EQ(set.payload(), nullptr); - - set.payload(&set); - - ASSERT_EQ(set.payload(), &set); - set.reserve(42); ASSERT_EQ(set.capacity(), 42u); @@ -84,8 +78,6 @@ TEST(SparseSet, Functionalities) { ASSERT_EQ(other.begin(), other.end()); ASSERT_FALSE(other.contains(entt::entity{0})); ASSERT_FALSE(other.contains(entt::entity{42})); - - ASSERT_EQ(set.payload(), &set); } TEST(SparseSet, Pagination) { diff --git a/test/example/signal_less.cpp b/test/example/signal_less.cpp new file mode 100644 index 000000000..48832fb3b --- /dev/null +++ b/test/example/signal_less.cpp @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include + +template +struct entt::storage_traits { + // no signal regardless of component type ... + using storage_type = storage_adapter_mixin>; +}; + +template +struct entt::storage_traits { + // ... unless it's char, because yes. + using storage_type = sigh_storage_mixin>; +}; + +template +struct has_on_construct: std::false_type {}; + +template +struct has_on_construct::storage_type::on_construct)>>: std::true_type {}; + +template +inline constexpr auto has_on_construct_v = has_on_construct::value; + +TEST(Example, SignalLess) { + // invoking registry::on_construct is a compile-time error + static_assert(!has_on_construct_v); + static_assert(has_on_construct_v); + + entt::registry registry; + const entt::entity entity[1u]{registry.create()}; + + // literally a test for storage_adapter_mixin + registry.emplace(entity[0], 0); + registry.remove(entity[0]); + registry.insert(std::begin(entity), std::end(entity), 3); + registry.patch(entity[0], [](auto &value) { value = 42; }); + + ASSERT_EQ(registry.get(entity[0]), 42); +}