Bladeren bron

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

Michele Caini 4 jaren geleden
bovenliggende
commit
e376a53b09

+ 1 - 0
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>

+ 49 - 20
src/entt/entity/registry.hpp

@@ -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 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));
+        }
 
-    /*! @brief Default move assignment operator. @return This registry. */
-    basic_registry &operator=(basic_registry &&) = default;
+        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(), ...);
         }
     }
 

+ 22 - 54
src/entt/entity/sparse_set.hpp

@@ -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;
 };

+ 38 - 88
src/entt/entity/storage.hpp

@@ -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{};
 };
 
 /**

+ 11 - 11
test/entt/entity/poly_storage.cpp

@@ -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]));
 }

+ 30 - 24
test/entt/entity/sigh_storage_mixin.cpp

@@ -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], &registry);
-    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], &registry);
-    pool.erase(entities[1u], &registry);
+    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), &registry);
+    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], &registry);
+    base.erase(entities[1u]);
 
     ASSERT_EQ(on_construct.value, 4);
     ASSERT_EQ(on_destroy.value, 3);
     ASSERT_FALSE(pool.empty());
 
-    base.erase(entities[0u], &registry);
+    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), &registry);
+    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], &registry);
-    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], &registry);
-    pool.erase(entities[1u], &registry);
+    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), &registry);
+    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], &registry);
+    base.erase(entities[1u]);
 
     ASSERT_EQ(on_construct.value, 4);
     ASSERT_EQ(on_destroy.value, 3);
     ASSERT_FALSE(pool.empty());
 
-    base.erase(entities[0u], &registry);
+    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), &registry);
+    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], &registry), "");
+    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], &registry);
+    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), &registry), "");
+    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), &registry);
+    pool.erase(std::begin(entities), std::end(entities));
 
     ASSERT_EQ(on_construct.value, 3);
     ASSERT_EQ(on_destroy.value, 3);

+ 2 - 29
test/entt/entity/sparse_set.cpp

@@ -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};

+ 3 - 3
test/entt/entity/view.cpp

@@ -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));

+ 1 - 1
test/example/signal_less.cpp

@@ -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>