Browse Source

updated entity module

Michele Caini 8 years ago
parent
commit
2f781906b5

+ 66 - 58
src/entt/entity/registry.hpp

@@ -102,6 +102,35 @@ class Registry {
         return pool<Component>();
     }
 
+    template<typename... Component>
+    SparseSet<Entity> & handler() {
+        static_assert(sizeof...(Component) > 1, "!");
+        const auto vtype = view_family::type<Component...>();
+
+        if(!(vtype < handlers.size())) {
+            handlers.resize(vtype + 1);
+        }
+
+        if(!handlers[vtype]) {
+            using accumulator_type = int[];
+
+            auto set = std::make_unique<SparseSet<Entity>>();
+
+            for(auto entity: view<Component...>()) {
+                set->construct(entity);
+            }
+
+            accumulator_type accumulator = {
+                (ensure<Component>().append(*set, &Registry::has<Component...>), 0)...
+            };
+
+            handlers[vtype] = std::move(set);
+            (void)accumulator;
+        }
+
+        return *handlers[vtype];
+    }
+
 public:
     /*! @brief Underlying entity identifier. */
     using entity_type = typename traits_type::entity_type;
@@ -110,20 +139,18 @@ public:
     /*! @brief Unsigned integer type. */
     using size_type = std::size_t;
 
-    /*! @brief Default constructor, explicit on purpose. */
-    explicit Registry() = default;
-    /*! @brief Default destructor. */
-    ~Registry() = default;
+    /*! @brief Default constructor. */
+    Registry() = default;
 
     /*! @brief Copying a registry isn't allowed. */
     Registry(const Registry &) = delete;
-    /*! @brief Moving a registry isn't allowed. */
-    Registry(Registry &&) = delete;
+    /*! @brief Default move constructor. */
+    Registry(Registry &&) = default;
 
     /*! @brief Copying a registry isn't allowed. @return This registry. */
     Registry & operator=(const Registry &) = delete;
-    /*! @brief Moving a registry isn't allowed. @return This registry. */
-    Registry & operator=(Registry &&) = delete;
+    /*! @brief Default move assignment operator. @return This registry. */
+    Registry & operator=(Registry &&) = default;
 
     /**
      * @brief Returns the number of existing components of the given type.
@@ -171,7 +198,7 @@ public:
     }
 
     /**
-     * @brief Verifies if the entity identifier still refers to a valid entity.
+     * @brief Verifies if an entity identifier still refers to a valid entity.
      * @param entity An entity identifier, either valid or not.
      * @return True if the identifier is still valid, false otherwise.
      */
@@ -181,16 +208,16 @@ public:
     }
 
     /**
-     * @brief Returns the version stored along with the given entity identifier.
+     * @brief Returns the version stored along with an entity identifier.
      * @param entity An entity identifier, either valid or not.
      * @return Version stored along with the given entity identifier.
      */
     version_type version(entity_type entity) const noexcept {
-        return version_type((entity >> traits_type::version_shift) & traits_type::version_mask);
+        return version_type((entity >> traits_type::entity_shift) & traits_type::version_mask);
     }
 
     /**
-     * @brief Returns the actual version for the given entity identifier.
+     * @brief Returns the actual version for an entity identifier.
      *
      * In case entity identifers are stored around, this function can be used to
      * know if they are still valid or the entity has been destroyed and
@@ -209,13 +236,14 @@ public:
     version_type current(entity_type entity) const noexcept {
         const auto entt = entity & traits_type::entity_mask;
         assert(entt < entities.size());
-        return version_type((entities[entt] >> traits_type::version_shift) & traits_type::version_mask);
+        return version_type((entities[entt] >> traits_type::entity_shift) & traits_type::version_mask);
     }
 
     /**
      * @brief Returns a new entity initialized with the given components.
      *
      * There are two kinds of entity identifiers:
+     *
      * * Newly created ones in case no entities have been previously destroyed.
      * * Recycled one with updated versions.
      *
@@ -243,6 +271,7 @@ public:
      * @brief Returns a new entity to which the given components are assigned.
      *
      * There are two kinds of entity identifiers:
+     *
      * * Newly created ones in case no entities have been previously destroyed.
      * * Recycled one with updated versions.
      *
@@ -269,6 +298,7 @@ public:
      * @brief Creates a new entity and returns it.
      *
      * There are two kinds of entity identifiers:
+     *
      * * Newly created ones in case no entities have been previously destroyed.
      * * Recycled one with updated versions.
      *
@@ -287,7 +317,7 @@ public:
         if(available.empty()) {
             entity = entity_type(entities.size());
             assert(entity < traits_type::entity_mask);
-            assert((entity >> traits_type::version_shift) == entity_type{});
+            assert((entity >> traits_type::entity_shift) == entity_type{});
             entities.push_back(entity);
         } else {
             entity = available.back();
@@ -316,8 +346,8 @@ public:
         assert(valid(entity));
 
         const auto entt = entity & traits_type::entity_mask;
-        const auto version = 1 + ((entity >> traits_type::version_shift) & traits_type::version_mask);
-        const auto next = entt | (version << traits_type::version_shift);
+        const auto version = 1 + ((entity >> traits_type::entity_shift) & traits_type::version_mask);
+        const auto next = entt | (version << traits_type::entity_shift);
         entities[entt] = next;
         available.push_back(next);
 
@@ -329,7 +359,7 @@ public:
     }
 
     /**
-     * @brief Assigns the given component to the given entity.
+     * @brief Assigns the given component to an entity.
      *
      * A new instance of the given component is created and initialized with the
      * arguments provided (the component must have a proper constructor or be of
@@ -355,7 +385,7 @@ public:
     }
 
     /**
-     * @brief Removes the given component from the given entity.
+     * @brief Removes the given component from an entity.
      *
      * @warning
      * Attempting to use an invalid entity or to remove a component from an
@@ -370,11 +400,11 @@ public:
     template<typename Component>
     void remove(entity_type entity) {
         assert(valid(entity));
-        return pool<Component>().destroy(entity);
+        pool<Component>().destroy(entity);
     }
 
     /**
-     * @brief Checks if the given entity has all the given components.
+     * @brief Checks if an entity has all the given components.
      *
      * @warning
      * Attempting to use an invalid entity results in undefined behavior.<br/>
@@ -397,7 +427,7 @@ public:
     }
 
     /**
-     * @brief Gets a reference to the given component owned by the given entity.
+     * @brief Returns a reference to the given component for an entity.
      *
      * @warning
      * Attempting to use an invalid entity or to get a component from an entity
@@ -417,7 +447,7 @@ public:
     }
 
     /**
-     * @brief Gets a reference to the given component owned by the given entity.
+     * @brief Returns a reference to the given component for an entity.
      *
      * @warning
      * Attempting to use an invalid entity or to get a component from an entity
@@ -436,7 +466,7 @@ public:
     }
 
     /**
-     * @brief Replaces the given component for the given entity.
+     * @brief Replaces the given component for an entity.
      *
      * A new instance of the given component is created and initialized with the
      * arguments provided (the component must have a proper constructor or be of
@@ -462,7 +492,7 @@ public:
     }
 
     /**
-     * @brief Assigns or replaces the given component to the given entity.
+     * @brief Assigns or replaces the given component for an entity.
      *
      * Equivalent to the following snippet (pseudocode):
      *
@@ -499,7 +529,7 @@ public:
     }
 
     /**
-     * @brief Sorts the pool of the given component.
+     * @brief Sorts the pool of entities for the given component.
      *
      * The order of the elements in a pool is highly affected by assignements
      * of components to entities and deletions. Components are arranged to
@@ -568,7 +598,7 @@ public:
     }
 
     /**
-     * @brief Resets the given component for the given entity.
+     * @brief Resets the given component for an entity.
      *
      * If the entity has an instance of the component, this function removes the
      * component from the entity. Otherwise it does nothing.
@@ -616,7 +646,7 @@ public:
     }
 
     /**
-     * @brief Resets the whole registry.
+     * @brief Resets a whole registry.
      *
      * Destroys all the entities. After a call to `reset`, all the entities
      * previously created are recycled with a new version number. In case entity
@@ -628,8 +658,8 @@ public:
         pools.clear();
 
         for(auto &&entity: entities) {
-            const auto version = 1 + ((entity >> traits_type::version_shift) & traits_type::version_mask);
-            entity = (entity & traits_type::entity_mask) | (version << traits_type::version_shift);
+            const auto version = 1 + ((entity >> traits_type::entity_shift) & traits_type::version_mask);
+            entity = (entity & traits_type::entity_mask) | (version << traits_type::entity_shift);
             available.push_back(entity);
         }
     }
@@ -646,6 +676,7 @@ public:
      *
      * Standard views do their best to iterate the smallest set of candidate
      * entites. In particular:
+     *
      * * Single component views are incredibly fast and iterate a packed array
      * of entities, all of which has the given component.
      * * Multi component views look at the number of entities available for each
@@ -662,7 +693,7 @@ public:
      * @see View<Entity, Component>
      * @see PersistentView
      *
-     * @tparam Component Type of the components used to construct the view.
+     * @tparam Component Type of components used to construct the view.
      * @return A newly created standard view.
      */
     template<typename... Component>
@@ -684,33 +715,11 @@ public:
      * can be prepared with this function. Just use the same set of components
      * that would have been used otherwise to contruct the view.
      *
-     * @tparam Component Types of the components used to prepare the view.
+     * @tparam Component Types of components used to prepare the view.
      */
     template<typename... Component>
     void prepare() {
-        static_assert(sizeof...(Component) > 1, "!");
-        const auto vtype = view_family::type<Component...>();
-
-        if(!(vtype < handlers.size())) {
-            handlers.resize(vtype + 1);
-        }
-
-        if(!handlers[vtype]) {
-            using accumulator_type = int[];
-
-            auto handler = std::make_unique<SparseSet<Entity>>();
-
-            for(auto entity: view<Component...>()) {
-                handler->construct(entity);
-            }
-
-            accumulator_type accumulator = {
-                (ensure<Component>().append(*handler, &Registry::has<Component...>), 0)...
-            };
-
-            handlers[vtype] = std::move(handler);
-            (void)accumulator;
-        }
+        handler<Component...>();
     }
 
     /**
@@ -727,6 +736,7 @@ public:
      * of components grows up and the most of the entities have all the given
      * components.<br/>
      * However they have also drawbacks:
+     *
      * * Each kind of persistent view requires a dedicated data structure that
      * is allocated within the registry and it increases memory pressure.
      * * Internal data structures used to construct persistent views must be
@@ -746,14 +756,12 @@ public:
      * @see View<Entity, Component>
      * @see PersistentView
      *
-     * @tparam Component Types of the components used to construct the view.
+     * @tparam Component Types of components used to construct the view.
      * @return A newly created persistent view.
      */
     template<typename... Component>
     PersistentView<Entity, Component...> persistent() {
-        static_assert(sizeof...(Component) > 1, "!");
-        prepare<Component...>();
-        return PersistentView<Entity, Component...>{*handlers[view_family::type<Component...>()], ensure<Component>()...};
+        return PersistentView<Entity, Component...>{handler<Component...>(), ensure<Component>()...};
     }
 
 private:

+ 48 - 39
src/entt/entity/sparse_set.hpp

@@ -87,6 +87,8 @@ class SparseSet<Entity> {
         std::size_t pos;
     };
 
+    static constexpr Entity in_use = 1 << traits_type::entity_shift;
+
 public:
     /*! @brief Underlying entity identifier. */
     using entity_type = Entity;
@@ -97,24 +99,24 @@ public:
     /*! @brief Input iterator type. */
     using iterator_type = Iterator;
 
-    /*! @brief Default constructor, explicit on purpose. */
-    explicit SparseSet() noexcept = default;
+    /*! @brief Default constructor. */
+    SparseSet() noexcept = default;
+
+    /*! @brief Default destructor. */
+    virtual ~SparseSet() noexcept = default;
 
     /*! @brief Copying a sparse set isn't allowed. */
     SparseSet(const SparseSet &) = delete;
     /*! @brief Default move constructor. */
     SparseSet(SparseSet &&) = default;
 
-    /*! @brief Default destructor. */
-    virtual ~SparseSet() noexcept = default;
-
     /*! @brief Copying a sparse set isn't allowed. @return This sparse set. */
     SparseSet & operator=(const SparseSet &) = delete;
     /*! @brief Default move assignment operator. @return This sparse set. */
     SparseSet & operator=(SparseSet &&) = default;
 
     /**
-     * @brief Returns the number of elements in the sparse set.
+     * @brief Returns the number of elements in a sparse set.
      *
      * The number of elements is also the size of the internal packed array.
      * There is no guarantee that the internal sparse array has the same size.
@@ -128,8 +130,8 @@ public:
     }
 
     /**
-     * @brief Checks whether the sparse set is empty.
-     * @return True is the sparse set is empty, false otherwise.
+     * @brief Checks whether a sparse set is empty.
+     * @return True if the sparse set is empty, false otherwise.
      */
     bool empty() const noexcept {
         return direct.empty();
@@ -188,17 +190,18 @@ public:
     }
 
     /**
-     * @brief Checks if the sparse set contains the given entity.
+     * @brief Checks if a sparse set contains an entity.
      * @param entity A valid entity identifier.
      * @return True if the sparse set contains the entity, false otherwise.
      */
     bool has(entity_type entity) const noexcept {
         const auto entt = entity & traits_type::entity_mask;
-        return entt < reverse.size() && reverse[entt] < direct.size() && direct[reverse[entt]] == entity;
+        // the in-use control bit permits to avoid accessing the direct vector
+        return (entt < reverse.size()) && (reverse[entt] & in_use);
     }
 
     /**
-     * @brief Returns the position of the entity in the sparse set.
+     * @brief Returns the position of an entity in a sparse set.
      *
      * @warning
      * Attempting to get the position of an entity that doesn't belong to the
@@ -211,11 +214,13 @@ public:
      */
     pos_type get(entity_type entity) const noexcept {
         assert(has(entity));
-        return reverse[entity & traits_type::entity_mask];
+        const auto entt = entity & traits_type::entity_mask;
+        // we must get rid of the in-use bit for it's not part of the position
+        return reverse[entt] & ~in_use;
     }
 
     /**
-     * @brief Assigns an entity to the sparse set.
+     * @brief Assigns an entity to a sparse set.
      *
      * @warning
      * Attempting to assign an entity that already belongs to the sparse set
@@ -224,25 +229,23 @@ public:
      * sparse set already contains the given entity.
      *
      * @param entity A valid entity identifier.
-     * @return The position of the entity in the internal packed array.
      */
-    pos_type construct(entity_type entity) {
+    void construct(entity_type entity) {
         assert(!has(entity));
         const auto entt = entity & traits_type::entity_mask;
 
         if(!(entt < reverse.size())) {
-            reverse.resize(entt+1);
+            reverse.resize(entt+1, pos_type{});
         }
 
-        const auto pos = pos_type(direct.size());
-        reverse[entt] = pos;
+        // we exploit the fact that pos_type is equal to entity_type and pos has
+        // traits_type::version_mask bits unused we can use to mark it as in-use
+        reverse[entt] = pos_type(direct.size()) | in_use;
         direct.emplace_back(entity);
-
-        return pos;
     }
 
     /**
-     * @brief Removes the given entity from the sparse set.
+     * @brief Removes an entity from a sparse set.
      *
      * @warning
      * Attempting to remove an entity that doesn't belong to the sparse set
@@ -256,14 +259,18 @@ public:
         assert(has(entity));
         const auto entt = entity & traits_type::entity_mask;
         const auto back = direct.back() & traits_type::entity_mask;
-        const auto pos = reverse[entt];
-        reverse[back] = pos;
+        const auto pos = reverse[entt] & ~in_use;
+        // the order matters: if back and entt are the same (for the sparse set
+        // has size 1), switching the two lines below doesn't work as expected
+        reverse[back] = pos | in_use;
+        reverse[entt] = pos;
+        // swap-and-pop the last element with the selected ont
         direct[pos] = direct.back();
         direct.pop_back();
     }
 
     /**
-     * @brief Swaps the position of the entities in the internal packed array.
+     * @brief Swaps the position of two entities in the internal packed array.
      *
      * For what it's worth, this function affects both the internal sparse array
      * and the internal packed array. Users should not care of that anyway.
@@ -280,10 +287,11 @@ public:
     virtual void swap(entity_type lhs, entity_type rhs) {
         assert(has(lhs));
         assert(has(rhs));
-        const auto le = lhs & traits_type::entity_mask;
-        const auto re = rhs & traits_type::entity_mask;
-        std::swap(direct[reverse[le]], direct[reverse[re]]);
-        std::swap(reverse[le], reverse[re]);
+        auto &le = reverse[lhs & traits_type::entity_mask];
+        auto &re = reverse[rhs & traits_type::entity_mask];
+        // we must get rid of the in-use bit for it's not part of the position
+        std::swap(direct[le & ~in_use], direct[re & ~in_use]);
+        std::swap(le, re);
     }
 
     /**
@@ -316,7 +324,7 @@ public:
     }
 
     /**
-     * @brief Sort entities according to their order in the given sparse set.
+     * @brief Sort entities according to their order in a sparse set.
      *
      * Entities that are part of both the sparse sets are ordered internally
      * according to the order they have in `other`. All the other entities goes
@@ -363,7 +371,7 @@ public:
     }
 
     /**
-     * @brief Resets the sparse set.
+     * @brief Resets a sparse set.
      */
     virtual void reset() {
         reverse.clear();
@@ -371,7 +379,7 @@ public:
     }
 
 private:
-    std::vector<entity_type> reverse;
+    std::vector<pos_type> reverse;
     std::vector<entity_type> direct;
 };
 
@@ -414,8 +422,8 @@ public:
     /*! @brief Input iterator type. */
     using iterator_type = typename underlying_type::iterator_type;
 
-    /*! @brief Default constructor, explicit on purpose. */
-    explicit SparseSet() noexcept = default;
+    /*! @brief Default constructor. */
+    SparseSet() noexcept = default;
 
     /*! @brief Copying a sparse set isn't allowed. */
     SparseSet(const SparseSet &) = delete;
@@ -466,7 +474,7 @@ public:
     }
 
     /**
-     * @brief Returns the object associated to the given entity.
+     * @brief Returns the object associated to an entity.
      *
      * @warning
      * Attempting to use an entity that doesn't belong to the sparse set results
@@ -482,7 +490,7 @@ public:
     }
 
     /**
-     * @brief Returns the object associated to the given entity.
+     * @brief Returns the object associated to an entity.
      *
      * @warning
      * Attempting to use an entity that doesn't belong to the sparse set results
@@ -498,7 +506,7 @@ public:
     }
 
     /**
-     * @brief Assigns an entity to the sparse set and constructs its object.
+     * @brief Assigns an entity to a sparse set and constructs its object.
      *
      * @warning
      * Attempting to use an entity that already belongs to the sparse set
@@ -519,7 +527,7 @@ public:
     }
 
     /**
-     * @brief Removes an entity from the sparse set and destroies its object.
+     * @brief Removes an entity from a sparse set and destroies its object.
      *
      * @warning
      * Attempting to use an entity that doesn't belong to the sparse set results
@@ -530,13 +538,14 @@ public:
      * @param entity A valid entity identifier.
      */
     void destroy(entity_type entity) override {
+        // swaps isn't required here, we are getting rid of the last element
         instances[underlying_type::get(entity)] = std::move(instances.back());
         instances.pop_back();
         underlying_type::destroy(entity);
     }
 
     /**
-     * @brief Swaps the two entities and their objects.
+     * @brief Swaps two entities and their objects.
      *
      * @note
      * This function doesn't swap objects between entities. It exchanges entity
@@ -557,7 +566,7 @@ public:
     }
 
     /**
-     * @brief Resets the sparse set.
+     * @brief Resets a sparse set.
      */
     void reset() override {
         underlying_type::reset();

+ 6 - 3
src/entt/entity/traits.hpp

@@ -22,6 +22,7 @@ struct entt_traits;
  * @brief Entity traits for a 16 bits entity identifier.
  *
  * A 16 bits entity identifier guarantees:
+ *
  * * 12 bits for the entity number (up to 4k entities).
  * * 4 bit for the version (resets in [0-15]).
  */
@@ -37,7 +38,7 @@ struct entt_traits<std::uint16_t> {
     /*! @brief Mask to use to get the version out of an identifier. */
     static constexpr auto version_mask = 0xF;
     /*! @brief Extent of the entity number within an identifier. */
-    static constexpr auto version_shift = 12;
+    static constexpr auto entity_shift = 12;
 };
 
 
@@ -45,6 +46,7 @@ struct entt_traits<std::uint16_t> {
  * @brief Entity traits for a 32 bits entity identifier.
  *
  * A 32 bits entity identifier guarantees:
+ *
  * * 24 bits for the entity number (suitable for almost all the games).
  * * 8 bit for the version (resets in [0-255]).
  */
@@ -60,7 +62,7 @@ struct entt_traits<std::uint32_t> {
     /*! @brief Mask to use to get the version out of an identifier. */
     static constexpr auto version_mask = 0xFF;
     /*! @brief Extent of the entity number within an identifier. */
-    static constexpr auto version_shift = 24;
+    static constexpr auto entity_shift = 24;
 };
 
 
@@ -68,6 +70,7 @@ struct entt_traits<std::uint32_t> {
  * @brief Entity traits for a 64 bits entity identifier.
  *
  * A 64 bits entity identifier guarantees:
+ *
  * * 40 bits for the entity number (an indecently large number).
  * * 24 bit for the version (an indecently large number).
  */
@@ -83,7 +86,7 @@ struct entt_traits<std::uint64_t> {
     /*! @brief Mask to use to get the version out of an identifier. */
     static constexpr auto version_mask = 0xFFFFFF;
     /*! @brief Extent of the entity number within an identifier. */
-    static constexpr auto version_shift = 40;
+    static constexpr auto entity_shift = 40;
 };
 
 

+ 7 - 6
src/entt/entity/view.hpp

@@ -70,6 +70,7 @@ public:
      * @brief Constructs a persistent view around a dedicated pool of entities.
      *
      * A persistent view is created out of:
+     *
      * * A dedicated pool of entities that is shared between all the persistent
      * views of the same type.
      * * A bunch of pools of components to which to refer to get instances.
@@ -77,7 +78,7 @@ public:
      * @param view Shared reference to a dedicated pool of entities.
      * @param pools References to pools of components.
      */
-    explicit PersistentView(view_type &view, pool_type<Component>&... pools) noexcept
+    PersistentView(view_type &view, pool_type<Component>&... pools) noexcept
         : view{view}, pools{pools...}
     {}
 
@@ -240,9 +241,9 @@ public:
      *
      * @note
      * The shared pool of entities and thus its order is affected by the changes
-     * to each and every pool of components that it tracks. Therefore changes to
-     * the pools of components can quickly ruin the order imposed to the pool of
-     * entities shared between the persistent views.
+     * to each and every pool that it tracks. Therefore changes to those pools
+     * can quickly ruin the order imposed to the pool of entities shared between
+     * the persistent views.
      *
      * @tparam Comp Type of the component to use to impose the order.
      */
@@ -368,7 +369,7 @@ public:
      * @param pool A reference to a pool of components.
      * @param other Other references to pools of components.
      */
-    explicit View(pool_type<First> &pool, pool_type<Other>&... other) noexcept
+    View(pool_type<First> &pool, pool_type<Other>&... other) noexcept
         : pools{pool, other...}, view{nullptr}
     {
         reset();
@@ -576,7 +577,7 @@ public:
      * @brief Constructs a view out of a pool of components.
      * @param pool A reference to a pool of components.
      */
-    explicit View(pool_type &pool) noexcept
+    View(pool_type &pool) noexcept
         : pool{pool}
     {}
 

+ 20 - 20
test/entt/entity/benchmark.cpp

@@ -30,7 +30,7 @@ private:
     std::chrono::time_point<std::chrono::system_clock> start;
 };
 
-TEST(DefaultRegistry, Construct) {
+TEST(Benchmark, Construct) {
     entt::DefaultRegistry registry;
 
     std::cout << "Constructing 10000000 entities" << std::endl;
@@ -44,7 +44,7 @@ TEST(DefaultRegistry, Construct) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, Destroy) {
+TEST(Benchmark, Destroy) {
     entt::DefaultRegistry registry;
     std::vector<entt::DefaultRegistry::entity_type> entities{};
 
@@ -63,7 +63,7 @@ TEST(DefaultRegistry, Destroy) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, IterateCreateDeleteSingleComponent) {
+TEST(Benchmark, IterateCreateDeleteSingleComponent) {
     entt::DefaultRegistry registry;
 
     std::cout << "Looping 10000 times creating and deleting a random number of entities" << std::endl;
@@ -87,7 +87,7 @@ TEST(DefaultRegistry, IterateCreateDeleteSingleComponent) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, IterateSingleComponent10M) {
+TEST(Benchmark, IterateSingleComponent10M) {
     entt::DefaultRegistry registry;
 
     std::cout << "Iterating over 10000000 entities, one component" << std::endl;
@@ -101,7 +101,7 @@ TEST(DefaultRegistry, IterateSingleComponent10M) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, IterateTwoComponents10M) {
+TEST(Benchmark, IterateTwoComponents10M) {
     entt::DefaultRegistry registry;
 
     std::cout << "Iterating over 10000000 entities, two components" << std::endl;
@@ -115,7 +115,7 @@ TEST(DefaultRegistry, IterateTwoComponents10M) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, IterateTwoComponents10MHalf) {
+TEST(Benchmark, IterateTwoComponents10MHalf) {
     entt::DefaultRegistry registry;
 
     std::cout << "Iterating over 10000000 entities, two components, half of the entities have all the components" << std::endl;
@@ -130,7 +130,7 @@ TEST(DefaultRegistry, IterateTwoComponents10MHalf) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, IterateTwoComponents10MOne) {
+TEST(Benchmark, IterateTwoComponents10MOne) {
     entt::DefaultRegistry registry;
 
     std::cout << "Iterating over 10000000 entities, two components, only one entity has all the components" << std::endl;
@@ -145,7 +145,7 @@ TEST(DefaultRegistry, IterateTwoComponents10MOne) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, IterateTwoComponentsPersistent10M) {
+TEST(Benchmark, IterateTwoComponentsPersistent10M) {
     entt::DefaultRegistry registry;
     registry.prepare<Position, Velocity>();
 
@@ -160,7 +160,7 @@ TEST(DefaultRegistry, IterateTwoComponentsPersistent10M) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, IterateTwoComponentsPersistent10MHalf) {
+TEST(Benchmark, IterateTwoComponentsPersistent10MHalf) {
     entt::DefaultRegistry registry;
     registry.prepare<Position, Velocity>();
 
@@ -176,7 +176,7 @@ TEST(DefaultRegistry, IterateTwoComponentsPersistent10MHalf) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, IterateTwoComponentsPersistent10MOne) {
+TEST(Benchmark, IterateTwoComponentsPersistent10MOne) {
     entt::DefaultRegistry registry;
     registry.prepare<Position, Velocity>();
 
@@ -192,7 +192,7 @@ TEST(DefaultRegistry, IterateTwoComponentsPersistent10MOne) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, IterateFiveComponents10M) {
+TEST(Benchmark, IterateFiveComponents10M) {
     entt::DefaultRegistry registry;
 
     std::cout << "Iterating over 10000000 entities, five components" << std::endl;
@@ -206,7 +206,7 @@ TEST(DefaultRegistry, IterateFiveComponents10M) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, IterateTenComponents10M) {
+TEST(Benchmark, IterateTenComponents10M) {
     entt::DefaultRegistry registry;
 
     std::cout << "Iterating over 10000000 entities, ten components" << std::endl;
@@ -220,7 +220,7 @@ TEST(DefaultRegistry, IterateTenComponents10M) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, IterateTenComponents10MHalf) {
+TEST(Benchmark, IterateTenComponents10MHalf) {
     entt::DefaultRegistry registry;
 
     std::cout << "Iterating over 10000000 entities, ten components, half of the entities have all the components" << std::endl;
@@ -235,7 +235,7 @@ TEST(DefaultRegistry, IterateTenComponents10MHalf) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, IterateTenComponents10MOne) {
+TEST(Benchmark, IterateTenComponents10MOne) {
     entt::DefaultRegistry registry;
 
     std::cout << "Iterating over 10000000 entities, ten components, only one entity has all the components" << std::endl;
@@ -250,7 +250,7 @@ TEST(DefaultRegistry, IterateTenComponents10MOne) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, IterateFiveComponentsPersistent10M) {
+TEST(Benchmark, IterateFiveComponentsPersistent10M) {
     entt::DefaultRegistry registry;
     registry.prepare<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>();
 
@@ -265,7 +265,7 @@ TEST(DefaultRegistry, IterateFiveComponentsPersistent10M) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, IterateTenComponentsPersistent10M) {
+TEST(Benchmark, IterateTenComponentsPersistent10M) {
     entt::DefaultRegistry registry;
     registry.prepare<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
 
@@ -280,7 +280,7 @@ TEST(DefaultRegistry, IterateTenComponentsPersistent10M) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, IterateTenComponentsPersistent10MHalf) {
+TEST(Benchmark, IterateTenComponentsPersistent10MHalf) {
     entt::DefaultRegistry registry;
     registry.prepare<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
 
@@ -296,7 +296,7 @@ TEST(DefaultRegistry, IterateTenComponentsPersistent10MHalf) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, IterateTenComponentsPersistent10MOne) {
+TEST(Benchmark, IterateTenComponentsPersistent10MOne) {
     entt::DefaultRegistry registry;
     registry.prepare<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
 
@@ -312,7 +312,7 @@ TEST(DefaultRegistry, IterateTenComponentsPersistent10MOne) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, SortSingle) {
+TEST(Benchmark, SortSingle) {
     entt::DefaultRegistry registry;
     std::vector<entt::DefaultRegistry::entity_type> entities{};
 
@@ -332,7 +332,7 @@ TEST(DefaultRegistry, SortSingle) {
     timer.elapsed();
 }
 
-TEST(DefaultRegistry, SortMulti) {
+TEST(Benchmark, SortMulti) {
     entt::DefaultRegistry registry;
     std::vector<entt::DefaultRegistry::entity_type> entities{};
 

+ 155 - 68
test/entt/entity/sparse_set.cpp

@@ -10,7 +10,9 @@ TEST(SparseSetNoType, Functionalities) {
     ASSERT_FALSE(set.has(0));
     ASSERT_FALSE(set.has(42));
 
-    ASSERT_EQ(set.construct(42), 0u);
+    set.construct(42);
+
+    ASSERT_EQ(set.get(42), 0u);
 
     ASSERT_FALSE(set.empty());
     ASSERT_EQ(set.size(), 1u);
@@ -27,7 +29,9 @@ TEST(SparseSetNoType, Functionalities) {
     ASSERT_FALSE(set.has(0));
     ASSERT_FALSE(set.has(42));
 
-    ASSERT_EQ(set.construct(42), 0u);
+    set.construct(42);
+
+    ASSERT_EQ(set.get(42), 0u);
 
     set.reset();
 
@@ -45,9 +49,13 @@ TEST(SparseSetNoType, Functionalities) {
 TEST(SparseSetNoType, DataBeginEnd) {
     entt::SparseSet<unsigned int> set;
 
-    ASSERT_EQ(set.construct(3), 0u);
-    ASSERT_EQ(set.construct(12), 1u);
-    ASSERT_EQ(set.construct(42), 2u);
+    set.construct(3);
+    set.construct(12);
+    set.construct(42);
+
+    ASSERT_EQ(set.get(3), 0u);
+    ASSERT_EQ(set.get(12), 1u);
+    ASSERT_EQ(set.get(42), 2u);
 
     ASSERT_EQ(*(set.data() + 0u), 3u);
     ASSERT_EQ(*(set.data() + 1u), 12u);
@@ -62,6 +70,12 @@ TEST(SparseSetNoType, DataBeginEnd) {
     ASSERT_EQ(begin, end);
 }
 
+TEST(SparseSetWithType, AggregatesMustWork) {
+    struct AggregateType { int value; };
+    // the goal of this test is to enforce the requirements for aggregate types
+    entt::SparseSet<unsigned int, AggregateType>{}.construct(0, 42);
+}
+
 TEST(SparseSetWithType, Functionalities) {
     entt::SparseSet<unsigned int, int> set;
 
@@ -71,8 +85,9 @@ TEST(SparseSetWithType, Functionalities) {
     ASSERT_FALSE(set.has(0));
     ASSERT_FALSE(set.has(42));
 
-    ASSERT_EQ(set.construct(42, 3), 3);
+    set.construct(42, 3);
 
+    ASSERT_EQ(set.get(42), 3);
     ASSERT_FALSE(set.empty());
     ASSERT_EQ(set.size(), 1u);
     ASSERT_NE(set.begin(), set.end());
@@ -88,7 +103,9 @@ TEST(SparseSetWithType, Functionalities) {
     ASSERT_FALSE(set.has(0));
     ASSERT_FALSE(set.has(42));
 
-    ASSERT_EQ(set.construct(42, 12), 12);
+    set.construct(42, 12);
+
+    ASSERT_EQ(set.get(42), 12);
 
     set.reset();
 
@@ -106,9 +123,13 @@ TEST(SparseSetWithType, Functionalities) {
 TEST(SparseSetWithType, RawBeginEnd) {
     entt::SparseSet<unsigned int, int> set;
 
-    ASSERT_EQ(set.construct(3, 3), 3);
-    ASSERT_EQ(set.construct(12, 6), 6);
-    ASSERT_EQ(set.construct(42, 9), 9);
+    set.construct(3, 3);
+    set.construct(12, 6);
+    set.construct(42, 9);
+
+    ASSERT_EQ(set.get(3), 3);
+    ASSERT_EQ(set.get(12), 6);
+    ASSERT_EQ(set.get(42), 9);
 
     ASSERT_EQ(*(set.raw() + 0u), 3);
     ASSERT_EQ(*(set.raw() + 1u), 6);
@@ -126,11 +147,17 @@ TEST(SparseSetWithType, RawBeginEnd) {
 TEST(SparseSetWithType, SortOrdered) {
     entt::SparseSet<unsigned int, int> set;
 
-    ASSERT_EQ(set.construct(12, 12), 12);
-    ASSERT_EQ(set.construct(42, 9), 9);
-    ASSERT_EQ(set.construct(7, 6), 6);
-    ASSERT_EQ(set.construct(3, 3), 3);
-    ASSERT_EQ(set.construct(9, 1), 1);
+    set.construct(12, 12);
+    set.construct(42, 9);
+    set.construct(7, 6);
+    set.construct(3, 3);
+    set.construct(9, 1);
+
+    ASSERT_EQ(set.get(12), 12);
+    ASSERT_EQ(set.get(42), 9);
+    ASSERT_EQ(set.get(7), 6);
+    ASSERT_EQ(set.get(3), 3);
+    ASSERT_EQ(set.get(9), 1);
 
     set.sort([&set](auto lhs, auto rhs) {
         return set.get(lhs) < set.get(rhs);
@@ -156,11 +183,17 @@ TEST(SparseSetWithType, SortOrdered) {
 TEST(SparseSetWithType, SortReverse) {
     entt::SparseSet<unsigned int, int> set;
 
-    ASSERT_EQ(set.construct(12, 1), 1);
-    ASSERT_EQ(set.construct(42, 3), 3);
-    ASSERT_EQ(set.construct(7, 6), 6);
-    ASSERT_EQ(set.construct(3, 9), 9);
-    ASSERT_EQ(set.construct(9, 12), 12);
+    set.construct(12, 1);
+    set.construct(42, 3);
+    set.construct(7, 6);
+    set.construct(3, 9);
+    set.construct(9, 12);
+
+    ASSERT_EQ(set.get(12), 1);
+    ASSERT_EQ(set.get(42), 3);
+    ASSERT_EQ(set.get(7), 6);
+    ASSERT_EQ(set.get(3), 9);
+    ASSERT_EQ(set.get(9), 12);
 
     set.sort([&set](auto lhs, auto rhs) {
         return set.get(lhs) < set.get(rhs);
@@ -186,11 +219,17 @@ TEST(SparseSetWithType, SortReverse) {
 TEST(SparseSetWithType, SortUnordered) {
     entt::SparseSet<unsigned int, int> set;
 
-    ASSERT_EQ(set.construct(12, 6), 6);
-    ASSERT_EQ(set.construct(42, 3), 3);
-    ASSERT_EQ(set.construct(7, 1), 1);
-    ASSERT_EQ(set.construct(3, 9), 9);
-    ASSERT_EQ(set.construct(9, 12), 12);
+    set.construct(12, 6);
+    set.construct(42, 3);
+    set.construct(7, 1);
+    set.construct(3, 9);
+    set.construct(9, 12);
+
+    ASSERT_EQ(set.get(12), 6);
+    ASSERT_EQ(set.get(42), 3);
+    ASSERT_EQ(set.get(7), 1);
+    ASSERT_EQ(set.get(3), 9);
+    ASSERT_EQ(set.get(9), 12);
 
     set.sort([&set](auto lhs, auto rhs) {
         return set.get(lhs) < set.get(rhs);
@@ -218,9 +257,13 @@ TEST(SparseSetWithType, RespectDisjoint) {
     entt::SparseSet<unsigned int, int> rhs;
     const auto &clhs = lhs;
 
-    ASSERT_EQ(lhs.construct(3, 3), 3);
-    ASSERT_EQ(lhs.construct(12, 6), 6);
-    ASSERT_EQ(lhs.construct(42, 9), 9);
+    lhs.construct(3, 3);
+    lhs.construct(12, 6);
+    lhs.construct(42, 9);
+
+    ASSERT_EQ(lhs.get(3), 3);
+    ASSERT_EQ(lhs.get(12), 6);
+    ASSERT_EQ(lhs.get(42), 9);
 
     lhs.respect(rhs);
 
@@ -242,10 +285,15 @@ TEST(SparseSetWithType, RespectOverlap) {
     entt::SparseSet<unsigned int, int> rhs;
     const auto &clhs = lhs;
 
-    ASSERT_EQ(lhs.construct(3, 3), 3);
-    ASSERT_EQ(lhs.construct(12, 6), 6);
-    ASSERT_EQ(lhs.construct(42, 9), 9);
-    ASSERT_EQ(rhs.construct(12, 6), 6);
+    lhs.construct(3, 3);
+    lhs.construct(12, 6);
+    lhs.construct(42, 9);
+    rhs.construct(12, 6);
+
+    ASSERT_EQ(lhs.get(3), 3);
+    ASSERT_EQ(lhs.get(12), 6);
+    ASSERT_EQ(lhs.get(42), 9);
+    ASSERT_EQ(rhs.get(12), 6);
 
     lhs.respect(rhs);
 
@@ -266,18 +314,31 @@ TEST(SparseSetWithType, RespectOrdered) {
     entt::SparseSet<unsigned int, int> lhs;
     entt::SparseSet<unsigned int, int> rhs;
 
-    ASSERT_EQ(lhs.construct(1, 0), 0);
-    ASSERT_EQ(lhs.construct(2, 0), 0);
-    ASSERT_EQ(lhs.construct(3, 0), 0);
-    ASSERT_EQ(lhs.construct(4, 0), 0);
-    ASSERT_EQ(lhs.construct(5, 0), 0);
-
-    ASSERT_EQ(rhs.construct(6, 0), 0);
-    ASSERT_EQ(rhs.construct(1, 0), 0);
-    ASSERT_EQ(rhs.construct(2, 0), 0);
-    ASSERT_EQ(rhs.construct(3, 0), 0);
-    ASSERT_EQ(rhs.construct(4, 0), 0);
-    ASSERT_EQ(rhs.construct(5, 0), 0);
+    lhs.construct(1, 0);
+    lhs.construct(2, 0);
+    lhs.construct(3, 0);
+    lhs.construct(4, 0);
+    lhs.construct(5, 0);
+
+    ASSERT_EQ(lhs.get(1), 0);
+    ASSERT_EQ(lhs.get(2), 0);
+    ASSERT_EQ(lhs.get(3), 0);
+    ASSERT_EQ(lhs.get(4), 0);
+    ASSERT_EQ(lhs.get(5), 0);
+
+    rhs.construct(6, 0);
+    rhs.construct(1, 0);
+    rhs.construct(2, 0);
+    rhs.construct(3, 0);
+    rhs.construct(4, 0);
+    rhs.construct(5, 0);
+
+    ASSERT_EQ(rhs.get(6), 0);
+    ASSERT_EQ(rhs.get(1), 0);
+    ASSERT_EQ(rhs.get(2), 0);
+    ASSERT_EQ(rhs.get(3), 0);
+    ASSERT_EQ(rhs.get(4), 0);
+    ASSERT_EQ(rhs.get(5), 0);
 
     rhs.respect(lhs);
 
@@ -299,18 +360,31 @@ TEST(SparseSetWithType, RespectReverse) {
     entt::SparseSet<unsigned int, int> lhs;
     entt::SparseSet<unsigned int, int> rhs;
 
-    ASSERT_EQ(lhs.construct(1, 0), 0);
-    ASSERT_EQ(lhs.construct(2, 0), 0);
-    ASSERT_EQ(lhs.construct(3, 0), 0);
-    ASSERT_EQ(lhs.construct(4, 0), 0);
-    ASSERT_EQ(lhs.construct(5, 0), 0);
-
-    ASSERT_EQ(rhs.construct(5, 0), 0);
-    ASSERT_EQ(rhs.construct(4, 0), 0);
-    ASSERT_EQ(rhs.construct(3, 0), 0);
-    ASSERT_EQ(rhs.construct(2, 0), 0);
-    ASSERT_EQ(rhs.construct(1, 0), 0);
-    ASSERT_EQ(rhs.construct(6, 0), 0);
+    lhs.construct(1, 0);
+    lhs.construct(2, 0);
+    lhs.construct(3, 0);
+    lhs.construct(4, 0);
+    lhs.construct(5, 0);
+
+    ASSERT_EQ(lhs.get(1), 0);
+    ASSERT_EQ(lhs.get(2), 0);
+    ASSERT_EQ(lhs.get(3), 0);
+    ASSERT_EQ(lhs.get(4), 0);
+    ASSERT_EQ(lhs.get(5), 0);
+
+    rhs.construct(5, 0);
+    rhs.construct(4, 0);
+    rhs.construct(3, 0);
+    rhs.construct(2, 0);
+    rhs.construct(1, 0);
+    rhs.construct(6, 0);
+
+    ASSERT_EQ(rhs.get(5), 0);
+    ASSERT_EQ(rhs.get(4), 0);
+    ASSERT_EQ(rhs.get(3), 0);
+    ASSERT_EQ(rhs.get(2), 0);
+    ASSERT_EQ(rhs.get(1), 0);
+    ASSERT_EQ(rhs.get(6), 0);
 
     rhs.respect(lhs);
 
@@ -332,18 +406,31 @@ TEST(SparseSetWithType, RespectUnordered) {
     entt::SparseSet<unsigned int, int> lhs;
     entt::SparseSet<unsigned int, int> rhs;
 
-    ASSERT_EQ(lhs.construct(1, 0), 0);
-    ASSERT_EQ(lhs.construct(2, 0), 0);
-    ASSERT_EQ(lhs.construct(3, 0), 0);
-    ASSERT_EQ(lhs.construct(4, 0), 0);
-    ASSERT_EQ(lhs.construct(5, 0), 0);
-
-    ASSERT_EQ(rhs.construct(3, 0), 0);
-    ASSERT_EQ(rhs.construct(2, 0), 0);
-    ASSERT_EQ(rhs.construct(6, 0), 0);
-    ASSERT_EQ(rhs.construct(1, 0), 0);
-    ASSERT_EQ(rhs.construct(4, 0), 0);
-    ASSERT_EQ(rhs.construct(5, 0), 0);
+    lhs.construct(1, 0);
+    lhs.construct(2, 0);
+    lhs.construct(3, 0);
+    lhs.construct(4, 0);
+    lhs.construct(5, 0);
+
+    ASSERT_EQ(lhs.get(1), 0);
+    ASSERT_EQ(lhs.get(2), 0);
+    ASSERT_EQ(lhs.get(3), 0);
+    ASSERT_EQ(lhs.get(4), 0);
+    ASSERT_EQ(lhs.get(5), 0);
+
+    rhs.construct(3, 0);
+    rhs.construct(2, 0);
+    rhs.construct(6, 0);
+    rhs.construct(1, 0);
+    rhs.construct(4, 0);
+    rhs.construct(5, 0);
+
+    ASSERT_EQ(rhs.get(3), 0);
+    ASSERT_EQ(rhs.get(2), 0);
+    ASSERT_EQ(rhs.get(6), 0);
+    ASSERT_EQ(rhs.get(1), 0);
+    ASSERT_EQ(rhs.get(4), 0);
+    ASSERT_EQ(rhs.get(5), 0);
 
     rhs.respect(lhs);