瀏覽代碼

WIP: improvements and new features

Michele Caini 8 年之前
父節點
當前提交
af70573634
共有 11 個文件被更改,包括 561 次插入501 次删除
  1. 4 0
      README.md
  2. 0 235
      src/component_pool.hpp
  3. 110 97
      src/registry.hpp
  4. 205 0
      src/sparse_set.hpp
  5. 97 0
      src/tag_handler.hpp
  6. 1 1
      test/CMakeLists.txt
  7. 1 0
      test/benchmark.cpp
  8. 0 166
      test/component_pool.cpp
  9. 7 2
      test/registry.cpp
  10. 90 0
      test/sparse_set.cpp
  11. 46 0
      test/tag_handler.cpp

+ 4 - 0
README.md

@@ -6,6 +6,9 @@
 
 # Introduction
 
+WIP. Documentation is coming soon.
+
+<!--
 `EnTT` is a header-only, tiny and easy to use Entity-Component System in modern C++.<br/>
 ECS is an architectural pattern used mostly in game development. For further details:
 
@@ -347,3 +350,4 @@ Check the [contributors list](https://github.com/skypjack/entt/blob/master/AUTHO
 
 Code and documentation Copyright (c) 2017 Michele Caini.<br/>
 Code released under [the MIT license](https://github.com/skypjack/entt/blob/master/LICENSE).
+-->

+ 0 - 235
src/component_pool.hpp

@@ -1,235 +0,0 @@
-#ifndef ENTT_COMPONENT_POOL_HPP
-#define ENTT_COMPONENT_POOL_HPP
-
-
-#include <utility>
-#include <vector>
-#include <cassert>
-
-
-namespace entt {
-
-
-template<typename, typename, typename...>
-class ComponentPool;
-
-
-template<typename Entity, typename Component>
-class ComponentPool<Entity, Component> {
-public:
-    using component_type = Component;
-    using entity_type = Entity;
-    using pos_type = entity_type;
-    using size_type = typename std::vector<component_type>::size_type;
-    using iterator_type = typename std::vector<entity_type>::iterator;
-    using const_iterator_type = typename std::vector<entity_type>::const_iterator;
-
-private:
-    inline bool valid(entity_type entity) const noexcept {
-        return entity < reverse.size() && reverse[entity] < direct.size() && direct[reverse[entity]] == entity;
-    }
-
-public:
-    explicit ComponentPool(size_type dim = 4098) noexcept {
-        assert(!(dim < 0));
-        data.reserve(dim);
-    }
-
-    ComponentPool(ComponentPool &&) = default;
-
-    ~ComponentPool() noexcept {
-        assert(empty());
-    }
-
-    ComponentPool & operator=(ComponentPool &&) = default;
-
-    bool empty() const noexcept {
-        return data.empty();
-    }
-
-    size_type capacity() const noexcept {
-        return data.capacity();
-    }
-
-    size_type size() const noexcept {
-        return data.size();
-    }
-
-    iterator_type begin() noexcept {
-        return direct.begin();
-    }
-
-    const_iterator_type cbegin() const noexcept {
-        return direct.cbegin();
-    }
-
-    iterator_type end() noexcept {
-        return direct.end();
-    }
-
-    const_iterator_type cend() const noexcept {
-        return direct.cend();
-    }
-
-    bool has(entity_type entity) const noexcept {
-        return valid(entity);
-    }
-
-    const component_type & get(entity_type entity) const noexcept {
-        assert(valid(entity));
-        return data[reverse[entity]];
-    }
-
-    component_type & get(entity_type entity) noexcept {
-        return const_cast<component_type &>(const_cast<const ComponentPool *>(this)->get(entity));
-    }
-
-    template<typename... Args>
-    component_type & construct(entity_type entity, Args... args) {
-        assert(!valid(entity));
-
-        if(!(entity < reverse.size())) {
-            reverse.resize(entity+1);
-        }
-
-        reverse[entity] = pos_type(direct.size());
-        direct.emplace_back(entity);
-        data.push_back({ args... });
-
-        return data.back();
-    }
-
-    void destroy(entity_type entity) {
-        assert(valid(entity));
-
-        auto last = direct.size() - 1;
-
-        reverse[direct[last]] = reverse[entity];
-        direct[reverse[entity]] = direct[last];
-        data[reverse[entity]] = std::move(data[last]);
-
-        direct.pop_back();
-        data.pop_back();
-    }
-
-    void reset() {
-        data.clear();
-        reverse.resize(0);
-        direct.clear();
-    }
-
-private:
-    std::vector<component_type> data;
-    std::vector<pos_type> reverse;
-    std::vector<entity_type> direct;
-};
-
-
-template<typename Entity, typename Component, typename... Components>
-class ComponentPool
-        : ComponentPool<Entity, Component>, ComponentPool<Entity, Components>...
-{
-    template<typename Comp>
-    using Pool = ComponentPool<Entity, Comp>;
-
-public:
-    using entity_type = typename Pool<Component>::entity_type;
-    using pos_type = typename Pool<Component>::pos_type;
-    using size_type = typename Pool<Component>::size_type;
-    using iterator_type = typename Pool<Component>::iterator_type;
-    using const_iterator_type = typename Pool<Component>::const_iterator_type;
-
-    explicit ComponentPool(size_type dim = 4098) noexcept
-#ifdef _MSC_VER
-        : ComponentPool<Entity, Component>{dim}, ComponentPool<Entity, Components>{dim}...
-#else
-        : Pool<Component>{dim}, Pool<Components>{dim}...
-#endif
-    {
-        assert(!(dim < 0));
-    }
-
-    ComponentPool(const ComponentPool &) = delete;
-    ComponentPool(ComponentPool &&) = delete;
-
-    ComponentPool & operator=(const ComponentPool &) = delete;
-    ComponentPool & operator=(ComponentPool &&) = delete;
-
-    template<typename Comp>
-    bool empty() const noexcept {
-        return Pool<Comp>::empty();
-    }
-
-    template<typename Comp>
-    size_type capacity() const noexcept {
-        return Pool<Comp>::capacity();
-    }
-
-    template<typename Comp>
-    size_type size() const noexcept {
-        return Pool<Comp>::size();
-    }
-
-    template<typename Comp>
-    iterator_type begin() noexcept {
-        return Pool<Comp>::begin();
-    }
-
-    template<typename Comp>
-    const_iterator_type cbegin() const noexcept {
-        return Pool<Comp>::cbegin();
-    }
-
-    template<typename Comp>
-    iterator_type end() noexcept {
-        return Pool<Comp>::end();
-    }
-
-    template<typename Comp>
-    const_iterator_type cend() const noexcept {
-        return Pool<Comp>::cend();
-    }
-
-    template<typename Comp>
-    bool has(entity_type entity) const noexcept {
-        return Pool<Comp>::has(entity);
-    }
-
-    template<typename Comp>
-    const Comp & get(entity_type entity) const noexcept {
-        return Pool<Comp>::get(entity);
-    }
-
-    template<typename Comp>
-    Comp & get(entity_type entity) noexcept {
-        return const_cast<Comp &>(const_cast<const ComponentPool *>(this)->get<Comp>(entity));
-    }
-
-    template<typename Comp, typename... Args>
-    Comp & construct(entity_type entity, Args... args) {
-        return Pool<Comp>::construct(entity, args...);
-    }
-
-    template<typename Comp>
-    void destroy(entity_type entity) {
-        Pool<Comp>::destroy(entity);
-    }
-
-    template<typename Comp>
-    void reset() {
-        Pool<Comp>::reset();
-    }
-
-    void reset() {
-        using accumulator_type = int[];
-        Pool<Component>::reset();
-        accumulator_type accumulator = { (Pool<Components>::reset(), 0)... };
-        (void)accumulator;
-    }
-};
-
-
-}
-
-
-#endif // ENTT_COMPONENT_POOL_HPP

+ 110 - 97
src/registry.hpp

@@ -2,6 +2,7 @@
 #define ENTT_REGISTRY_HPP
 
 
+#include <tuple>
 #include <vector>
 #include <bitset>
 #include <utility>
@@ -9,30 +10,29 @@
 #include <iterator>
 #include <cassert>
 #include <type_traits>
-#include "component_pool.hpp"
+#include "sparse_set.hpp"
 #include "ident.hpp"
 
 
 namespace entt {
 
 
-template<typename...>
+template<typename, std::size_t...>
 class View;
 
 
-template<template<typename...> class Pool, typename Entity, typename... Components, typename Type, typename... Types>
-class View<Pool<Entity, Components...>, Type, Types...> final {
-    using pool_type = Pool<Entity, Components...>;
-    using entity_type = typename pool_type::entity_type;
-    using mask_type = std::bitset<sizeof...(Components)+1>;
-    using underlying_iterator_type = typename pool_type::const_iterator_type;
+template<typename Pool, std::size_t Ident, std::size_t... Other>
+class View<Pool, Ident, Other...> final {
+    using pool_type = Pool;
+    using mask_type = std::bitset<std::tuple_size<Pool>::value + 1>;
+    using underlying_iterator_type = typename std::tuple_element_t<Ident, Pool>::iterator_type;
 
     class ViewIterator;
 
 public:
     using iterator_type = ViewIterator;
-    using const_iterator_type = iterator_type;
-    using size_type = typename pool_type::size_type;
+    using entity_type = typename std::tuple_element_t<Ident, Pool>::index_type;
+    using size_type = typename std::tuple_element_t<Ident, Pool>::size_type;
 
 private:
     class ViewIterator {
@@ -42,10 +42,6 @@ private:
 
     public:
         using value_type = entity_type;
-        using difference_type = std::ptrdiff_t;
-        using reference = entity_type &;
-        using pointer = entity_type *;
-        using iterator_category = std::input_iterator_tag;
 
         ViewIterator(underlying_iterator_type begin, underlying_iterator_type end, const mask_type &bitmask, const mask_type *mask) noexcept
             : begin{begin}, end{end}, bitmask{bitmask}, mask{mask}
@@ -85,133 +81,137 @@ private:
         const mask_type *mask;
     };
 
-    template<typename Comp>
+    template<std::size_t Idx>
     void prefer(size_type &size) noexcept {
-        auto sz = pool.template size<Comp>();
+        auto &&cpool = std::get<Idx>(*pool);
+        auto sz = cpool.size();
 
         if(sz < size) {
-            from = pool.template begin<Type>();
-            to = pool.template end<Type>();
+            from = cpool.begin();
+            to = cpool.end();
             size = sz;
         }
     }
 
 public:
-    explicit View(pool_type &pool, const mask_type *mask) noexcept
-        : from{pool.template begin<Type>()},
-          to{pool.template end<Type>()},
+    explicit View(const pool_type *pool, const mask_type *mask) noexcept
+        : from{std::get<Ident>(*pool).begin()},
+          to{std::get<Ident>(*pool).end()},
           pool{pool},
           mask{mask}
     {
         using accumulator_type = int[];
-        size_type size = pool.template size<Type>();
-        bitmask.set(ident<Components...>.template get<Type>());
-        accumulator_type types = { 0, (bitmask.set(ident<Components...>.template get<Types>()), 0)... };
-        accumulator_type pref = { 0, (prefer<Types>(size), 0)... };
+        size_type size = std::get<Ident>(*pool).size();
+        bitmask.set(Ident);
+        accumulator_type types = { 0, (bitmask.set(Other), 0)... };
+        accumulator_type pref = { 0, (prefer<Other>(size), 0)... };
         (void)types, (void)pref;
     }
 
-    const_iterator_type begin() const noexcept {
+    iterator_type begin() const noexcept {
         return ViewIterator{from, to, bitmask, mask};
     }
 
-    iterator_type begin() noexcept {
-        return const_cast<const View *>(this)->begin();
-    }
-
-    const_iterator_type end() const noexcept {
+    iterator_type end() const noexcept {
         return ViewIterator{to, to, bitmask, mask};
     }
 
-    iterator_type end() noexcept {
-        return const_cast<const View *>(this)->end();
-    }
-
     void reset() noexcept {
         using accumulator_type = int[];
-        from = pool.template begin<Type>();
-        to = pool.template end<Type>();
-        size_type size = pool.template size<Type>();
-        accumulator_type accumulator = { 0, (prefer<Types>(size), 0)... };
+        auto &&cpool = std::get<Ident>(*pool);
+        from = cpool.begin();
+        to = cpool.end();
+        size_type size = cpool.size();
+        accumulator_type accumulator = { 0, (prefer<Other>(size), 0)... };
         (void)accumulator;
     }
 
 private:
     underlying_iterator_type from;
     underlying_iterator_type to;
-    pool_type &pool;
+    const pool_type *pool;
     const mask_type *mask;
     mask_type bitmask;
 };
 
 
-template<template<typename...> class Pool, typename Entity, typename... Components, typename Type>
-class View<Pool<Entity, Components...>, Type> final {
-    using pool_type = Pool<Entity, Components...>;
+template<typename Pool, std::size_t Ident>
+class View<Pool, Ident> final {
+    using pool_type = std::tuple_element_t<Ident, Pool>;
 
 public:
+    using iterator_type = typename pool_type::iterator_type;
+    using entity_type = typename pool_type::index_type;
     using size_type = typename pool_type::size_type;
-    using iterator_type = typename pool_type::const_iterator_type;
-    using const_iterator_type = iterator_type;
+    using raw_type = typename pool_type::type;
 
-    explicit View(pool_type &pool) noexcept
-        : pool{pool}
+    explicit View(const Pool *pool) noexcept
+        : pool{&std::get<Ident>(*pool)}
     {}
 
-    const_iterator_type cbegin() const noexcept {
-        return pool.template cbegin<Type>();
+    raw_type * raw() noexcept {
+        return pool->raw();
     }
 
-    iterator_type begin() noexcept {
-        return pool.template begin<Type>();
+    const raw_type * raw() const noexcept {
+        return pool->raw();
     }
 
-    const_iterator_type cend() const noexcept {
-        return pool.template cend<Type>();
+    const entity_type * data() const noexcept {
+        return pool->data();
     }
 
-    iterator_type end() noexcept {
-        return pool.template end<Type>();
+    size_type size() const noexcept {
+        return pool->size();
     }
 
-    size_type size() const noexcept {
-        return pool.template size<Type>();
+    iterator_type begin() const noexcept {
+        return pool->begin();
+    }
+
+    iterator_type end() const noexcept {
+        return pool->end();
     }
 
 private:
-    pool_type &pool;
+    const pool_type *pool;
 };
 
 
-template<typename>
+template<typename...>
 class Registry;
 
 
-template<template<typename...> class Pool, typename Entity, typename... Components>
-class Registry<Pool<Entity, Components...>> {
-    static_assert(sizeof...(Components) > 1, "!");
+template<template<typename...> class... Pool, typename Entity, typename... Component>
+class Registry<Pool<Entity, Component>...> {
+    using pool_type = std::tuple<Pool<Entity, Component>...>;
+    using mask_type = std::bitset<sizeof...(Component)+1>;
 
-    using pool_type = Pool<Entity, Components...>;
-    using mask_type = std::bitset<sizeof...(Components)+1>;
-
-    static constexpr auto validity_bit = sizeof...(Components);
+    static constexpr auto validity_bit = sizeof...(Component);
 
 public:
-    using entity_type = typename pool_type::entity_type;
+    using entity_type = Entity;
     using size_type = typename std::vector<mask_type>::size_type;
 
+    template<typename... Comp>
+    using view_type = View<pool_type, ident<Component...>.template get<Comp>()...>;
+
 private:
     template<typename Comp>
     void clone(entity_type to, entity_type from) {
-        if(entities[from].test(ident<Components...>.template get<Comp>())) {
-            assign<Comp>(to, pool.template get<Comp>(from));
+        constexpr auto index = ident<Component...>.template get<Comp>();
+
+        if(entities[from].test(index)) {
+            assign<Comp>(to, std::get<index>(pool).get(from));
         }
     }
 
     template<typename Comp>
     void sync(entity_type to, entity_type from) {
-        bool src = entities[from].test(ident<Components...>.template get<Comp>());
-        bool dst = entities[to].test(ident<Components...>.template get<Comp>());
+        constexpr auto index = ident<Component...>.template get<Comp>();
+
+        bool src = entities[from].test(index);
+        bool dst = entities[to].test(index);
 
         if(src && dst) {
             copy<Comp>(to, from);
@@ -223,9 +223,6 @@ private:
     }
 
 public:
-    template<typename... Comp>
-    using view_type = View<pool_type, Comp...>;
-
     template<typename... Args>
     Registry(Args&&... args)
         : pool{std::forward<Args>(args)...}
@@ -247,7 +244,8 @@ public:
 
     template<typename Comp>
     bool empty() const noexcept {
-        return pool.template empty<Comp>();
+        constexpr auto index = ident<Component...>.template get<Comp>();
+        return std::get<index>(pool).empty();
     }
 
     bool empty() const noexcept {
@@ -286,7 +284,7 @@ public:
     void destroy(entity_type entity) {
         assert(valid(entity));
         using accumulator_type = int[];
-        accumulator_type accumulator = { 0, (reset<Components>(entity), 0)... };
+        accumulator_type accumulator = { 0, (reset<Component>(entity), 0)... };
         available.push_back(entity);
         entities[entity].reset();
         (void)accumulator;
@@ -295,15 +293,17 @@ public:
     template<typename Comp, typename... Args>
     Comp & assign(entity_type entity, Args... args) {
         assert(valid(entity));
-        entities[entity].set(ident<Components...>.template get<Comp>());
-        return pool.template construct<Comp>(entity, args...);
+        constexpr auto index = ident<Component...>.template get<Comp>();
+        entities[entity].set(index);
+        return std::get<index>(pool).construct(entity, args...);
     }
 
     template<typename Comp>
     void remove(entity_type entity) {
         assert(valid(entity));
-        entities[entity].reset(ident<Components...>.template get<Comp>());
-        pool.template destroy<Comp>(entity);
+        constexpr auto index = ident<Component...>.template get<Comp>();
+        entities[entity].reset(index);
+        std::get<index>(pool).destroy(entity);
     }
 
     template<typename... Comp>
@@ -312,31 +312,36 @@ public:
         using accumulator_type = bool[];
         bool all = true;
         auto &mask = entities[entity];
-        accumulator_type accumulator = { true, (all = all && mask.test(ident<Components...>.template get<Comp>()))... };
+        accumulator_type accumulator = { true, (all = all && mask.test(ident<Component...>.template get<Comp>()))... };
         (void)accumulator;
         return all;
     }
 
     template<typename Comp>
     const Comp & get(entity_type entity) const noexcept {
-        return pool.template get<Comp>(entity);
+        constexpr auto index = ident<Component...>.template get<Comp>();
+        return std::get<index>(pool).get(entity);
     }
 
     template<typename Comp>
     Comp & get(entity_type entity) noexcept {
-        return pool.template get<Comp>(entity);
+        constexpr auto index = ident<Component...>.template get<Comp>();
+        return std::get<index>(pool).get(entity);
     }
 
     template<typename Comp, typename... Args>
     Comp & replace(entity_type entity, Args... args) {
-        return (pool.template get<Comp>(entity) = Comp{args...});
+        constexpr auto index = ident<Component...>.template get<Comp>();
+        return (std::get<index>(pool).get(entity) = Comp{args...});
     }
 
     template<typename Comp, typename... Args>
     Comp & accomodate(entity_type entity, Args... args) {
         assert(valid(entity));
 
-        return (entities[entity].test(ident<Components...>.template get<Comp>())
+        constexpr auto index = ident<Component...>.template get<Comp>();
+
+        return (entities[entity].test(index)
                 ? this->template replace<Comp>(entity, std::forward<Args>(args)...)
                 : this->template assign<Comp>(entity, std::forward<Args>(args)...));
     }
@@ -345,19 +350,21 @@ public:
         assert(valid(from));
         using accumulator_type = int[];
         auto to = create();
-        accumulator_type accumulator = { 0, (clone<Components>(to, from), 0)... };
+        accumulator_type accumulator = { 0, (clone<Component>(to, from), 0)... };
         (void)accumulator;
         return to;
     }
 
     template<typename Comp>
     Comp & copy(entity_type to, entity_type from) {
-        return (pool.template get<Comp>(to) = pool.template get<Comp>(from));
+        constexpr auto index = ident<Component...>.template get<Comp>();
+        auto &&cpool = std::get<index>(pool);
+        return (cpool.get(to) = cpool.get(from));
     }
 
     void copy(entity_type to, entity_type from) {
         using accumulator_type = int[];
-        accumulator_type accumulator = { 0, (sync<Components>(to, from), 0)... };
+        accumulator_type accumulator = { 0, (sync<Component>(to, from), 0)... };
         (void)accumulator;
     }
 
@@ -365,33 +372,39 @@ public:
     void reset(entity_type entity) {
         assert(valid(entity));
 
-        if(entities[entity].test(ident<Components...>.template get<Comp>())) {
+        constexpr auto index = ident<Component...>.template get<Comp>();
+
+        if(entities[entity].test(index)) {
             remove<Comp>(entity);
         }
     }
 
     template<typename Comp>
     void reset() {
+        constexpr auto index = ident<Component...>.template get<Comp>();
+
         for(entity_type entity = 0, last = entity_type(entities.size()); entity < last; ++entity) {
-            if(entities[entity].test(ident<Components...>.template get<Comp>())) {
+            if(entities[entity].test(index)) {
                 remove<Comp>(entity);
             }
         }
     }
 
     void reset() {
+        using accumulator_type = int[];
+        accumulator_type acc = { 0, (std::get<ident<Component...>.template get<Component>()>(pool).reset(), 0)... };
         entities.clear();
         available.clear();
-        pool.reset();
+        (void)acc;
     }
 
     template<typename... Comp>
     std::enable_if_t<(sizeof...(Comp) == 1), view_type<Comp...>>
-    view() noexcept { return view_type<Comp...>{pool}; }
+    view() noexcept { return view_type<Comp...>{&pool}; }
 
     template<typename... Comp>
     std::enable_if_t<(sizeof...(Comp) > 1), view_type<Comp...>>
-    view() noexcept { return view_type<Comp...>{pool, entities.data()}; }
+    view() noexcept { return view_type<Comp...>{&pool, entities.data()}; }
 
 private:
     std::vector<mask_type> entities;
@@ -400,12 +413,12 @@ private:
 };
 
 
-template<typename Entity, typename... Components>
-using StandardRegistry = Registry<ComponentPool<Entity, Components...>>;
+template<typename Entity, typename... Component>
+using StandardRegistry = Registry<SparseSet<Entity, Component>...>;
 
 
-template<typename... Components>
-using DefaultRegistry = Registry<ComponentPool<std::uint32_t, Components...>>;
+template<typename... Component>
+using DefaultRegistry = Registry<SparseSet<std::uint32_t, Component>...>;
 
 
 }

+ 205 - 0
src/sparse_set.hpp

@@ -0,0 +1,205 @@
+#ifndef ENTT_COMPONENT_POOL_HPP
+#define ENTT_COMPONENT_POOL_HPP
+
+
+#include <utility>
+#include <vector>
+#include <cstddef>
+#include <cassert>
+
+
+namespace entt {
+
+
+template<typename...>
+class SparseSet;
+
+
+template<typename Index>
+class SparseSet<Index> {
+    struct SparseSetIterator;
+
+public:
+    using index_type = Index;
+    using pos_type = index_type;
+    using size_type = std::size_t;
+    using iterator_type = SparseSetIterator;
+
+private:
+    struct SparseSetIterator {
+        using value_type = index_type;
+
+        SparseSetIterator(const std::vector<index_type> *direct, size_type pos)
+            : direct{direct}, pos{pos}
+        {}
+
+        SparseSetIterator & operator++() noexcept {
+            return --pos, *this;
+        }
+
+        SparseSetIterator operator++(int) noexcept {
+            SparseSetIterator orig = *this;
+            return ++(*this), orig;
+        }
+
+        bool operator==(const SparseSetIterator &other) const noexcept {
+            return other.pos == pos && other.direct == direct;
+        }
+
+        bool operator!=(const SparseSetIterator &other) const noexcept {
+            return !(*this == other);
+        }
+
+        value_type operator*() const noexcept {
+            return (*direct)[pos-1];
+        }
+
+    private:
+        const std::vector<index_type> *direct;
+        size_type pos;
+    };
+
+    inline bool valid(Index idx) const noexcept {
+        return idx < reverse.size() && reverse[idx] < direct.size() && direct[reverse[idx]] == idx;
+    }
+
+public:
+    explicit SparseSet() = default;
+
+    SparseSet(const SparseSet &) = delete;
+    SparseSet(SparseSet &&) = default;
+
+    virtual ~SparseSet() noexcept {
+        assert(empty());
+    }
+
+    SparseSet & operator=(const SparseSet &) = delete;
+    SparseSet & operator=(SparseSet &&) = default;
+
+    bool empty() const noexcept {
+        return direct.empty();
+    }
+
+    const index_type * data() const noexcept {
+        return direct.data();
+    }
+
+    size_type size() const noexcept {
+        return direct.size();
+    }
+
+    iterator_type begin() const noexcept {
+        return SparseSetIterator{&direct, direct.size()};
+    }
+
+    iterator_type end() const noexcept {
+        return SparseSetIterator{&direct, 0};
+    }
+
+    bool has(index_type idx) const noexcept {
+        return valid(idx);
+    }
+
+    pos_type get(index_type idx) const noexcept {
+        assert(valid(idx));
+        return reverse[idx];
+    }
+
+    pos_type construct(index_type idx) {
+        assert(!valid(idx));
+
+        if(!(idx < reverse.size())) {
+            reverse.resize(idx+1);
+        }
+
+        auto pos = pos_type(direct.size());
+        reverse[idx] = pos;
+        direct.emplace_back(idx);
+
+        return pos;
+    }
+
+    pos_type destroy(index_type idx) {
+        assert(valid(idx));
+
+        auto last = direct.size() - 1;
+        auto pos = reverse[idx];
+
+        reverse[direct[last]] = pos;
+        direct[pos] = direct[last];
+        direct.pop_back();
+
+        return pos;
+    }
+
+    void reset() {
+        reverse.resize(0);
+        direct.clear();
+    }
+
+private:
+    std::vector<pos_type> reverse;
+    std::vector<index_type> direct;
+};
+
+
+template<typename Index, typename Type>
+class SparseSet<Index, Type> final: public SparseSet<Index> {
+public:
+    using type = Type;
+    using index_type = typename SparseSet<Index>::index_type;
+    using pos_type = typename SparseSet<Index>::pos_type;
+    using size_type = typename SparseSet<Index>::size_type;
+    using iterator_type = typename SparseSet<Index>::iterator_type;
+
+    explicit SparseSet() = default;
+
+    SparseSet(const SparseSet &) = delete;
+    SparseSet(SparseSet &&) = default;
+
+    SparseSet & operator=(const SparseSet &) = delete;
+    SparseSet & operator=(SparseSet &&) = default;
+
+    type * raw() noexcept {
+        return instances.data();
+    }
+
+    const type * raw() const noexcept {
+        return instances.data();
+    }
+
+    const type & get(index_type idx) const noexcept {
+        return instances[SparseSet<Index>::get(idx)];
+    }
+
+    type & get(index_type idx) noexcept {
+        return const_cast<type &>(const_cast<const SparseSet *>(this)->get(idx));
+    }
+
+    template<typename... Args>
+    type & construct(index_type idx, Args... args) {
+        SparseSet<Index>::construct(idx);
+        instances.push_back({ args... });
+        return instances.back();
+    }
+
+    void destroy(index_type idx) {
+        auto pos = SparseSet<Index>::destroy(idx);
+        instances[pos] = std::move(instances[SparseSet<Index>::size()]);
+        instances.pop_back();
+    }
+
+    void reset() {
+        SparseSet<Index>::reset();
+        instances.clear();
+    }
+
+private:
+    std::vector<type> instances;
+};
+
+
+}
+
+
+#endif // ENTT_COMPONENT_POOL_HPP

+ 97 - 0
src/tag_handler.hpp

@@ -0,0 +1,97 @@
+#ifndef ENTT_TAG_HANDLER_HPP
+#define ENTT_TAG_HANDLER_HPP
+
+
+#include <type_traits>
+#include <utility>
+#include <cstddef>
+#include <cassert>
+
+
+namespace entt {
+
+
+template<typename Index, typename Type>
+struct TagHandler {
+    using type = Type;
+    using index_type = Index;
+    using pos_type = index_type;
+    using size_type = std::size_t;
+    using iterator_type = const type *;
+
+private:
+    inline bool valid(index_type idx) const noexcept {
+        return !idx && tag;
+    }
+
+public:
+    explicit TagHandler() = default;
+
+    TagHandler(const TagHandler &) = delete;
+    TagHandler(TagHandler &&) = default;
+
+    ~TagHandler() noexcept {
+        assert(not tag);
+    }
+
+    TagHandler & operator=(const TagHandler &) = delete;
+    TagHandler & operator=(TagHandler &&) = default;
+
+    bool empty() const noexcept {
+        return !tag;
+    }
+
+    size_type size() const noexcept {
+        return tag ? size_type{1} : size_type{0};
+    }
+
+    iterator_type begin() const noexcept {
+        return tag;
+    }
+
+    iterator_type end() const noexcept {
+        return tag ? tag + 1 : tag;
+    }
+
+    bool has(index_type idx) const noexcept {
+        return valid(idx);
+    }
+
+    const type & get(index_type idx) const noexcept {
+        assert(valid(idx));
+        return *tag;
+    }
+
+    type & get(index_type idx) noexcept {
+        return const_cast<type &>(const_cast<const TagHandler *>(this)->get(idx));
+    }
+
+    template<typename... Args>
+    type & construct(index_type idx, Args... args) {
+        assert(!valid(idx));
+        tag = new(&chunk) Type{std::forward<Args>(args)...};
+        return *tag;
+    }
+
+    void destroy(index_type idx) {
+        assert(valid(idx));
+        tag->~Type();
+        tag = nullptr;
+    }
+
+    void reset() {
+        if(tag) {
+            destroy(index_type{0});
+        }
+    }
+
+private:
+    std::aligned_storage_t<sizeof(Type), alignof(Type)> chunk;
+    Type *tag{nullptr};
+};
+
+
+}
+
+
+#endif // ENTT_TAG_HANDLER_HPP

+ 1 - 1
test/CMakeLists.txt

@@ -11,7 +11,7 @@ set(TARGET_BENCHMARK benchmark)
 
 # Test TARGET_ENTT
 
-add_executable(${TARGET_ENTT} component_pool.cpp registry.cpp)
+add_executable(${TARGET_ENTT} registry.cpp sparse_set.cpp tag_handler.cpp)
 target_include_directories(${TARGET_ENTT} PRIVATE ${PROJECT_SRC_DIR})
 target_link_libraries(${TARGET_ENTT} PRIVATE ${COMMON_LINK_LIBS})
 add_test(NAME ${TARGET_ENTT} COMMAND ${TARGET_ENTT})

+ 1 - 0
test/benchmark.cpp

@@ -38,6 +38,7 @@ TEST(DefaultRegistry, Construct) {
     std::cout << "Constructing 10000000 entities" << std::endl;
 
     Timer timer;
+
     for (uint64_t i = 0; i < 10000000L; i++) {
         registry.create();
     }

+ 0 - 166
test/component_pool.cpp

@@ -1,166 +0,0 @@
-#include <cstddef>
-#include <gtest/gtest.h>
-#include <component_pool.hpp>
-
-TEST(ComponentPool, Functionalities) {
-    using pool_type = entt::ComponentPool<std::uint8_t, int, double>;
-
-    pool_type pool{0};
-
-    ASSERT_TRUE(pool.empty<int>());
-    ASSERT_TRUE(pool.empty<double>());
-    ASSERT_EQ(pool.capacity<int>(), pool_type::size_type{0});
-    ASSERT_EQ(pool.capacity<double>(), pool_type::size_type{0});
-    ASSERT_EQ(pool.size<int>(), pool_type::size_type{0});
-    ASSERT_EQ(pool.size<double>(), pool_type::size_type{0});
-    ASSERT_EQ(pool.begin<int>(), pool.end<int>());
-    ASSERT_EQ(pool.begin<double>(), pool.end<double>());
-    ASSERT_FALSE(pool.has<int>(0));
-    ASSERT_FALSE(pool.has<double>(0));
-}
-
-TEST(ComponentPool, ConstructDestroy) {
-    using pool_type = entt::ComponentPool<std::uint8_t, double, int>;
-
-    pool_type pool{4};
-
-    ASSERT_EQ(pool.construct<int>(0, 42), 42);
-    ASSERT_FALSE(pool.empty<int>());
-    ASSERT_TRUE(pool.empty<double>());
-    ASSERT_EQ(pool.capacity<int>(), pool_type::size_type{4});
-    ASSERT_EQ(pool.capacity<double>(), pool_type::size_type{4});
-    ASSERT_EQ(pool.size<int>(), pool_type::size_type{1});
-    ASSERT_EQ(pool.size<double>(), pool_type::size_type{0});
-    ASSERT_TRUE(pool.has<int>(0));
-    ASSERT_FALSE(pool.has<double>(0));
-    ASSERT_FALSE(pool.has<int>(1));
-    ASSERT_FALSE(pool.has<double>(1));
-
-    ASSERT_EQ(pool.construct<int>(1), 0);
-    ASSERT_FALSE(pool.empty<int>());
-    ASSERT_TRUE(pool.empty<double>());
-    ASSERT_EQ(pool.capacity<int>(), pool_type::size_type{4});
-    ASSERT_EQ(pool.capacity<double>(), pool_type::size_type{4});
-    ASSERT_EQ(pool.size<int>(), pool_type::size_type{2});
-    ASSERT_EQ(pool.size<double>(), pool_type::size_type{0});
-    ASSERT_TRUE(pool.has<int>(0));
-    ASSERT_FALSE(pool.has<double>(0));
-    ASSERT_TRUE(pool.has<int>(1));
-    ASSERT_FALSE(pool.has<double>(1));
-    ASSERT_NE(pool.get<int>(0), pool.get<int>(1));
-    ASSERT_NE(&pool.get<int>(0), &pool.get<int>(1));
-
-    ASSERT_NO_THROW(pool.destroy<int>(0));
-    ASSERT_FALSE(pool.empty<int>());
-    ASSERT_TRUE(pool.empty<double>());
-    ASSERT_EQ(pool.capacity<int>(), pool_type::size_type{4});
-    ASSERT_EQ(pool.capacity<double>(), pool_type::size_type{4});
-    ASSERT_EQ(pool.size<int>(), pool_type::size_type{1});
-    ASSERT_EQ(pool.size<double>(), pool_type::size_type{0});
-    ASSERT_FALSE(pool.has<int>(0));
-    ASSERT_FALSE(pool.has<double>(0));
-    ASSERT_TRUE(pool.has<int>(1));
-    ASSERT_FALSE(pool.has<double>(1));
-
-    ASSERT_NO_THROW(pool.destroy<int>(1));
-    ASSERT_TRUE(pool.empty<int>());
-    ASSERT_TRUE(pool.empty<double>());
-    ASSERT_EQ(pool.capacity<int>(), pool_type::size_type{4});
-    ASSERT_EQ(pool.capacity<double>(), pool_type::size_type{4});
-    ASSERT_EQ(pool.size<int>(), pool_type::size_type{0});
-    ASSERT_EQ(pool.size<int>(), pool_type::size_type{0});
-    ASSERT_FALSE(pool.has<int>(0));
-    ASSERT_FALSE(pool.has<double>(0));
-    ASSERT_FALSE(pool.has<int>(1));
-    ASSERT_FALSE(pool.has<double>(1));
-
-    int *comp[] = {
-        &pool.construct<int>(0, 0),
-        &pool.construct<int>(1, 1),
-        nullptr,
-        &pool.construct<int>(3, 3)
-    };
-
-    ASSERT_FALSE(pool.empty<int>());
-    ASSERT_TRUE(pool.empty<double>());
-    ASSERT_EQ(pool.capacity<int>(), pool_type::size_type{4});
-    ASSERT_EQ(pool.capacity<double>(), pool_type::size_type{4});
-    ASSERT_EQ(pool.size<int>(), pool_type::size_type{3});
-    ASSERT_EQ(pool.size<double>(), pool_type::size_type{0});
-    ASSERT_TRUE(pool.has<int>(0));
-    ASSERT_FALSE(pool.has<double>(0));
-    ASSERT_TRUE(pool.has<int>(1));
-    ASSERT_FALSE(pool.has<double>(1));
-    ASSERT_FALSE(pool.has<int>(2));
-    ASSERT_FALSE(pool.has<double>(2));
-    ASSERT_TRUE(pool.has<int>(3));
-    ASSERT_FALSE(pool.has<double>(3));
-    ASSERT_EQ(&pool.get<int>(0), comp[0]);
-    ASSERT_EQ(&pool.get<int>(1), comp[1]);
-    ASSERT_EQ(&pool.get<int>(3), comp[3]);
-    ASSERT_EQ(pool.get<int>(0), 0);
-    ASSERT_EQ(pool.get<int>(1), 1);
-    ASSERT_EQ(pool.get<int>(3), 3);
-
-    ASSERT_NO_THROW(pool.destroy<int>(0));
-    ASSERT_NO_THROW(pool.destroy<int>(1));
-    ASSERT_NO_THROW(pool.destroy<int>(3));
-}
-
-TEST(ComponentPool, HasGet) {
-    using pool_type = entt::ComponentPool<std::uint8_t, int, char>;
-
-    pool_type pool;
-    const pool_type &cpool = pool;
-
-    int &comp = pool.construct<int>(0, 42);
-
-    ASSERT_EQ(pool.get<int>(0), comp);
-    ASSERT_EQ(pool.get<int>(0), 42);
-    ASSERT_TRUE(pool.has<int>(0));
-
-    ASSERT_EQ(cpool.get<int>(0), comp);
-    ASSERT_EQ(cpool.get<int>(0), 42);
-    ASSERT_TRUE(cpool.has<int>(0));
-
-    ASSERT_NO_THROW(pool.destroy<int>(0));
-}
-
-TEST(ComponentPool, BeginEndReset) {
-    using pool_type = entt::ComponentPool<std::uint8_t, int, char>;
-
-    pool_type pool{2};
-
-    ASSERT_EQ(pool.construct<int>(0, 0), 0);
-    ASSERT_EQ(pool.construct<int>(2, 2), 2);
-    ASSERT_EQ(pool.construct<int>(3, 3), 3);
-    ASSERT_EQ(pool.construct<int>(1, 1), 1);
-
-    ASSERT_EQ(pool.size<int>(), decltype(pool.size<int>()){4});
-    ASSERT_EQ(*(pool.begin<int>()+0), typename pool_type::entity_type{0});
-    ASSERT_EQ(*(pool.begin<int>()+1), typename pool_type::entity_type{2});
-    ASSERT_EQ(*(pool.begin<int>()+2), typename pool_type::entity_type{3});
-    ASSERT_EQ(*(pool.begin<int>()+3), typename pool_type::entity_type{1});
-
-    pool.destroy<int>(2);
-
-    ASSERT_EQ(pool.size<int>(), decltype(pool.size<int>()){3});
-    ASSERT_EQ(*(pool.begin<int>()+0), typename pool_type::entity_type{0});
-    ASSERT_EQ(*(pool.begin<int>()+1), typename pool_type::entity_type{1});
-    ASSERT_EQ(*(pool.begin<int>()+2), typename pool_type::entity_type{3});
-
-    ASSERT_EQ(pool.construct<char>(0, 'c'), 'c');
-
-    ASSERT_FALSE(pool.empty<int>());
-    ASSERT_FALSE(pool.empty<char>());
-
-    ASSERT_NO_THROW(pool.reset<char>());
-
-    ASSERT_FALSE(pool.empty<int>());
-    ASSERT_TRUE(pool.empty<char>());
-
-    ASSERT_NO_THROW(pool.reset());
-
-    ASSERT_TRUE(pool.empty<int>());
-    ASSERT_TRUE(pool.empty<char>());
-}

+ 7 - 2
test/registry.cpp

@@ -199,7 +199,7 @@ TEST(DefaultRegistry, ViewMultipleComponent) {
     ASSERT_NO_THROW(registry.reset());
 }
 
-TEST(DefaultRegistry, EmptyViewSingleComponent) {
+TEST(DefaultRegistry, ViewSingleComponentEmpty) {
     using registry_type = entt::DefaultRegistry<char, int, double>;
 
     registry_type registry;
@@ -211,10 +211,15 @@ TEST(DefaultRegistry, EmptyViewSingleComponent) {
 
     ASSERT_EQ(view.size(), registry_type::size_type{0});
 
+    for(auto entity: view) {
+        (void)entity;
+        FAIL();
+    }
+
     registry.reset();
 }
 
-TEST(DefaultRegistry, EmptyViewMultipleComponent) {
+TEST(DefaultRegistry, ViewMultipleComponentEmpty) {
     using registry_type = entt::DefaultRegistry<char, int, float, double>;
 
     registry_type registry;

+ 90 - 0
test/sparse_set.cpp

@@ -0,0 +1,90 @@
+#include <gtest/gtest.h>
+#include <sparse_set.hpp>
+
+TEST(SparseSetNoType, Functionalities) {
+    using SparseSet = entt::SparseSet<unsigned int>;
+
+    SparseSet set;
+
+    ASSERT_TRUE(set.empty());
+    ASSERT_EQ(set.size(), 0u);
+    ASSERT_EQ(set.begin(), set.end());
+    ASSERT_FALSE(set.has(0));
+    ASSERT_FALSE(set.has(42));
+
+    ASSERT_EQ(set.construct(42), 0u);
+
+    ASSERT_FALSE(set.empty());
+    ASSERT_EQ(set.size(), 1u);
+    ASSERT_NE(set.begin(), set.end());
+    ASSERT_FALSE(set.has(0));
+    ASSERT_TRUE(set.has(42));
+
+    auto begin = set.begin();
+
+    ASSERT_EQ(*begin, 42u);
+    ASSERT_EQ(++begin, set.end());
+    ASSERT_EQ(set.get(42), 0u);
+
+    ASSERT_EQ(set.destroy(42), 0u);
+
+    ASSERT_TRUE(set.empty());
+    ASSERT_EQ(set.size(), 0u);
+    ASSERT_EQ(set.begin(), set.end());
+    ASSERT_FALSE(set.has(0));
+    ASSERT_FALSE(set.has(42));
+
+    ASSERT_EQ(set.construct(42), 0u);
+
+    set.reset();
+
+    ASSERT_TRUE(set.empty());
+    ASSERT_EQ(set.size(), 0u);
+    ASSERT_EQ(set.begin(), set.end());
+    ASSERT_FALSE(set.has(0));
+    ASSERT_FALSE(set.has(42));
+}
+
+TEST(SparseSetWithType, Functionalities) {
+    using SparseSet = entt::SparseSet<unsigned int, int>;
+
+    SparseSet set;
+
+    ASSERT_TRUE(set.empty());
+    ASSERT_EQ(set.size(), 0u);
+    ASSERT_EQ(set.begin(), set.end());
+    ASSERT_FALSE(set.has(0));
+    ASSERT_FALSE(set.has(42));
+
+    ASSERT_EQ(set.construct(42, 3), 3);
+
+    ASSERT_FALSE(set.empty());
+    ASSERT_EQ(set.size(), 1u);
+    ASSERT_NE(set.begin(), set.end());
+    ASSERT_FALSE(set.has(0));
+    ASSERT_TRUE(set.has(42));
+
+    auto begin = set.begin();
+
+    ASSERT_EQ(*begin, 42u);
+    ASSERT_EQ(set.get(42), 3);
+    ASSERT_EQ(++begin, set.end());
+
+    set.destroy(42);
+
+    ASSERT_TRUE(set.empty());
+    ASSERT_EQ(set.size(), 0u);
+    ASSERT_EQ(set.begin(), set.end());
+    ASSERT_FALSE(set.has(0));
+    ASSERT_FALSE(set.has(42));
+
+    ASSERT_EQ(set.construct(42, 12), 12);
+
+    set.reset();
+
+    ASSERT_TRUE(set.empty());
+    ASSERT_EQ(set.size(), 0u);
+    ASSERT_EQ(set.begin(), set.end());
+    ASSERT_FALSE(set.has(0));
+    ASSERT_FALSE(set.has(42));
+}

+ 46 - 0
test/tag_handler.cpp

@@ -0,0 +1,46 @@
+#include <gtest/gtest.h>
+#include <tag_handler.hpp>
+
+TEST(TagHandler, Functionalities) {
+    using TagHandler = entt::TagHandler<unsigned int, int>;
+
+    TagHandler handler;
+
+    ASSERT_TRUE(handler.empty());
+    ASSERT_EQ(handler.size(), 0u);
+    ASSERT_EQ(handler.begin(), handler.end());
+    ASSERT_FALSE(handler.has(0));
+    ASSERT_FALSE(handler.has(1));
+
+    ASSERT_EQ(handler.construct(0, 42), 42);
+
+    ASSERT_FALSE(handler.empty());
+    ASSERT_EQ(handler.size(), 1u);
+    ASSERT_NE(handler.begin(), handler.end());
+    ASSERT_TRUE(handler.has(0));
+    ASSERT_FALSE(handler.has(1));
+
+    auto begin = handler.begin();
+
+    ASSERT_EQ(*begin, 42);
+    ASSERT_EQ(handler.get(0), 42);
+    ASSERT_EQ(++begin, handler.end());
+
+    handler.destroy(0);
+
+    ASSERT_TRUE(handler.empty());
+    ASSERT_EQ(handler.size(), 0u);
+    ASSERT_EQ(handler.begin(), handler.end());
+    ASSERT_FALSE(handler.has(0));
+    ASSERT_FALSE(handler.has(1));
+
+    ASSERT_EQ(handler.construct(0, 12), 12);
+
+    handler.reset();
+
+    ASSERT_TRUE(handler.empty());
+    ASSERT_EQ(handler.size(), 0u);
+    ASSERT_EQ(handler.begin(), handler.end());
+    ASSERT_FALSE(handler.has(0));
+    ASSERT_FALSE(handler.has(1));
+}