Browse Source

more on save/restore (#67)

Michele Caini 7 năm trước cách đây
mục cha
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.
   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
 that it works with tags and not with components.<br/>
 Note also that both `component` and `tag` store items along with entities. It
@@ -921,10 +936,8 @@ InputArchive input;
 
 loader.entities(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()
     .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
 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
-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
 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
 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:
 
@@ -983,13 +983,15 @@ In particular:
   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
-  void operator()(const T &);
+  void operator()(Entity, const T &);
   ```
 
   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
   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
-  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
 

+ 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 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...
-* 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)
 * improve CMake interface, see mail from Malte
 * is registry/utility.hpp really required?

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

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

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

@@ -1479,7 +1479,7 @@ public:
             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
 
 
-#include <unordered_map>
-#include <algorithm>
+#include <array>
 #include <cstddef>
 #include <utility>
 #include <cassert>
 #include <iterator>
 #include <type_traits>
+#include <unordered_map>
 #include "../config/config.h"
 #include "entt_traits.hpp"
 #include "utility.hpp"
@@ -41,38 +41,40 @@ class Snapshot final {
 
     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},
           seed{seed},
-          size{size},
           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));
 
-        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:
@@ -115,26 +117,49 @@ public:
      */
     template<typename Archive>
     Snapshot & destroyed(Archive &archive) {
+        auto size = registry.capacity() - registry.size();
         archive(static_cast<Entity>(size));
+        auto curr = seed;
 
-        if(size) {
-            auto curr = seed;
+        for(; size; --size) {
             archive(curr);
-
-            for(auto i = size - 1; i; --i) {
-                curr = follow(registry, curr);
-                archive(curr);
-            }
+            curr = follow(registry, curr);
         }
 
         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.
      *
-     * 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 Archive Type of output archive.
@@ -142,17 +167,63 @@ public:
      * @return An object of this type to continue creating the snapshot.
      */
     template<typename... Component, typename Archive>
-    Snapshot & component(Archive &archive) {
+    std::enable_if_t<(sizeof...(Component) > 1), Snapshot &>
+    component(Archive &archive) {
         using accumulator_type = int[];
-        accumulator_type accumulator = { 0, (get<Component>(archive, registry), 0)... };
+        accumulator_type accumulator = { 0, (component<Component>(archive), 0)... };
         (void)accumulator;
         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.
      *
-     * 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.
      *
      * @tparam Tag Types of tags to serialize.
@@ -161,9 +232,10 @@ public:
      * @return An object of this type to continue creating the snapshot.
      */
     template<typename... Tag, typename Archive>
-    Snapshot & tag(Archive &archive) {
+    std::enable_if_t<(sizeof...(Tag) > 1), Snapshot &>
+    tag(Archive &archive) {
         using accumulator_type = int[];
-        accumulator_type accumulator = { 0, (get<Tag>(archive), 0)... };
+        accumulator_type accumulator = { 0, (tag<Tag>(archive), 0)... };
         (void)accumulator;
         return *this;
     }
@@ -171,7 +243,6 @@ public:
 private:
     const Registry<Entity> &registry;
     const Entity seed;
-    const std::size_t size;
     follow_fn_type follow;
 };
 
@@ -201,26 +272,31 @@ class SnapshotLoader final {
         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{};
         archive(length);
 
-        while(length) {
+        while(length--) {
             Entity entity{};
             archive(entity);
-            func(entity);
-            --length;
+            assure_fn(registry, entity, destroyed);
         }
     }
 
     template<typename Type, typename Archive, typename... 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;
             assure_fn(registry, entity, destroyed);
-            archive(registry.template assign<Type>(args..., entity));
-        });
+            registry.template assign<Type>(args..., entity, static_cast<const Type &>(instance));
+        }
     }
 
 public:
@@ -246,11 +322,8 @@ public:
      */
     template<typename 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;
     }
 
@@ -266,11 +339,8 @@ public:
      */
     template<typename 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;
     }
 
@@ -360,7 +430,7 @@ template<typename Entity>
 class ContinuousLoader final {
     using traits_type = entt_traits<Entity>;
 
-    Entity destroy(const Entity entity) {
+    void destroy(Entity entity) {
         const auto it = remloc.find(entity);
 
         if(it == remloc.cend()) {
@@ -368,11 +438,9 @@ class ContinuousLoader final {
             remloc.emplace(entity, std::make_pair(local, true));
             registry.destroy(local);
         }
-
-        return remloc[entity].first;
     }
 
-    Entity restore(const Entity entity) {
+    void restore(Entity entity) {
         const auto it = remloc.find(entity);
 
         if(it == remloc.cend()) {
@@ -387,34 +455,35 @@ class ContinuousLoader final {
             // set the dirty flag
             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);
     }
 
-    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);
         }
     }
 
-    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{};
         archive(length);
 
-        while(length) {
+        while(length--) {
             Entity 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[];
-            accumulator_type accumulator = { 0, (update(component, member), 0)... };
+            accumulator_type accumulator = { 0, (update(instance, member), 0)... };
             (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:
@@ -513,7 +552,7 @@ public:
      */
     template<typename Archive>
     ContinuousLoader & entities(Archive &archive) {
-        each(archive, [this](const auto entity) { restore(entity); });
+        assure(archive, &ContinuousLoader::restore);
         return *this;
     }
 
@@ -529,28 +568,7 @@ public:
      */
     template<typename 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;
     }
 
@@ -567,34 +585,20 @@ public:
      *
      * @tparam Component Type of component to restore.
      * @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 member Members to update with their local counterparts.
      * @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[];
-        accumulator_type accumulator = { 0, (attach<Tag>(archive), 0)... };
+        accumulator_type accumulator = { 0, (reset<Component>(), assign<Component>(archive, apply, member...), 0)... };
         (void)accumulator;
         return *this;
     }
@@ -612,14 +616,21 @@ public:
      *
      * @tparam Tag Type of tag to restore.
      * @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 member Members to update with their local counterparts.
      * @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;
     }
 

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

@@ -44,7 +44,7 @@ class Delegate<Ret(Args...)> final {
         return (Function)(args...);
     }
 
-    template<typename Class, Ret(Class::*Member)(Args...)>
+    template<typename Class, Ret(Class:: *Member)(Args...)>
     static Ret proto(void *instance, Args... args) {
         return (static_cast<Class *>(instance)->*Member)(args...);
     }
@@ -75,7 +75,7 @@ public:
      * @tparam Member Member function to connect to the delegate.
      * @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 {
         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...);
     }
 
-    template<typename Class, Ret(Class::*Member)(Args... args)>
+    template<typename Class, Ret(Class:: *Member)(Args... args)>
     static Ret proto(void *instance, Args... args) {
         return (static_cast<Class *>(instance)->*Member)(args...);
     }
@@ -177,7 +177,7 @@ public:
      * @tparam Member Member function to connect to the signal.
      * @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) {
         disconnect<Class, Member>(instance);
         calls.emplace_back(instance, &proto<Class, Member>);
@@ -199,7 +199,7 @@ public:
      * @tparam Member Member function to connect to the signal.
      * @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) {
         call_type target{instance, &proto<Class, Member>};
         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}
     {}
 
-    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:
@@ -25,11 +27,17 @@ struct InputArchive {
         : 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:
@@ -240,6 +248,41 @@ TEST(Snapshot, Partial) {
     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) {
     using entity_type = entt::DefaultRegistry::entity_type;
 
@@ -299,8 +342,7 @@ TEST(Snapshot, Continuous) {
 
     loader.entities(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)
             .orphans();
 
@@ -340,13 +382,12 @@ TEST(Snapshot, Continuous) {
     src.snapshot()
             .entities(output)
             .destroyed(output)
-            .component<AComponent, AnotherComponent, WhatAComponent>(output)
+            .component<AComponent, WhatAComponent, AnotherComponent>(output)
             .tag<double>(output);
 
     loader.entities(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)
             .orphans();
 
@@ -370,13 +411,12 @@ TEST(Snapshot, Continuous) {
     src.snapshot()
             .entities(output)
             .destroyed(output)
-            .component<AComponent, AnotherComponent, WhatAComponent>(output)
+            .component<WhatAComponent, AComponent, AnotherComponent>(output)
             .tag<double>(output);
 
     loader.entities(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)
             .orphans();
 
@@ -400,8 +440,7 @@ TEST(Snapshot, Continuous) {
 
     loader.entities(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)
             .orphans()
             .shrink();
@@ -424,13 +463,12 @@ TEST(Snapshot, Continuous) {
     src.snapshot()
             .entities(output)
             .destroyed(output)
-            .component<AComponent, AnotherComponent, WhatAComponent>(output)
+            .component<AComponent, WhatAComponent, AnotherComponent>(output)
             .tag<double>(output);
 
     loader.entities(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)
             .orphans();
 
@@ -444,13 +482,12 @@ TEST(Snapshot, Continuous) {
     src.snapshot()
             .entities(output)
             .destroyed(output)
-            .component<AComponent, AnotherComponent, WhatAComponent>(output)
+            .component<WhatAComponent, AComponent, AnotherComponent>(output)
             .tag<double>(output);
 
     loader.entities(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)
             .orphans();
 

+ 1 - 2
test/snapshot/snapshot.cpp

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