Pārlūkot izejas kodu

review: prototype (#89)

Michele Caini 7 gadi atpakaļ
vecāks
revīzija
e07128760e

+ 13 - 9
README.md

@@ -1057,16 +1057,15 @@ how many prototypes they want, each one initialized differently from the others.
 The following is an example of use of a prototype:
 The following is an example of use of a prototype:
 
 
 ```cpp
 ```cpp
-entt::DefaultPrototype prototype;
+entt::DefaultRegistry registry;
+entt::DefaultPrototype prototype{registry};
 
 
 prototype.set<Position>(100.f, 100.f);
 prototype.set<Position>(100.f, 100.f);
 prototype.set<Velocity>(0.f, 0.f);
 prototype.set<Velocity>(0.f, 0.f);
 
 
 // ...
 // ...
 
 
-entt::DefaultRegistry registry;
-
-const auto entity = prototype(registry);
+const auto entity = prototype();
 ```
 ```
 
 
 To assign and remove components from a prototype, it offers two dedicated member
 To assign and remove components from a prototype, it offers two dedicated member
@@ -1079,21 +1078,21 @@ Creating an entity from a prototype is straightforward:
 * To create a new entity from scratch and assign it a prototype, this is the way
 * To create a new entity from scratch and assign it a prototype, this is the way
   to go:
   to go:
   ```cpp
   ```cpp
-  const auto entity = prototype(registry);
+  const auto entity = prototype();
   ```
   ```
   It is equivalent to the following invokation:
   It is equivalent to the following invokation:
   ```cpp
   ```cpp
-  const auto entity = prototype.create(registry);
+  const auto entity = prototype.create();
   ```
   ```
 
 
 * In case we want to initialize an already existing entity, we can provide the
 * In case we want to initialize an already existing entity, we can provide the
   `operator()` directly with the entity identifier:
   `operator()` directly with the entity identifier:
   ```cpp
   ```cpp
-  prototype(registry, entity);
+  prototype(entity);
   ```
   ```
   It is equivalent to the following invokation:
   It is equivalent to the following invokation:
   ```cpp
   ```cpp
-  prototype.assign(registry, entity);
+  prototype.assign(entity);
   ```
   ```
   Note that existing components aren't overwritten in this case. Only those
   Note that existing components aren't overwritten in this case. Only those
   components that the entity doesn't own yet are copied over. All the other
   components that the entity doesn't own yet are copied over. All the other
@@ -1102,9 +1101,14 @@ Creating an entity from a prototype is straightforward:
 * Finally, to assign or replace all the components for an entity, thus
 * Finally, to assign or replace all the components for an entity, thus
   overwriting existing ones:
   overwriting existing ones:
   ```cpp
   ```cpp
-  prototype.accommodate(registry, entity);
+  prototype.accommodate(entity);
   ```
   ```
 
 
+In the examples above, the prototype uses its underlying registry to create
+entities and components both for its purposes and when it's cloned. To use a
+different repository to clone a prototype, all the member functions accept also
+a reference to a valid registry as a first argument.
+
 Prototypes are a very useful tool that can save a lot of typing sometimes.
 Prototypes are a very useful tool that can save a lot of typing sometimes.
 Furthermore, the codebase may be easier to maintain, since updating a prototype
 Furthermore, the codebase may be easier to maintain, since updating a prototype
 is much less error prone than jumping around in the codebase to update all the
 is much less error prone than jumping around in the codebase to update all the

+ 217 - 90
src/entt/entity/prototype.hpp

@@ -3,11 +3,10 @@
 
 
 
 
 #include <tuple>
 #include <tuple>
-#include <memory>
-#include <vector>
 #include <utility>
 #include <utility>
 #include <cstddef>
 #include <cstddef>
-#include <algorithm>
+#include <type_traits>
+#include <unordered_map>
 #include "registry.hpp"
 #include "registry.hpp"
 
 
 
 
@@ -24,43 +23,28 @@ namespace entt {
  * entities of a registry at once.
  * entities of a registry at once.
  *
  *
  * @note
  * @note
- * Components used along with prototypes must be copy constructible.
+ * Components used along with prototypes must be copy constructible. Prototypes
+ * wrap component types with custom types, so they do not interfere with other
+ * users of the registry they were built with.
+ *
+ * @warning
+ * Prototypes directly use their underlying registries to store entities and
+ * components for their purposes. Users must ensure that the lifetime of a
+ * registry and its contents exceed that of the prototypes that use it.
  *
  *
  * @tparam Entity A valid entity type (see entt_traits for more details).
  * @tparam Entity A valid entity type (see entt_traits for more details).
  */
  */
 template<typename Entity>
 template<typename Entity>
-class Prototype {
+class Prototype final {
+    using fn_type = void(*)(const Prototype &, Registry<Entity> &, const Entity);
     using component_type = typename Registry<Entity>::component_type;
     using component_type = typename Registry<Entity>::component_type;
-    using fn_type = void(*)(Registry<Entity> &, const Entity, const void *);
-    using deleter_type = void(*)(void *);
-    using ptr_type = std::unique_ptr<void, deleter_type>;
-
-    template<typename Component>
-    static void accommodate(Registry<Entity> &registry, const Entity entity, const void *component) {
-        const auto &ref = *static_cast<const Component *>(component);
-        registry.template accommodate<Component>(entity, ref);
-    }
 
 
     template<typename Component>
     template<typename Component>
-    static void assign(Registry<Entity> &registry, const Entity entity, const void *component) {
-        if(!registry.template has<Component>(entity)) {
-            const auto &ref = *static_cast<const Component *>(component);
-            registry.template assign<Component>(entity, ref);
-        }
-    }
+    struct Wrapper { Component component; };
 
 
-    struct Handler final {
-        Handler(ptr_type component, const fn_type accommodate, const fn_type assign, const component_type type)
-            : component{std::move(component)},
-              accommodate{accommodate},
-              assign{assign},
-              type{type}
-        {}
-
-        ptr_type component{nullptr, +[](void *) {}};
-        fn_type accommodate{nullptr};
-        fn_type assign{nullptr};
-        component_type type;
+    struct Handler {
+        fn_type accommodate;
+        fn_type assign;
     };
     };
 
 
 public:
 public:
@@ -71,6 +55,22 @@ public:
     /*! @brief Unsigned integer type. */
     /*! @brief Unsigned integer type. */
     using size_type = std::size_t;
     using size_type = std::size_t;
 
 
+    /**
+     * @brief Constructs a prototype that is bound to a given registry.
+     * @param registry A valid reference to a registry.
+     */
+    Prototype(Registry<Entity> &registry)
+        : registry{registry},
+          entity{registry.create()}
+    {}
+
+    /**
+     * @brief Releases all its resources.
+     */
+    ~Prototype() {
+        registry.destroy(entity);
+    }
+
     /**
     /**
      * @brief Assigns to or replaces the given component of a prototype.
      * @brief Assigns to or replaces the given component of a prototype.
      * @tparam Component Type of component to assign or replace.
      * @tparam Component Type of component to assign or replace.
@@ -80,22 +80,21 @@ public:
      */
      */
     template<typename Component, typename... Args>
     template<typename Component, typename... Args>
     Component & set(Args &&... args) {
     Component & set(Args &&... args) {
-        const auto ctype = registry_type::template type<Component>();
-
-        auto it = std::find_if(handlers.begin(), handlers.end(), [ctype](const auto &handler) {
-            return handler.type == ctype;
-        });
-
-        const auto deleter = +[](void *component) { delete static_cast<Component *>(component); };
-        ptr_type component{new Component{std::forward<Args>(args)...}, deleter};
+        fn_type accommodate = [](const Prototype &prototype, Registry<Entity> &other, const Entity dst) {
+            const auto &wrapper = prototype.registry.template get<Wrapper<Component>>(prototype.entity);
+            other.template accommodate<Component>(dst, wrapper.component);
+        };
 
 
-        if(it == handlers.cend()) {
-            handlers.emplace_back(std::move(component), &Prototype::accommodate<Component>, &Prototype::assign<Component>, ctype);
-        } else {
-            it->component = std::move(component);
-        }
+        fn_type assign = [](const Prototype &prototype, Registry<Entity> &other, const Entity dst) {
+            if(!other.template has<Component>(dst)) {
+                const auto &wrapper = prototype.registry.template get<Wrapper<Component>>(prototype.entity);
+                other.template accommodate<Component>(dst, wrapper.component);
+            }
+        };
 
 
-        return get<Component>();
+        handlers[registry.template type<Component>()] = Handler{accommodate, assign};
+        auto &wrapper = registry.template accommodate<Wrapper<Component>>(entity, Component{std::forward<Args>(args)...});
+        return wrapper.component;
     }
     }
 
 
     /**
     /**
@@ -104,9 +103,8 @@ public:
      */
      */
     template<typename Component>
     template<typename Component>
     void unset() ENTT_NOEXCEPT {
     void unset() ENTT_NOEXCEPT {
-        handlers.erase(std::remove_if(handlers.begin(), handlers.end(), [](const auto &handler) {
-            return handler.type == registry_type::template type<Component>();
-        }), handlers.end());
+        registry.template reset<Wrapper<Component>>(entity);
+        handlers.erase(registry.template type<Component>());
     }
     }
 
 
     /**
     /**
@@ -116,17 +114,7 @@ public:
      */
      */
     template<typename... Component>
     template<typename... Component>
     bool has() const ENTT_NOEXCEPT {
     bool has() const ENTT_NOEXCEPT {
-        auto found = [this](const auto ctype) {
-            return std::find_if(handlers.cbegin(), handlers.cend(), [ctype](const auto &handler) {
-                return handler.type == ctype;
-            }) != handlers.cend();
-        };
-
-        bool all = true;
-        using accumulator_type = bool[];
-        accumulator_type accumulator = { all, (all = all && found(registry_type::template type<Component>()))... };
-        (void)accumulator;
-        return all;
+        return registry.template has<Wrapper<Component>...>(entity);
     }
     }
 
 
     /**
     /**
@@ -143,13 +131,7 @@ public:
      */
      */
     template<typename Component>
     template<typename Component>
     const Component & get() const ENTT_NOEXCEPT {
     const Component & get() const ENTT_NOEXCEPT {
-        assert(has<Component>());
-
-        auto it = std::find_if(handlers.cbegin(), handlers.cend(), [](const auto &handler) {
-            return handler.type == registry_type::template type<Component>();
-        });
-
-        return *static_cast<Component *>(it->component.get());
+        return registry.template get<Wrapper<Component>>(entity).component;
     }
     }
 
 
     /**
     /**
@@ -182,7 +164,7 @@ public:
      * @return References to the components owned by the prototype.
      * @return References to the components owned by the prototype.
      */
      */
     template<typename... Component>
     template<typename... Component>
-    std::enable_if_t<(sizeof...(Component) > 1), std::tuple<const Component &...>>
+    inline std::enable_if_t<(sizeof...(Component) > 1), std::tuple<const Component &...>>
     get() const ENTT_NOEXCEPT {
     get() const ENTT_NOEXCEPT {
         return std::tuple<const Component &...>{get<Component>()...};
         return std::tuple<const Component &...>{get<Component>()...};
     }
     }
@@ -200,7 +182,7 @@ public:
      * @return References to the components owned by the prototype.
      * @return References to the components owned by the prototype.
      */
      */
     template<typename... Component>
     template<typename... Component>
-    std::enable_if_t<(sizeof...(Component) > 1), std::tuple<Component &...>>
+    inline std::enable_if_t<(sizeof...(Component) > 1), std::tuple<Component &...>>
     get() ENTT_NOEXCEPT {
     get() ENTT_NOEXCEPT {
         return std::tuple<Component &...>{get<Component>()...};
         return std::tuple<Component &...>{get<Component>()...};
     }
     }
@@ -215,10 +197,38 @@ public:
      * prototype(registry, entity);
      * prototype(registry, entity);
      * @endcode
      * @endcode
      *
      *
-     * @param registry A valid reference to a registry.
+     * @note
+     * The registry may or may not be different from the one already used by
+     * the prototype. There is also an overload that directly uses the
+     * underlying registry.
+     *
+     * @param other A valid reference to a registry.
+     * @return A valid entity identifier.
+     */
+    entity_type create(registry_type &other) const {
+        const auto entity = other.create();
+        assign(other, entity);
+        return entity;
+    }
+
+    /**
+     * @brief Creates a new entity using a given prototype.
+     *
+     * Utility shortcut, equivalent to the following snippet:
+     *
+     * @code{.cpp}
+     * const auto entity = registry.create();
+     * prototype(entity);
+     * @endcode
+     *
+     * @note
+     * This overload directly uses the underlying registry as a working space.
+     * Therefore, the components of the prototype and of the entity will share
+     * the same registry.
+     *
      * @return A valid entity identifier.
      * @return A valid entity identifier.
      */
      */
-    entity_type create(registry_type &registry) {
+    entity_type create() const {
         const auto entity = registry.create();
         const auto entity = registry.create();
         assign(registry, entity);
         assign(registry, entity);
         return entity;
         return entity;
@@ -232,18 +242,49 @@ public:
      * In other words, only those components that the entity doesn't own yet are
      * In other words, only those components that the entity doesn't own yet are
      * copied over. All the other components remain unchanged.
      * copied over. All the other components remain unchanged.
      *
      *
+     * @note
+     * The registry may or may not be different from the one already used by
+     * the prototype. There is also an overload that directly uses the
+     * underlying registry.
+     *
      * @warning
      * @warning
      * Attempting to use an invalid entity results in undefined behavior.<br/>
      * Attempting to use an invalid entity results in undefined behavior.<br/>
      * An assertion will abort the execution at runtime in debug mode in case of
      * An assertion will abort the execution at runtime in debug mode in case of
      * invalid entity.
      * invalid entity.
      *
      *
-     * @param registry A valid reference to a registry.
-     * @param entity A valid entity identifier.
+     * @param other A valid reference to a registry.
+     * @param dst A valid entity identifier.
+     */
+    void assign(registry_type &other, const entity_type dst) const {
+        for(auto &handler: handlers) {
+            handler.second.assign(*this, other, dst);
+        }
+    }
+
+    /**
+     * @brief Assigns the components of a prototype to a given entity.
+     *
+     * Assigning a prototype to an entity won't overwrite existing components
+     * under any circumstances.<br/>
+     * In other words, only those components that the entity doesn't own yet are
+     * copied over. All the other components remain unchanged.
+     *
+     * @note
+     * This overload directly uses the underlying registry as a working space.
+     * Therefore, the components of the prototype and of the entity will share
+     * the same registry.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode in case of
+     * invalid entity.
+     *
+     * @param dst A valid entity identifier.
      */
      */
-    void assign(registry_type &registry, const entity_type entity) {
-        std::for_each(handlers.begin(), handlers.end(), [&registry, entity](auto &&handler) {
-            handler.assign(registry, entity, handler.component.get());
-        });
+    void assign(const entity_type dst) const {
+        for(auto &handler: handlers) {
+            handler.second.assign(*this, registry, dst);
+        }
     }
     }
 
 
     /**
     /**
@@ -252,18 +293,47 @@ public:
      * Existing components are overwritten, if any. All the other components
      * Existing components are overwritten, if any. All the other components
      * will be copied over to the target entity.
      * will be copied over to the target entity.
      *
      *
+     * @note
+     * The registry may or may not be different from the one already used by
+     * the prototype. There is also an overload that directly uses the
+     * underlying registry.
+     *
      * @warning
      * @warning
      * Attempting to use an invalid entity results in undefined behavior.<br/>
      * Attempting to use an invalid entity results in undefined behavior.<br/>
      * An assertion will abort the execution at runtime in debug mode in case of
      * An assertion will abort the execution at runtime in debug mode in case of
      * invalid entity.
      * invalid entity.
      *
      *
-     * @param registry A valid reference to a registry.
-     * @param entity A valid entity identifier.
+     * @param other A valid reference to a registry.
+     * @param dst A valid entity identifier.
      */
      */
-    void accommodate(registry_type &registry, const entity_type entity) {
-        std::for_each(handlers.begin(), handlers.end(), [&registry, entity](auto &&handler) {
-            handler.accommodate(registry, entity, handler.component.get());
-        });
+    void accommodate(registry_type &other, const entity_type dst) const {
+        for(auto &handler: handlers) {
+            handler.second.accommodate(*this, other, dst);
+        }
+    }
+
+    /**
+     * @brief Assigns or replaces the components of a prototype for an entity.
+     *
+     * Existing components are overwritten, if any. All the other components
+     * will be copied over to the target entity.
+     *
+     * @note
+     * This overload directly uses the underlying registry as a working space.
+     * Therefore, the components of the prototype and of the entity will share
+     * the same registry.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode in case of
+     * invalid entity.
+     *
+     * @param dst A valid entity identifier.
+     */
+    void accommodate(const entity_type dst) const {
+        for(auto &handler: handlers) {
+            handler.second.accommodate(*this, registry, dst);
+        }
     }
     }
 
 
     /**
     /**
@@ -274,16 +344,45 @@ public:
      * In other words, only the components that the entity doesn't own yet are
      * In other words, only the components that the entity doesn't own yet are
      * copied over. All the other components remain unchanged.
      * copied over. All the other components remain unchanged.
      *
      *
+     * @note
+     * The registry may or may not be different from the one already used by
+     * the prototype. There is also an overload that directly uses the
+     * underlying registry.
+     *
      * @warning
      * @warning
      * Attempting to use an invalid entity results in undefined behavior.<br/>
      * Attempting to use an invalid entity results in undefined behavior.<br/>
      * An assertion will abort the execution at runtime in debug mode in case of
      * An assertion will abort the execution at runtime in debug mode in case of
      * invalid entity.
      * invalid entity.
      *
      *
-     * @param registry A valid reference to a registry.
-     * @param entity A valid entity identifier.
+     * @param other A valid reference to a registry.
+     * @param dst A valid entity identifier.
      */
      */
-    inline void operator()(registry_type &registry, const entity_type entity) ENTT_NOEXCEPT {
-        assign(registry, entity);
+    inline void operator()(registry_type &other, const entity_type dst) const ENTT_NOEXCEPT {
+        assign(other, dst);
+    }
+
+    /**
+     * @brief Assigns the components of a prototype to an entity.
+     *
+     * Assigning a prototype to an entity won't overwrite existing components
+     * under any circumstances.<br/>
+     * In other words, only the components that the entity doesn't own yet are
+     * copied over. All the other components remain unchanged.
+     *
+     * @note
+     * This overload directly uses the underlying registry as a working space.
+     * Therefore, the components of the prototype and of the entity will share
+     * the same registry.
+     *
+     * @warning
+     * Attempting to use an invalid entity results in undefined behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode in case of
+     * invalid entity.
+     *
+     * @param dst A valid entity identifier.
+     */
+    inline void operator()(const entity_type dst) const ENTT_NOEXCEPT {
+        assign(registry, dst);
     }
     }
 
 
     /**
     /**
@@ -296,15 +395,43 @@ public:
      * prototype(registry, entity);
      * prototype(registry, entity);
      * @endcode
      * @endcode
      *
      *
-     * @param registry A valid reference to a registry.
+     * @note
+     * The registry may or may not be different from the one already used by
+     * the prototype. There is also an overload that directly uses the
+     * underlying registry.
+     *
+     * @param other A valid reference to a registry.
+     * @return A valid entity identifier.
+     */
+    inline entity_type operator()(registry_type &other) const ENTT_NOEXCEPT {
+        return create(other);
+    }
+
+    /**
+     * @brief Creates a new entity using a given prototype.
+     *
+     * Utility shortcut, equivalent to the following snippet:
+     *
+     * @code{.cpp}
+     * const auto entity = registry.create();
+     * prototype(entity);
+     * @endcode
+     *
+     * @note
+     * This overload directly uses the underlying registry as a working space.
+     * Therefore, the components of the prototype and of the entity will share
+     * the same registry.
+     *
      * @return A valid entity identifier.
      * @return A valid entity identifier.
      */
      */
-    inline entity_type operator()(registry_type &registry) ENTT_NOEXCEPT {
+    inline entity_type operator()() const ENTT_NOEXCEPT {
         return create(registry);
         return create(registry);
     }
     }
 
 
 private:
 private:
-    std::vector<Handler> handlers;
+    std::unordered_map<component_type, Handler> handlers;
+    Registry<Entity> &registry;
+    entity_type entity;
 };
 };
 
 
 
 

+ 2 - 2
src/entt/entity/registry.hpp

@@ -756,7 +756,7 @@ public:
      * @return References to the components owned by the entity.
      * @return References to the components owned by the entity.
      */
      */
     template<typename... Component>
     template<typename... Component>
-    std::enable_if_t<(sizeof...(Component) > 1), std::tuple<const Component &...>>
+    inline std::enable_if_t<(sizeof...(Component) > 1), std::tuple<const Component &...>>
     get(const entity_type entity) const ENTT_NOEXCEPT {
     get(const entity_type entity) const ENTT_NOEXCEPT {
         return std::tuple<const Component &...>{get<Component>(entity)...};
         return std::tuple<const Component &...>{get<Component>(entity)...};
     }
     }
@@ -776,7 +776,7 @@ public:
      * @return References to the components owned by the entity.
      * @return References to the components owned by the entity.
      */
      */
     template<typename... Component>
     template<typename... Component>
-    std::enable_if_t<(sizeof...(Component) > 1), std::tuple<Component &...>>
+    inline std::enable_if_t<(sizeof...(Component) > 1), std::tuple<Component &...>>
     get(const entity_type entity) ENTT_NOEXCEPT {
     get(const entity_type entity) ENTT_NOEXCEPT {
         return std::tuple<Component &...>{get<Component>(entity)...};
         return std::tuple<Component &...>{get<Component>(entity)...};
     }
     }

+ 4 - 4
src/entt/entity/view.hpp

@@ -306,7 +306,7 @@ public:
      * @return The components assigned to the entity.
      * @return The components assigned to the entity.
      */
      */
     template<typename... Comp>
     template<typename... Comp>
-    std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<const Comp &...>>
+    inline std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<const Comp &...>>
     get(const entity_type entity) const ENTT_NOEXCEPT {
     get(const entity_type entity) const ENTT_NOEXCEPT {
         assert(contains(entity));
         assert(contains(entity));
         return std::tuple<const Comp &...>{get<Comp>(entity)...};
         return std::tuple<const Comp &...>{get<Comp>(entity)...};
@@ -330,7 +330,7 @@ public:
      * @return The components assigned to the entity.
      * @return The components assigned to the entity.
      */
      */
     template<typename... Comp>
     template<typename... Comp>
-    std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<Comp &...>>
+    inline std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<Comp &...>>
     get(const entity_type entity) ENTT_NOEXCEPT {
     get(const entity_type entity) ENTT_NOEXCEPT {
         assert(contains(entity));
         assert(contains(entity));
         return std::tuple<Comp &...>{get<Comp>(entity)...};
         return std::tuple<Comp &...>{get<Comp>(entity)...};
@@ -819,7 +819,7 @@ public:
      * @return The components assigned to the entity.
      * @return The components assigned to the entity.
      */
      */
     template<typename... Comp>
     template<typename... Comp>
-    std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<const Comp &...>>
+    inline std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<const Comp &...>>
     get(const entity_type entity) const ENTT_NOEXCEPT {
     get(const entity_type entity) const ENTT_NOEXCEPT {
         assert(contains(entity));
         assert(contains(entity));
         return std::tuple<const Comp &...>{get<Comp>(entity)...};
         return std::tuple<const Comp &...>{get<Comp>(entity)...};
@@ -843,7 +843,7 @@ public:
      * @return The components assigned to the entity.
      * @return The components assigned to the entity.
      */
      */
     template<typename... Comp>
     template<typename... Comp>
-    std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<Comp &...>>
+    inline std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<Comp &...>>
     get(const entity_type entity) ENTT_NOEXCEPT {
     get(const entity_type entity) ENTT_NOEXCEPT {
         assert(contains(entity));
         assert(contains(entity));
         return std::tuple<Comp &...>{get<Comp>(entity)...};
         return std::tuple<Comp &...>{get<Comp>(entity)...};

+ 75 - 3
test/entt/entity/prototype.cpp

@@ -2,13 +2,73 @@
 #include <entt/entity/prototype.hpp>
 #include <entt/entity/prototype.hpp>
 #include <entt/entity/registry.hpp>
 #include <entt/entity/registry.hpp>
 
 
-TEST(Prototype, Functionalities) {
+TEST(Prototype, SameRegistry) {
     entt::DefaultRegistry registry;
     entt::DefaultRegistry registry;
-    entt::DefaultPrototype prototype;
+    entt::DefaultPrototype prototype{registry};
     const auto &cprototype = prototype;
     const auto &cprototype = prototype;
 
 
+    ASSERT_FALSE(registry.empty());
     ASSERT_FALSE((prototype.has<int, char>()));
     ASSERT_FALSE((prototype.has<int, char>()));
+
+    ASSERT_EQ(prototype.set<int>(2), 2);
+    ASSERT_EQ(prototype.set<int>(3), 3);
+    ASSERT_EQ(prototype.set<char>('c'), 'c');
+
+    ASSERT_EQ(prototype.get<int>(), 3);
+    ASSERT_EQ(cprototype.get<char>(), 'c');
+    ASSERT_EQ(std::get<0>(prototype.get<int, char>()), 3);
+    ASSERT_EQ(std::get<1>(cprototype.get<int, char>()), 'c');
+
+    const auto e0 = prototype();
+
+    ASSERT_TRUE((prototype.has<int, char>()));
+    ASSERT_FALSE(registry.orphan(e0));
+
+    const auto e1 = prototype();
+    prototype(e0);
+
+    ASSERT_FALSE(registry.orphan(e0));
+    ASSERT_FALSE(registry.orphan(e1));
+
+    ASSERT_TRUE((registry.has<int, char>(e0)));
+    ASSERT_TRUE((registry.has<int, char>(e1)));
+
+    registry.remove<int>(e0);
+    registry.remove<int>(e1);
+    prototype.unset<int>();
+
+    ASSERT_FALSE((prototype.has<int, char>()));
+    ASSERT_FALSE((prototype.has<int>()));
+    ASSERT_TRUE((prototype.has<char>()));
+
+    prototype(e0);
+    prototype(e1);
+
+    ASSERT_FALSE(registry.has<int>(e0));
+    ASSERT_FALSE(registry.has<int>(e1));
+
+    ASSERT_EQ(registry.get<char>(e0), 'c');
+    ASSERT_EQ(registry.get<char>(e1), 'c');
+
+    registry.get<char>(e0) = '*';
+    prototype.assign(e0);
+
+    ASSERT_EQ(registry.get<char>(e0), '*');
+
+    registry.get<char>(e1) = '*';
+    prototype.accommodate(e1);
+
+    ASSERT_EQ(registry.get<char>(e1), 'c');
+}
+
+TEST(Prototype, OtherRegistry) {
+    entt::DefaultRegistry registry;
+    entt::DefaultRegistry repository;
+    entt::DefaultPrototype prototype{repository};
+    const auto &cprototype = prototype;
+
     ASSERT_TRUE(registry.empty());
     ASSERT_TRUE(registry.empty());
+    ASSERT_FALSE((prototype.has<int, char>()));
 
 
     ASSERT_EQ(prototype.set<int>(2), 2);
     ASSERT_EQ(prototype.set<int>(2), 2);
     ASSERT_EQ(prototype.set<int>(3), 3);
     ASSERT_EQ(prototype.set<int>(3), 3);
@@ -23,7 +83,6 @@ TEST(Prototype, Functionalities) {
 
 
     ASSERT_TRUE((prototype.has<int, char>()));
     ASSERT_TRUE((prototype.has<int, char>()));
     ASSERT_FALSE(registry.orphan(e0));
     ASSERT_FALSE(registry.orphan(e0));
-    ASSERT_FALSE(registry.empty());
 
 
     const auto e1 = prototype(registry);
     const auto e1 = prototype(registry);
     prototype(registry, e0);
     prototype(registry, e0);
@@ -61,3 +120,16 @@ TEST(Prototype, Functionalities) {
 
 
     ASSERT_EQ(registry.get<char>(e1), 'c');
     ASSERT_EQ(registry.get<char>(e1), 'c');
 }
 }
+
+TEST(Prototype, RAII) {
+    entt::DefaultRegistry registry;
+
+    {
+        entt::DefaultPrototype prototype{registry};
+        prototype.set<int>(0);
+
+        ASSERT_FALSE(registry.empty());
+    }
+
+    ASSERT_TRUE(registry.empty());
+}