Explorar el Código

more on save/restore (#67)

Michele Caini hace 7 años
padre
commit
808464f47d

+ 35 - 31
README.md

@@ -846,6 +846,21 @@ consequence of a couple of design choices from the past and in the present:
   internally and doesn't know at any time what types of components it contains.
   internally and doesn't know at any time what types of components it contains.
   Therefore being explicit at the call point is mandatory.
   Therefore being explicit at the call point is mandatory.
 
 
+There exists also another version of the `component` member function that
+accepts a range of entities to serialize. This version is a bit slower than the
+other one, mainly because it iterates the range of entities more than once for
+internal purposes. However, it can be used to filter out those entities that
+shouldn't be serialized for some reasons.<br/>
+As an example:
+
+```cpp
+const auto view = registry.view<Serialize>();
+OutputArchive output;
+
+registry.snapshot()
+    .component<AComponent, AnotherComponent>(output, view.cbegin(), view.cend());
+```
+
 The `tag` member function is similar to the previous one, apart from the fact
 The `tag` member function is similar to the previous one, apart from the fact
 that it works with tags and not with components.<br/>
 that it works with tags and not with components.<br/>
 Note also that both `component` and `tag` store items along with entities. It
 Note also that both `component` and `tag` store items along with entities. It
@@ -921,10 +936,8 @@ InputArchive input;
 
 
 loader.entities(input)
 loader.entities(input)
     .destroyed(input)
     .destroyed(input)
-    .component<AComponent, AnotherComponent>(input)
-    .component<DirtyComponent>(input, &DirtyComponent::parent, &DirtyComponent::child)
-    .tag<MyTag>(input)
-    .tag<DirtyTag>(input, &DirtyTag::container)
+    .component<AComponent, AnotherComponent, DirtyComponent>(input, &DirtyComponent::parent, &DirtyComponent::child)
+    .tag<MyTag, DirtyTag>(input, &DirtyTag::container)
     .orphans()
     .orphans()
     .shrink();
     .shrink();
 ```
 ```
@@ -945,20 +958,7 @@ and the tags specified and assign them to the right entities.<br/>
 In case the component or the tag contains entities itself (either as data
 In case the component or the tag contains entities itself (either as data
 members of type `entity_type` or as containers of entities), the loader can
 members of type `entity_type` or as containers of entities), the loader can
 update them automatically. To do that, it's enough to specify the data members
 update them automatically. To do that, it's enough to specify the data members
-to update as shown in the example. If the component or the tag was in the middle
-of the template parameter list during serialization, multiple commands are
-required during a restore:
-
-```cpp
-registry.snapshot().component<ASimpleComponent, AnotherSimpleComponent, AMoreComplexComponent, TheLastComponent>();
-
-// ...
-
-loader
-    .component<ASimpleComponent, AnotherSimpleComponent>(input)
-    .component<AMoreComplexComponent>(input, &AMoreComplexComponent::entity);
-    .component<TheLastComponent>(input);
-```
+to update as shown in the example.
 
 
 The `orphans` member function literally destroys those entities that have
 The `orphans` member function literally destroys those entities that have
 neither components nor tags after a restore. It has exactly the same purpose
 neither components nor tags after a restore. It has exactly the same purpose
@@ -972,7 +972,7 @@ snapshot, unless they know exactly what they are doing.
 
 
 Archives must publicly expose a predefined set of member functions. The API is
 Archives must publicly expose a predefined set of member functions. The API is
 straightforward and consists only of a group of function call operators that
 straightforward and consists only of a group of function call operators that
-are invoked by the registry.
+are invoked by the snapshot class and the loaders.
 
 
 In particular:
 In particular:
 
 
@@ -983,13 +983,15 @@ In particular:
   void operator()(Entity);
   void operator()(Entity);
   ```
   ```
 
 
-  Where `Entity` is the type of the entities used by the registry.<br/>
-  In addition, it must accept the types of both the components and the tags to
-  serialize. Therefore, given a type `T` (either a component or a tag), it must
-  contain a function call operator with the following signature:
+  Where `Entity` is the type of the entities used by the registry. Note that all
+  the member functions of the snapshot class make also an initial call to this
+  endpoint to save the _size_ of the set they are going to store.<br/>
+  In addition, an archive must accept a pair of entity and either component or
+  tag for each type to be serialized. Therefore, given a type `T`, the archive
+  must contain a function call operator with the following signature:
 
 
   ```cpp
   ```cpp
-  void operator()(const T &);
+  void operator()(Entity, const T &);
   ```
   ```
 
 
   The output archive can freely decide how to serialize the data. The register
   The output archive can freely decide how to serialize the data. The register
@@ -1004,17 +1006,19 @@ In particular:
 
 
   Where `Entity` is the type of the entities used by the registry. Each time the
   Where `Entity` is the type of the entities used by the registry. Each time the
   function is invoked, the archive must read the next element from the
   function is invoked, the archive must read the next element from the
-  underlying storage and copy it in the given variable.<br/>
-  In addition, it must accept the types of both the components and the tags to
-  restore. Therefore, given a type `T` (either a component or a tag), it must
-  contain a function call operator with the following signature:
+  underlying storage and copy it in the given variable. Note that all the member
+  functions of a loader class make also an initial call to this endpoint to read
+  the _size_ of the set they are going to load.<br/>
+  In addition, the archive must accept a pair of entity and either component or
+  tag for each type to be restored. Therefore, given a type `T`, the archive
+  must contain a function call operator with the following signature:
 
 
   ```cpp
   ```cpp
-  void operator()(T &);
+  void operator()(Entity &, T &);
   ```
   ```
 
 
-  Every time such an operator is invoked, the archive must read the next element
-  from the underlying storage and copy it in the given variable.
+  Every time such an operator is invoked, the archive must read the next
+  elements from the underlying storage and copy them in the given variables.
 
 
 #### One example to rule them all
 #### One example to rule them all
 
 

+ 0 - 1
TODO

@@ -6,7 +6,6 @@
 * define basic reactive systems (track entities to which component is attached, track entities from which component is removed, and so on)
 * define basic reactive systems (track entities to which component is attached, track entities from which component is removed, and so on)
 * define systems as composable mixins (initializazion, reactive, update, whatever) with flexible auto-detected arguments (registry, views, etc)
 * define systems as composable mixins (initializazion, reactive, update, whatever) with flexible auto-detected arguments (registry, views, etc)
 * create dedicated flat map based on types implementation (sort of "type map") for types to use within the registry and so on...
 * create dedicated flat map based on types implementation (sort of "type map") for types to use within the registry and so on...
-* does it worth it to add an optional functor to the member functions of snapshot so as to filter out instances and entities?
 * ease the assignment of tags as string (use a template class with a non-type template parameter behind the scene)
 * ease the assignment of tags as string (use a template class with a non-type template parameter behind the scene)
 * improve CMake interface, see mail from Malte
 * improve CMake interface, see mail from Malte
 * is registry/utility.hpp really required?
 * is registry/utility.hpp really required?

+ 0 - 1
src/entt/entity/actor.hpp

@@ -6,7 +6,6 @@
 #include <utility>
 #include <utility>
 #include "../config/config.h"
 #include "../config/config.h"
 #include "registry.hpp"
 #include "registry.hpp"
-#include "utility.hpp"
 
 
 
 
 namespace entt {
 namespace entt {

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

@@ -1479,7 +1479,7 @@ public:
             return (next | (entities[next] & ~traits_type::entity_mask));
             return (next | (entities[next] & ~traits_type::entity_mask));
         };
         };
 
 
-        return { *this, seed, available, follow };
+        return { *this, seed, follow };
     }
     }
 
 
     /**
     /**

+ 176 - 165
src/entt/entity/snapshot.hpp

@@ -2,13 +2,13 @@
 #define ENTT_ENTITY_SNAPSHOT_HPP
 #define ENTT_ENTITY_SNAPSHOT_HPP
 
 
 
 
-#include <unordered_map>
-#include <algorithm>
+#include <array>
 #include <cstddef>
 #include <cstddef>
 #include <utility>
 #include <utility>
 #include <cassert>
 #include <cassert>
 #include <iterator>
 #include <iterator>
 #include <type_traits>
 #include <type_traits>
+#include <unordered_map>
 #include "../config/config.h"
 #include "../config/config.h"
 #include "entt_traits.hpp"
 #include "entt_traits.hpp"
 #include "utility.hpp"
 #include "utility.hpp"
@@ -41,38 +41,40 @@ class Snapshot final {
 
 
     using follow_fn_type = Entity(*)(const Registry<Entity> &, const Entity);
     using follow_fn_type = Entity(*)(const Registry<Entity> &, const Entity);
 
 
-    Snapshot(const Registry<Entity> &registry, Entity seed, std::size_t size, follow_fn_type follow) ENTT_NOEXCEPT
+    Snapshot(const Registry<Entity> &registry, Entity seed, follow_fn_type follow) ENTT_NOEXCEPT
         : registry{registry},
         : registry{registry},
           seed{seed},
           seed{seed},
-          size{size},
           follow{follow}
           follow{follow}
     {}
     {}
 
 
-    template<typename Component, typename Archive>
-    void get(Archive &archive, const Registry<Entity> &registry) {
-        const auto sz = registry.template size<Component>();
-        const auto *entities = registry.template data<Component>();
-
+    template<typename Component, typename Archive, typename It>
+    void get(Archive &archive, std::size_t sz, It first, It last) {
         archive(static_cast<Entity>(sz));
         archive(static_cast<Entity>(sz));
 
 
-        for(std::remove_const_t<decltype(sz)> i{}; i < sz; ++i) {
-            const auto entity = entities[i];
-            archive(entity);
-            archive(registry.template get<Component>(entity));
-        };
-    }
+        while(first != last) {
+            const auto entity = *(first++);
 
 
-    template<typename Tag, typename Archive>
-    void get(Archive &archive) {
-        const bool has = registry.template has<Tag>();
+            if(registry.template has<Component>(entity)) {
+                archive(entity, registry.template get<Component>(entity));
+            }
+        }
+    }
 
 
-        // numerical length is forced for tags to facilitate loading
-        archive(has ? Entity(1): Entity{});
+    template<typename... Component, typename Archive, typename It, std::size_t... Indexes>
+    void component(Archive &archive, It first, It last, std::index_sequence<Indexes...>) {
+        std::array<std::size_t, sizeof...(Indexes)> size{};
+        auto begin = first;
 
 
-        if(has) {
-            archive(registry.template attachee<Tag>());
-            archive(registry.template get<Tag>());
+        while(begin != last) {
+            const auto entity = *(begin++);
+            using accumulator_type = std::size_t[];
+            accumulator_type accumulator = { (registry.template has<Component>(entity) ? ++size[Indexes] : size[Indexes])... };
+            (void)accumulator;
         }
         }
+
+        using accumulator_type = int[];
+        accumulator_type accumulator = { (get<Component>(archive, size[Indexes], first, last), 0)... };
+        (void)accumulator;
     }
     }
 
 
 public:
 public:
@@ -115,26 +117,49 @@ public:
      */
      */
     template<typename Archive>
     template<typename Archive>
     Snapshot & destroyed(Archive &archive) {
     Snapshot & destroyed(Archive &archive) {
+        auto size = registry.capacity() - registry.size();
         archive(static_cast<Entity>(size));
         archive(static_cast<Entity>(size));
+        auto curr = seed;
 
 
-        if(size) {
-            auto curr = seed;
+        for(; size; --size) {
             archive(curr);
             archive(curr);
-
-            for(auto i = size - 1; i; --i) {
-                curr = follow(registry, curr);
-                archive(curr);
-            }
+            curr = follow(registry, curr);
         }
         }
 
 
         return *this;
         return *this;
     }
     }
 
 
+    /**
+     * @brief Puts aside the given component.
+     *
+     * Each instance is serialized together with the entity to which it belongs.
+     * Entities are serialized along with their versions.
+     *
+     * @tparam Component Type of component to serialize.
+     * @tparam Archive Type of output archive.
+     * @param archive A valid reference to an output archive.
+     * @return An object of this type to continue creating the snapshot.
+     */
+    template<typename Component, typename Archive>
+    Snapshot & component(Archive &archive) {
+        const auto sz = registry.template size<Component>();
+        const auto *entities = registry.template data<Component>();
+
+        archive(static_cast<Entity>(sz));
+
+        for(std::remove_const_t<decltype(sz)> i{}; i < sz; ++i) {
+            const auto entity = entities[i];
+            archive(entity, registry.template get<Component>(entity));
+        };
+
+        return *this;
+    }
+
     /**
     /**
      * @brief Puts aside the given components.
      * @brief Puts aside the given components.
      *
      *
-     * Each component is serialized together with the entity to which it
-     * belongs. Entities are serialized along with their versions.
+     * Each instance is serialized together with the entity to which it belongs.
+     * Entities are serialized along with their versions.
      *
      *
      * @tparam Component Types of components to serialize.
      * @tparam Component Types of components to serialize.
      * @tparam Archive Type of output archive.
      * @tparam Archive Type of output archive.
@@ -142,17 +167,63 @@ public:
      * @return An object of this type to continue creating the snapshot.
      * @return An object of this type to continue creating the snapshot.
      */
      */
     template<typename... Component, typename Archive>
     template<typename... Component, typename Archive>
-    Snapshot & component(Archive &archive) {
+    std::enable_if_t<(sizeof...(Component) > 1), Snapshot &>
+    component(Archive &archive) {
         using accumulator_type = int[];
         using accumulator_type = int[];
-        accumulator_type accumulator = { 0, (get<Component>(archive, registry), 0)... };
+        accumulator_type accumulator = { 0, (component<Component>(archive), 0)... };
         (void)accumulator;
         (void)accumulator;
         return *this;
         return *this;
     }
     }
 
 
+    /**
+     * @brief Puts aside the given components for the entities in a range.
+     *
+     * Each instance is serialized together with the entity to which it belongs.
+     * Entities are serialized along with their versions.
+     *
+     * @tparam Component Types of components to serialize.
+     * @tparam Archive Type of output archive.
+     * @tparam It Type of input iterator.
+     * @param archive A valid reference to an output archive.
+     * @param first An iterator to the first element of the range to serialize.
+     * @param last An iterator past the last element of the range to serialize.
+     * @return An object of this type to continue creating the snapshot.
+     */
+    template<typename... Component, typename Archive, typename It>
+    Snapshot & component(Archive &archive, It first, It last) {
+        component<Component...>(archive, first, last, std::make_index_sequence<sizeof...(Component)>{});
+        return *this;
+    }
+
+    /**
+     * @brief Puts aside the given tag.
+     *
+     * Each instance is serialized together with the entity to which it belongs.
+     * Entities are serialized along with their versions.
+     *
+     * @tparam Tag Type of tag to serialize.
+     * @tparam Archive Type of output archive.
+     * @param archive A valid reference to an output archive.
+     * @return An object of this type to continue creating the snapshot.
+     */
+    template<typename Tag, typename Archive>
+    Snapshot & tag(Archive &archive) {
+        const bool has = registry.template has<Tag>();
+
+        // numerical length is forced for tags to facilitate loading
+        archive(has ? Entity(1): Entity{});
+
+        if(has) {
+            archive(registry.template attachee<Tag>(), registry.template get<Tag>());
+        }
+
+        return *this;
+    }
+
     /**
     /**
      * @brief Puts aside the given tags.
      * @brief Puts aside the given tags.
      *
      *
-     * Each tag is serialized together with the entity to which it belongs.
+     * Each instance is serialized together with the entity to which it belongs.
      * Entities are serialized along with their versions.
      * Entities are serialized along with their versions.
      *
      *
      * @tparam Tag Types of tags to serialize.
      * @tparam Tag Types of tags to serialize.
@@ -161,9 +232,10 @@ public:
      * @return An object of this type to continue creating the snapshot.
      * @return An object of this type to continue creating the snapshot.
      */
      */
     template<typename... Tag, typename Archive>
     template<typename... Tag, typename Archive>
-    Snapshot & tag(Archive &archive) {
+    std::enable_if_t<(sizeof...(Tag) > 1), Snapshot &>
+    tag(Archive &archive) {
         using accumulator_type = int[];
         using accumulator_type = int[];
-        accumulator_type accumulator = { 0, (get<Tag>(archive), 0)... };
+        accumulator_type accumulator = { 0, (tag<Tag>(archive), 0)... };
         (void)accumulator;
         (void)accumulator;
         return *this;
         return *this;
     }
     }
@@ -171,7 +243,6 @@ public:
 private:
 private:
     const Registry<Entity> &registry;
     const Registry<Entity> &registry;
     const Entity seed;
     const Entity seed;
-    const std::size_t size;
     follow_fn_type follow;
     follow_fn_type follow;
 };
 };
 
 
@@ -201,26 +272,31 @@ class SnapshotLoader final {
         assert(!registry.capacity());
         assert(!registry.capacity());
     }
     }
 
 
-    template<typename Archive, typename Func>
-    void each(Archive &archive, Func func) {
+    template<typename Archive>
+    void assure(Archive &archive, bool destroyed) {
         Entity length{};
         Entity length{};
         archive(length);
         archive(length);
 
 
-        while(length) {
+        while(length--) {
             Entity entity{};
             Entity entity{};
             archive(entity);
             archive(entity);
-            func(entity);
-            --length;
+            assure_fn(registry, entity, destroyed);
         }
         }
     }
     }
 
 
     template<typename Type, typename Archive, typename... Args>
     template<typename Type, typename Archive, typename... Args>
     void assign(Archive &archive, Args... args) {
     void assign(Archive &archive, Args... args) {
-        each(archive, [&archive, this, args...](const auto entity) {
+        Entity length{};
+        archive(length);
+
+        while(length--) {
+            Entity entity{};
+            Type instance{};
+            archive(entity, instance);
             static constexpr auto destroyed = false;
             static constexpr auto destroyed = false;
             assure_fn(registry, entity, destroyed);
             assure_fn(registry, entity, destroyed);
-            archive(registry.template assign<Type>(args..., entity));
-        });
+            registry.template assign<Type>(args..., entity, static_cast<const Type &>(instance));
+        }
     }
     }
 
 
 public:
 public:
@@ -246,11 +322,8 @@ public:
      */
      */
     template<typename Archive>
     template<typename Archive>
     SnapshotLoader & entities(Archive &archive) {
     SnapshotLoader & entities(Archive &archive) {
-        each(archive, [this](const auto entity) {
-            static constexpr auto destroyed = false;
-            assure_fn(registry, entity, destroyed);
-        });
-
+        static constexpr auto destroyed = false;
+        assure(archive, destroyed);
         return *this;
         return *this;
     }
     }
 
 
@@ -266,11 +339,8 @@ public:
      */
      */
     template<typename Archive>
     template<typename Archive>
     SnapshotLoader & destroyed(Archive &archive) {
     SnapshotLoader & destroyed(Archive &archive) {
-        each(archive, [this](const auto entity) {
-            static constexpr auto destroyed = true;
-            assure_fn(registry, entity, destroyed);
-        });
-
+        static constexpr auto destroyed = true;
+        assure(archive, destroyed);
         return *this;
         return *this;
     }
     }
 
 
@@ -360,7 +430,7 @@ template<typename Entity>
 class ContinuousLoader final {
 class ContinuousLoader final {
     using traits_type = entt_traits<Entity>;
     using traits_type = entt_traits<Entity>;
 
 
-    Entity destroy(const Entity entity) {
+    void destroy(Entity entity) {
         const auto it = remloc.find(entity);
         const auto it = remloc.find(entity);
 
 
         if(it == remloc.cend()) {
         if(it == remloc.cend()) {
@@ -368,11 +438,9 @@ class ContinuousLoader final {
             remloc.emplace(entity, std::make_pair(local, true));
             remloc.emplace(entity, std::make_pair(local, true));
             registry.destroy(local);
             registry.destroy(local);
         }
         }
-
-        return remloc[entity].first;
     }
     }
 
 
-    Entity restore(const Entity entity) {
+    void restore(Entity entity) {
         const auto it = remloc.find(entity);
         const auto it = remloc.find(entity);
 
 
         if(it == remloc.cend()) {
         if(it == remloc.cend()) {
@@ -387,34 +455,35 @@ class ContinuousLoader final {
             // set the dirty flag
             // set the dirty flag
             remloc[entity].second = true;
             remloc[entity].second = true;
         }
         }
-
-        return remloc[entity].first;
     }
     }
 
 
-    template<typename Instance, typename Type>
-    std::enable_if_t<std::is_same<Type, Entity>::value>
-    update(Instance &instance, Type Instance::*member) {
+    template<typename Type, typename Member>
+    std::enable_if_t<std::is_same<Member, Entity>::value>
+    update(Type &instance, Member Type:: *member) {
         instance.*member = map(instance.*member);
         instance.*member = map(instance.*member);
     }
     }
 
 
-    template<typename Instance, typename Type>
-    std::enable_if_t<std::is_same<typename std::iterator_traits<typename Type::iterator>::value_type, Entity>::value>
-    update(Instance &instance, Type Instance::*member) {
-        for(auto &entity: (instance.*member)) {
+    template<typename Type, typename Member>
+    std::enable_if_t<std::is_same<typename std::iterator_traits<typename Member::iterator>::value_type, Entity>::value>
+    update(Type &instance, Member Type:: *member) {
+        for(auto &entity: instance.*member) {
             entity = map(entity);
             entity = map(entity);
         }
         }
     }
     }
 
 
-    template<typename Archive, typename Func>
-    void each(Archive &archive, Func func) {
+    template<typename Other, typename Type, typename Member>
+    std::enable_if_t<!std::is_same<Other, Type>::value>
+    update(Other &, Member Type:: *) {}
+
+    template<typename Archive>
+    void assure(Archive &archive, void(ContinuousLoader:: *member)(Entity)) {
         Entity length{};
         Entity length{};
         archive(length);
         archive(length);
 
 
-        while(length) {
+        while(length--) {
             Entity entity{};
             Entity entity{};
             archive(entity);
             archive(entity);
-            func(entity);
-            --length;
+            (this->*member)(entity);
         }
         }
     }
     }
 
 
@@ -429,54 +498,24 @@ class ContinuousLoader final {
         }
         }
     }
     }
 
 
-    template<typename Component, typename Archive>
-    void assign(Archive &archive) {
-        reset<Component>();
-
-        each(archive, [&archive, this](const auto entity) {
-            const auto local = restore(entity);
-            archive(registry.template accommodate<Component>(local));
-        });
-    }
+    template<typename Other, typename Archive, typename Func, typename... Type, typename... Member>
+    void assign(Archive &archive, Func func, Member Type:: *... member) {
+        Entity length{};
+        archive(length);
 
 
-    template<typename Component, typename Archive, typename... Type>
-    void assign(Archive &archive, Type Component::*... member) {
-        reset<Component>();
+        while(length--) {
+            Entity entity{};
+            Other instance{};
 
 
-        each(archive, [&archive, member..., this](const auto entity) {
-            const auto local = restore(entity);
-            auto &component = registry.template accommodate<Component>(local);
-            archive(component);
+            archive(entity, instance);
+            restore(entity);
 
 
             using accumulator_type = int[];
             using accumulator_type = int[];
-            accumulator_type accumulator = { 0, (update(component, member), 0)... };
+            accumulator_type accumulator = { 0, (update(instance, member), 0)... };
             (void)accumulator;
             (void)accumulator;
-        });
-    }
 
 
-    template<typename Tag, typename Archive>
-    void attach(Archive &archive) {
-        registry.template remove<Tag>();
-
-        each(archive, [&archive, this](const auto entity) {
-            const auto local = restore(entity);
-            archive(registry.template assign<Tag>(tag_t{}, local));
-        });
-    }
-
-    template<typename Tag, typename Archive, typename... Type>
-    void attach(Archive &archive, Type Tag::*... member) {
-        registry.template remove<Tag>();
-
-        each(archive, [&archive, member..., this](const auto entity) {
-            const auto local = restore(entity);
-            auto &tag = registry.template assign<Tag>(tag_t{}, local);
-            archive(tag);
-
-            using accumulator_type = int[];
-            accumulator_type accumulator = { 0, (update(tag, member), 0)... };
-            (void)accumulator;
-        });
+            func(map(entity), instance);
+        }
     }
     }
 
 
 public:
 public:
@@ -513,7 +552,7 @@ public:
      */
      */
     template<typename Archive>
     template<typename Archive>
     ContinuousLoader & entities(Archive &archive) {
     ContinuousLoader & entities(Archive &archive) {
-        each(archive, [this](const auto entity) { restore(entity); });
+        assure(archive, &ContinuousLoader::restore);
         return *this;
         return *this;
     }
     }
 
 
@@ -529,28 +568,7 @@ public:
      */
      */
     template<typename Archive>
     template<typename Archive>
     ContinuousLoader & destroyed(Archive &archive) {
     ContinuousLoader & destroyed(Archive &archive) {
-        each(archive, [this](const auto entity) { destroy(entity); });
-        return *this;
-    }
-
-    /**
-     * @brief Restores components and assigns them to the right entities.
-     *
-     * The template parameter list must be exactly the same used during
-     * serialization. In the event that the entity to which the component is
-     * assigned doesn't exist yet, the loader will take care to create a local
-     * counterpart for it.
-     *
-     * @tparam Component Types of components to restore.
-     * @tparam Archive Type of input archive.
-     * @param archive A valid reference to an input archive.
-     * @return A non-const reference to this loader.
-     */
-    template<typename... Component, typename Archive>
-    ContinuousLoader & component(Archive &archive) {
-        using accumulator_type = int[];
-        accumulator_type accumulator = { 0, (assign<Component>(archive), 0)... };
-        (void)accumulator;
+        assure(archive, &ContinuousLoader::destroy);
         return *this;
         return *this;
     }
     }
 
 
@@ -567,34 +585,20 @@ public:
      *
      *
      * @tparam Component Type of component to restore.
      * @tparam Component Type of component to restore.
      * @tparam Archive Type of input archive.
      * @tparam Archive Type of input archive.
-     * @tparam Type Types of members to update with their local counterparts.
+     * @tparam Type Types of components to update with local counterparts.
+     * @tparam Member Types of members to update with their local counterparts.
      * @param archive A valid reference to an input archive.
      * @param archive A valid reference to an input archive.
      * @param member Members to update with their local counterparts.
      * @param member Members to update with their local counterparts.
      * @return A non-const reference to this loader.
      * @return A non-const reference to this loader.
      */
      */
-    template<typename Component, typename Archive, typename... Type>
-    ContinuousLoader & component(Archive &archive, Type Component::*... member) {
-        assign(archive, member...);
-        return *this;
-    }
+    template<typename... Component, typename Archive, typename... Type, typename... Member>
+    ContinuousLoader & component(Archive &archive, Member Type:: *... member) {
+        auto apply = [this](const auto entity, const auto &component) {
+            registry.template accommodate<std::decay_t<decltype(component)>>(entity, component);
+        };
 
 
-    /**
-     * @brief Restores tags and assigns them to the right entities.
-     *
-     * The template parameter list must be exactly the same used during
-     * serialization. In the event that the entity to which the tag is assigned
-     * doesn't exist yet, the loader will take care to create a local
-     * counterpart for it.
-     *
-     * @tparam Tag Types of tags to restore.
-     * @tparam Archive Type of input archive.
-     * @param archive A valid reference to an input archive.
-     * @return A non-const reference to this loader.
-     */
-    template<typename... Tag, typename Archive>
-    ContinuousLoader & tag(Archive &archive) {
         using accumulator_type = int[];
         using accumulator_type = int[];
-        accumulator_type accumulator = { 0, (attach<Tag>(archive), 0)... };
+        accumulator_type accumulator = { 0, (reset<Component>(), assign<Component>(archive, apply, member...), 0)... };
         (void)accumulator;
         (void)accumulator;
         return *this;
         return *this;
     }
     }
@@ -612,14 +616,21 @@ public:
      *
      *
      * @tparam Tag Type of tag to restore.
      * @tparam Tag Type of tag to restore.
      * @tparam Archive Type of input archive.
      * @tparam Archive Type of input archive.
-     * @tparam Type Types of members to update with their local counterparts.
+     * @tparam Type Types of components to update with local counterparts.
+     * @tparam Member Types of members to update with their local counterparts.
      * @param archive A valid reference to an input archive.
      * @param archive A valid reference to an input archive.
      * @param member Members to update with their local counterparts.
      * @param member Members to update with their local counterparts.
      * @return A non-const reference to this loader.
      * @return A non-const reference to this loader.
      */
      */
-    template<typename Tag, typename Archive, typename... Type>
-    ContinuousLoader & tag(Archive &archive, Type Tag::*... member) {
-        attach<Tag>(archive, member...);
+    template<typename... Tag, typename Archive, typename... Type, typename... Member>
+    ContinuousLoader & tag(Archive &archive, Member Type:: *... member) {
+        auto apply = [this](const auto entity, const auto &tag) {
+            registry.template assign<std::decay_t<decltype(tag)>>(tag_t{}, entity, tag);
+        };
+
+        using accumulator_type = int[];
+        accumulator_type accumulator = { 0, (registry.template remove<Tag>(), assign<Tag>(archive, apply, member...), 0)... };
+        (void)accumulator;
         return *this;
         return *this;
     }
     }
 
 

+ 2 - 2
src/entt/signal/delegate.hpp

@@ -44,7 +44,7 @@ class Delegate<Ret(Args...)> final {
         return (Function)(args...);
         return (Function)(args...);
     }
     }
 
 
-    template<typename Class, Ret(Class::*Member)(Args...)>
+    template<typename Class, Ret(Class:: *Member)(Args...)>
     static Ret proto(void *instance, Args... args) {
     static Ret proto(void *instance, Args... args) {
         return (static_cast<Class *>(instance)->*Member)(args...);
         return (static_cast<Class *>(instance)->*Member)(args...);
     }
     }
@@ -75,7 +75,7 @@ public:
      * @tparam Member Member function to connect to the delegate.
      * @tparam Member Member function to connect to the delegate.
      * @param instance A valid instance of type pointer to `Class`.
      * @param instance A valid instance of type pointer to `Class`.
      */
      */
-    template<typename Class, Ret(Class::*Member)(Args...)>
+    template<typename Class, Ret(Class:: *Member)(Args...)>
     void connect(Class *instance) ENTT_NOEXCEPT {
     void connect(Class *instance) ENTT_NOEXCEPT {
         stub = std::make_pair(instance, &proto<Class, Member>);
         stub = std::make_pair(instance, &proto<Class, Member>);
     }
     }

+ 3 - 3
src/entt/signal/sigh.hpp

@@ -140,7 +140,7 @@ class Sink<Ret(Args...)> final {
         return (Function)(args...);
         return (Function)(args...);
     }
     }
 
 
-    template<typename Class, Ret(Class::*Member)(Args... args)>
+    template<typename Class, Ret(Class:: *Member)(Args... args)>
     static Ret proto(void *instance, Args... args) {
     static Ret proto(void *instance, Args... args) {
         return (static_cast<Class *>(instance)->*Member)(args...);
         return (static_cast<Class *>(instance)->*Member)(args...);
     }
     }
@@ -177,7 +177,7 @@ public:
      * @tparam Member Member function to connect to the signal.
      * @tparam Member Member function to connect to the signal.
      * @param instance A valid instance of type pointer to `Class`.
      * @param instance A valid instance of type pointer to `Class`.
      */
      */
-    template <typename Class, Ret(Class::*Member)(Args...) = &Class::receive>
+    template <typename Class, Ret(Class:: *Member)(Args...) = &Class::receive>
     void connect(Class *instance) {
     void connect(Class *instance) {
         disconnect<Class, Member>(instance);
         disconnect<Class, Member>(instance);
         calls.emplace_back(instance, &proto<Class, Member>);
         calls.emplace_back(instance, &proto<Class, Member>);
@@ -199,7 +199,7 @@ public:
      * @tparam Member Member function to connect to the signal.
      * @tparam Member Member function to connect to the signal.
      * @param instance A valid instance of type pointer to `Class`.
      * @param instance A valid instance of type pointer to `Class`.
      */
      */
-    template<typename Class, Ret(Class::*Member)(Args...)>
+    template<typename Class, Ret(Class:: *Member)(Args...)>
     void disconnect(Class *instance) {
     void disconnect(Class *instance) {
         call_type target{instance, &proto<Class, Member>};
         call_type target{instance, &proto<Class, Member>};
         calls.erase(std::remove(calls.begin(), calls.end(), std::move(target)), calls.end());
         calls.erase(std::remove(calls.begin(), calls.end(), std::move(target)), calls.end());

+ 61 - 24
test/entt/entity/snapshot.cpp

@@ -10,9 +10,11 @@ struct OutputArchive {
         : storage{storage}
         : storage{storage}
     {}
     {}
 
 
-    template<typename Value>
-    void operator()(const Value &value) {
-        std::get<std::queue<Value>>(storage).push(value);
+    template<typename... Value>
+    void operator()(const Value &... value) {
+        using accumulator_type = int[];
+        accumulator_type accumulator = { (std::get<std::queue<Value>>(storage).push(value), 0)... };
+        (void)accumulator;
     }
     }
 
 
 private:
 private:
@@ -25,11 +27,17 @@ struct InputArchive {
         : storage{storage}
         : storage{storage}
     {}
     {}
 
 
-    template<typename Value>
-    void operator()(Value &value) {
-        auto &queue = std::get<std::queue<Value>>(storage);
-        value = queue.front();
-        queue.pop();
+    template<typename... Value>
+    void operator()(Value &... value) {
+        auto assign = [this](auto &value) {
+            auto &queue = std::get<std::queue<std::decay_t<decltype(value)>>>(storage);
+            value = queue.front();
+            queue.pop();
+        };
+
+        using accumulator_type = int[];
+        accumulator_type accumulator = { (assign(value), 0)... };
+        (void)accumulator;
     }
     }
 
 
 private:
 private:
@@ -240,6 +248,41 @@ TEST(Snapshot, Partial) {
     ASSERT_FALSE(registry.valid(e4));
     ASSERT_FALSE(registry.valid(e4));
 }
 }
 
 
+TEST(Snapshot, Iterator) {
+    entt::DefaultRegistry registry;
+
+    for(auto i = 0; i < 50; ++i) {
+        const auto entity = registry.create();
+        registry.assign<AnotherComponent>(entity, i, i);
+
+        if(i % 2) {
+            registry.assign<AComponent>(entity);
+        }
+    }
+
+    using storage_type = std::tuple<
+        std::queue<entt::DefaultRegistry::entity_type>,
+        std::queue<AnotherComponent>
+    >;
+
+    storage_type storage;
+    OutputArchive<storage_type> output{storage};
+    InputArchive<storage_type> input{storage};
+
+    const auto view = registry.view<AComponent>();
+    const auto size = view.size();
+
+    registry.snapshot().component<AnotherComponent>(output, view.cbegin(), view.cend());
+    registry.reset();
+    registry.restore().component<AnotherComponent>(input);
+
+    ASSERT_EQ(registry.view<AnotherComponent>().size(), size);
+
+    registry.view<AnotherComponent>().each([](const auto entity, auto &&...) {
+        ASSERT_TRUE(entity % 2);
+    });
+}
+
 TEST(Snapshot, Continuous) {
 TEST(Snapshot, Continuous) {
     using entity_type = entt::DefaultRegistry::entity_type;
     using entity_type = entt::DefaultRegistry::entity_type;
 
 
@@ -299,8 +342,7 @@ TEST(Snapshot, Continuous) {
 
 
     loader.entities(input)
     loader.entities(input)
             .destroyed(input)
             .destroyed(input)
-            .component<AComponent, AnotherComponent>(input)
-            .component<WhatAComponent>(input, &WhatAComponent::bar, &WhatAComponent::quux)
+            .component<AComponent, AnotherComponent, WhatAComponent>(input, &WhatAComponent::bar, &WhatAComponent::quux)
             .tag<double>(input)
             .tag<double>(input)
             .orphans();
             .orphans();
 
 
@@ -340,13 +382,12 @@ TEST(Snapshot, Continuous) {
     src.snapshot()
     src.snapshot()
             .entities(output)
             .entities(output)
             .destroyed(output)
             .destroyed(output)
-            .component<AComponent, AnotherComponent, WhatAComponent>(output)
+            .component<AComponent, WhatAComponent, AnotherComponent>(output)
             .tag<double>(output);
             .tag<double>(output);
 
 
     loader.entities(input)
     loader.entities(input)
             .destroyed(input)
             .destroyed(input)
-            .component<AComponent, AnotherComponent>(input)
-            .component<WhatAComponent>(input, &WhatAComponent::bar, &WhatAComponent::quux)
+            .component<AComponent, WhatAComponent, AnotherComponent>(input, &WhatAComponent::bar, &WhatAComponent::quux)
             .tag<double>(input)
             .tag<double>(input)
             .orphans();
             .orphans();
 
 
@@ -370,13 +411,12 @@ TEST(Snapshot, Continuous) {
     src.snapshot()
     src.snapshot()
             .entities(output)
             .entities(output)
             .destroyed(output)
             .destroyed(output)
-            .component<AComponent, AnotherComponent, WhatAComponent>(output)
+            .component<WhatAComponent, AComponent, AnotherComponent>(output)
             .tag<double>(output);
             .tag<double>(output);
 
 
     loader.entities(input)
     loader.entities(input)
             .destroyed(input)
             .destroyed(input)
-            .component<AComponent, AnotherComponent>(input)
-            .component<WhatAComponent>(input, &WhatAComponent::bar, &WhatAComponent::quux)
+            .component<WhatAComponent, AComponent, AnotherComponent>(input, &WhatAComponent::bar, &WhatAComponent::quux)
             .tag<double>(input)
             .tag<double>(input)
             .orphans();
             .orphans();
 
 
@@ -400,8 +440,7 @@ TEST(Snapshot, Continuous) {
 
 
     loader.entities(input)
     loader.entities(input)
             .destroyed(input)
             .destroyed(input)
-            .component<AComponent, AnotherComponent>(input)
-            .component<WhatAComponent>(input, &WhatAComponent::bar, &WhatAComponent::quux)
+            .component<AComponent, AnotherComponent, WhatAComponent>(input, &WhatAComponent::bar, &WhatAComponent::quux)
             .tag<double>(input)
             .tag<double>(input)
             .orphans()
             .orphans()
             .shrink();
             .shrink();
@@ -424,13 +463,12 @@ TEST(Snapshot, Continuous) {
     src.snapshot()
     src.snapshot()
             .entities(output)
             .entities(output)
             .destroyed(output)
             .destroyed(output)
-            .component<AComponent, AnotherComponent, WhatAComponent>(output)
+            .component<AComponent, WhatAComponent, AnotherComponent>(output)
             .tag<double>(output);
             .tag<double>(output);
 
 
     loader.entities(input)
     loader.entities(input)
             .destroyed(input)
             .destroyed(input)
-            .component<AComponent, AnotherComponent>(input)
-            .component<WhatAComponent>(input, &WhatAComponent::bar, &WhatAComponent::quux)
+            .component<AComponent, WhatAComponent, AnotherComponent>(input, &WhatAComponent::bar, &WhatAComponent::quux)
             .tag<double>(input)
             .tag<double>(input)
             .orphans();
             .orphans();
 
 
@@ -444,13 +482,12 @@ TEST(Snapshot, Continuous) {
     src.snapshot()
     src.snapshot()
             .entities(output)
             .entities(output)
             .destroyed(output)
             .destroyed(output)
-            .component<AComponent, AnotherComponent, WhatAComponent>(output)
+            .component<WhatAComponent, AComponent, AnotherComponent>(output)
             .tag<double>(output);
             .tag<double>(output);
 
 
     loader.entities(input)
     loader.entities(input)
             .destroyed(input)
             .destroyed(input)
-            .component<AComponent, AnotherComponent>(input)
-            .component<WhatAComponent>(input, &WhatAComponent::bar, &WhatAComponent::quux)
+            .component<WhatAComponent, AComponent, AnotherComponent>(input, &WhatAComponent::bar, &WhatAComponent::quux)
             .tag<double>(input)
             .tag<double>(input)
             .orphans();
             .orphans();
 
 

+ 1 - 2
test/snapshot/snapshot.cpp

@@ -128,8 +128,7 @@ TEST(Snapshot, Continuous) {
     cereal::JSONInputArchive input{storage};
     cereal::JSONInputArchive input{storage};
     entt::ContinuousLoader<entt::DefaultRegistry::entity_type> loader{destination};
     entt::ContinuousLoader<entt::DefaultRegistry::entity_type> loader{destination};
     loader.entities(input)
     loader.entities(input)
-            .component<Position>(input)
-            .component<Relationship>(input, &Relationship::parent)
+            .component<Position, Relationship>(input, &Relationship::parent)
             .component<Timer>(input);
             .component<Timer>(input);
 
 
     ASSERT_FALSE(destination.valid(e0));
     ASSERT_FALSE(destination.valid(e0));