Selaa lähdekoodia

registry: create no longer accepts a list of default constructible types to assign to entities

Michele Caini 6 vuotta sitten
vanhempi
commit
469276b8be

+ 1 - 1
TODO

@@ -13,9 +13,9 @@
 * static reflection, hint: template<> meta_type_t<Type>: meta_descriptor<name, func..., props..., etc...>
 * static reflection, hint: template<> meta_type_t<Type>: meta_descriptor<name, func..., props..., etc...>
 * add meta support to registry (eg entity for each component and opaque get)
 * add meta support to registry (eg entity for each component and opaque get)
 * allow for custom stamp functions
 * allow for custom stamp functions
-* merge create() and create(hint), is it a viable solution?
 * observer: user defined filters (eg .replace<T, &function> or .group<T, U, &func>)
 * observer: user defined filters (eg .replace<T, &function> or .group<T, U, &func>)
 * use underlying_type as entity type within pools and registry? it would make different registries work together flawlessy
 * use underlying_type as entity type within pools and registry? it would make different registries work together flawlessy
 * can we write a bool conv func for entt::entity that silently compares it to null?
 * can we write a bool conv func for entt::entity that silently compares it to null?
+* stamp makes sense only when the list of components is empty
 * any-of rule for views/groups (eg entity has A and any of B/C/D)
 * any-of rule for views/groups (eg entity has A and any of B/C/D)
   - get -> all, exclude -> none
   - get -> all, exclude -> none

+ 0 - 4
docs/md/entity.md

@@ -172,10 +172,6 @@ auto view = registry.view<a_component, another_component>();
 registry.destroy(view.begin(), view.end());
 registry.destroy(view.begin(), view.end());
 ```
 ```
 
 
-In all cases, the `create` member function accepts also a list of default
-constructible component types to assign to the entities before to return. It's a
-faster alternative to the creation and subsequent assignment of components.
-
 When an entity is destroyed, the registry can freely reuse it internally with a
 When an entity is destroyed, the registry can freely reuse it internally with a
 slightly different identifier. In particular, the version of an entity is
 slightly different identifier. In particular, the version of an entity is
 increased after destruction.<br/>
 increased after destruction.<br/>

+ 31 - 76
src/entt/entity/registry.hpp

@@ -183,31 +183,6 @@ class basic_registry {
         }
         }
     };
     };
 
 
-    auto generate() {
-        Entity entt;
-
-        if(destroyed == null) {
-            entt = entities.emplace_back(entity_type(entities.size()));
-            // traits_type::entity_mask is reserved to allow for null identifiers
-            ENTT_ASSERT(to_integral(entt) < traits_type::entity_mask);
-        } else {
-            const auto curr = to_integral(destroyed);
-            const auto version = to_integral(entities[curr]) & (traits_type::version_mask << traits_type::entity_shift);
-            destroyed = entity_type{to_integral(entities[curr]) & traits_type::entity_mask};
-            entt = entities[curr] = entity_type{curr | version};
-        }
-
-        return entt;
-    }
-
-    void release(const Entity entity) {
-        // lengthens the implicit list of destroyed entities
-        const auto entt = to_integral(entity) & traits_type::entity_mask;
-        const auto version = ((to_integral(entity) >> traits_type::entity_shift) + 1) << traits_type::entity_shift;
-        entities[entt] = entity_type{to_integral(destroyed) | version};
-        destroyed = entity_type{entt};
-    }
-
     template<typename Component, typename... Args>
     template<typename Component, typename... Args>
     const pool_handler<Component> & assure(Args &&... args) const {
     const pool_handler<Component> & assure(Args &&... args) const {
         static std::size_t index{pools.size()};
         static std::size_t index{pools.size()};
@@ -476,33 +451,28 @@ public:
     /**
     /**
      * @brief Creates a new entity and returns it.
      * @brief Creates a new entity and returns it.
      *
      *
-     * There are two kinds of entity identifiers:
+     * There are two kinds of possible entity identifiers:
      *
      *
      * * Newly created ones in case no entities have been previously destroyed.
      * * Newly created ones in case no entities have been previously destroyed.
      * * Recycled ones with updated versions.
      * * Recycled ones with updated versions.
      *
      *
-     * Users should not care about the type of the returned entity identifier.
-     * In case entity identifers are stored around, the `valid` member
-     * function can be used to know if they are still valid or the entity has
-     * been destroyed and potentially recycled.<br/>
-     * The returned entity has assigned the given components, if any.
-     *
-     * The components must be at least default constructible. A compilation
-     * error will occur otherwhise.
-     *
-     * @tparam Component Types of components to assign to the entity.
-     * @return A valid entity identifier if the component list is empty, a tuple
-     * containing the entity identifier and the references to the components
-     * just created otherwise.
+     * @return A valid entity identifier.
      */
      */
-    template<typename... Component>
-    auto create() {
-        if constexpr(sizeof...(Component) == 0) {
-            return generate();
+    entity_type create() {
+        entity_type entt;
+
+        if(destroyed == null) {
+            entt = entities.emplace_back(entity_type(entities.size()));
+            // traits_type::entity_mask is reserved to allow for null identifiers
+            ENTT_ASSERT(to_integral(entt) < traits_type::entity_mask);
         } else {
         } else {
-            const entity_type entt = generate();
-            return std::tuple_cat(std::make_tuple(entt), std::forward_as_tuple(assign<Component>(entt)...));
+            const auto curr = to_integral(destroyed);
+            const auto version = to_integral(entities[curr]) & (traits_type::version_mask << traits_type::entity_shift);
+            destroyed = entity_type{to_integral(entities[curr]) & traits_type::entity_mask};
+            entt = entities[curr] = entity_type{curr | version};
         }
         }
+
+        return entt;
     }
     }
 
 
     /**
     /**
@@ -510,24 +480,13 @@ public:
      *
      *
      * @sa create
      * @sa create
      *
      *
-     * The components must be at least move and default insertable. A
-     * compilation error will occur otherwhise.
-     *
-     * @tparam Component Types of components to assign to the entity.
      * @tparam It Type of forward iterator.
      * @tparam It Type of forward iterator.
      * @param first An iterator to the first element of the range to generate.
      * @param first An iterator to the first element of the range to generate.
      * @param last An iterator past the last element of the range to generate.
      * @param last An iterator past the last element of the range to generate.
-     * @return No return value if the component list is empty, a tuple
-     * containing the iterators to the lists of components just created and
-     * sorted the same of the entities otherwise.
      */
      */
-    template<typename... Component, typename It>
-    auto create(It first, It last) {
-        std::generate(first, last, [this]() { return generate(); });
-
-        if constexpr(sizeof...(Component) > 0) {
-            return std::make_tuple(assure<Component>().assign(*this, first, last)...);
-        }
+    template<typename It>
+    void create(It first, It last) {
+        std::generate(first, last, [this]() { return create(); });
     }
     }
 
 
     /**
     /**
@@ -538,14 +497,11 @@ public:
      * If the requested entity isn't in use, the suggested identifier is created
      * If the requested entity isn't in use, the suggested identifier is created
      * and returned. Otherwise, a new one will be generated for this purpose.
      * and returned. Otherwise, a new one will be generated for this purpose.
      *
      *
-     * @tparam Component Types of components to assign to the entity.
      * @param hint A desired entity identifier.
      * @param hint A desired entity identifier.
-     * @return A valid entity identifier if the component list is empty, a tuple
-     * containing the entity identifier and the references to the components
-     * just created otherwise.
+     * @return A valid entity identifier.
      */
      */
     template<typename... Component>
     template<typename... Component>
-    auto create(const entity_type hint) {
+    entity_type create(const entity_type hint) {
         ENTT_ASSERT(hint != null);
         ENTT_ASSERT(hint != null);
         entity_type entt;
         entity_type entt;
 
 
@@ -559,7 +515,7 @@ public:
 
 
             entt = entities.emplace_back(hint);
             entt = entities.emplace_back(hint);
         } else if(const auto curr = (to_integral(entities[req]) & traits_type::entity_mask); req == curr) {
         } else if(const auto curr = (to_integral(entities[req]) & traits_type::entity_mask); req == curr) {
-            entt = generate();
+            entt = create();
         } else {
         } else {
             auto *it = &destroyed;
             auto *it = &destroyed;
             for(; (to_integral(*it) & traits_type::entity_mask) != req; it = &entities[to_integral(*it) & traits_type::entity_mask]);
             for(; (to_integral(*it) & traits_type::entity_mask) != req; it = &entities[to_integral(*it) & traits_type::entity_mask]);
@@ -567,20 +523,14 @@ public:
             entt = entities[req] = hint;
             entt = entities[req] = hint;
         }
         }
 
 
-        if constexpr(sizeof...(Component) == 0) {
-            return entt;
-        } else {
-            return std::tuple_cat(std::make_tuple(entt), std::forward_as_tuple(assign<Component>(entt)...));
-        }
+        return entt;
     }
     }
 
 
     /**
     /**
      * @brief Destroys an entity and lets the registry recycle the identifier.
      * @brief Destroys an entity and lets the registry recycle the identifier.
      *
      *
      * When an entity is destroyed, its version is updated and the identifier
      * When an entity is destroyed, its version is updated and the identifier
-     * can be recycled at any time. In case entity identifers are stored around,
-     * the `valid` member function can be used to know if they are still valid
-     * or the entity has been destroyed and potentially recycled.
+     * can be recycled at any time.
      *
      *
      * @warning
      * @warning
      * In case there are listeners that observe the destruction of components
      * In case there are listeners that observe the destruction of components
@@ -607,7 +557,12 @@ public:
 
 
         // just a way to protect users from listeners that attach components
         // just a way to protect users from listeners that attach components
         ENTT_ASSERT(orphan(entity));
         ENTT_ASSERT(orphan(entity));
-        release(entity);
+
+        // lengthens the implicit list of destroyed entities
+        const auto entt = to_integral(entity) & traits_type::entity_mask;
+        const auto version = ((to_integral(entity) >> traits_type::entity_shift) + 1) << traits_type::entity_shift;
+        entities[entt] = entity_type{to_integral(destroyed) | version};
+        destroyed = entity_type{entt};
     }
     }
 
 
     /**
     /**
@@ -658,10 +613,10 @@ public:
      *
      *
      * @tparam Component Type of component to create.
      * @tparam Component Type of component to create.
      * @tparam It Type of input iterator.
      * @tparam It Type of input iterator.
-     * @tparam Args Types of arguments to use to construct the component.
+     * @tparam Args Types of arguments to use to construct the components.
      * @param first An iterator to the first element of the range of entities.
      * @param first An iterator to the first element of the range of entities.
      * @param last An iterator past the last element of the range of entities.
      * @param last An iterator past the last element of the range of entities.
-     * @param args Parameters to use to initialize the component.
+     * @param args Parameters to use to initialize the components.
      * @return An iterator to the list of components just created.
      * @return An iterator to the list of components just created.
      */
      */
     template<typename Component, typename It, typename... Args>
     template<typename Component, typename It, typename... Args>

+ 3 - 1
test/benchmark/benchmark.cpp

@@ -114,7 +114,9 @@ TEST(Benchmark, ConstructManyWithComponents) {
     std::cout << "Constructing 1000000 entities at once with components" << std::endl;
     std::cout << "Constructing 1000000 entities at once with components" << std::endl;
 
 
     timer timer;
     timer timer;
-    registry.create<position, velocity>(entities.begin(), entities.end());
+    registry.create(entities.begin(), entities.end());
+    registry.assign<position>(entities.begin(), entities.end());
+    registry.assign<velocity>(entities.begin(), entities.end());
     timer.elapsed();
     timer.elapsed();
 }
 }
 
 

+ 10 - 2
test/entt/entity/group.cpp

@@ -468,7 +468,11 @@ TEST(NonOwningGroup, TrackEntitiesOnComponentDestruction) {
 
 
 TEST(NonOwningGroup, Less) {
 TEST(NonOwningGroup, Less) {
     entt::registry registry;
     entt::registry registry;
-    const auto entity = std::get<0>(registry.create<int, char, entt::tag<"empty"_hs>>());
+    const auto entity = registry.create();
+
+    registry.assign<int>(entity);
+    registry.assign<char>(entity);
+    registry.assign<entt::tag<"empty"_hs>>(entity);
 
 
     registry.group(entt::get<int, char, entt::tag<"empty"_hs>>).less([entity](const auto entt, int, char) {
     registry.group(entt::get<int, char, entt::tag<"empty"_hs>>).less([entity](const auto entt, int, char) {
         ASSERT_EQ(entity, entt);
         ASSERT_EQ(entity, entt);
@@ -1040,7 +1044,11 @@ TEST(OwningGroup, TrackEntitiesOnComponentDestruction) {
 
 
 TEST(OwningGroup, Less) {
 TEST(OwningGroup, Less) {
     entt::registry registry;
     entt::registry registry;
-    const auto entity = std::get<0>(registry.create<int, char, entt::tag<"empty"_hs>>());
+    const auto entity = registry.create();
+
+    registry.assign<int>(entity);
+    registry.assign<char>(entity);
+    registry.assign<entt::tag<"empty"_hs>>(entity);
 
 
     registry.group<int>(entt::get<char, entt::tag<"empty"_hs>>).less([entity](const auto entt, int, char) {
     registry.group<int>(entt::get<char, entt::tag<"empty"_hs>>).less([entity](const auto entt, int, char) {
         ASSERT_EQ(entity, entt);
         ASSERT_EQ(entity, entt);

+ 4 - 2
test/entt/entity/observer.cpp

@@ -14,7 +14,8 @@ TEST(Observer, Functionalities) {
     ASSERT_EQ(observer.data(), nullptr);
     ASSERT_EQ(observer.data(), nullptr);
     ASSERT_EQ(observer.begin(), observer.end());
     ASSERT_EQ(observer.begin(), observer.end());
 
 
-    const auto entity = std::get<0>(registry.create<int>());
+    const auto entity = registry.create();
+    registry.assign<int>(entity);
 
 
     ASSERT_EQ(observer.size(), 1u);
     ASSERT_EQ(observer.size(), 1u);
     ASSERT_FALSE(observer.empty());
     ASSERT_FALSE(observer.empty());
@@ -263,7 +264,8 @@ TEST(Observer, CrossRulesCornerCase) {
 TEST(Observer, Each) {
 TEST(Observer, Each) {
     entt::registry registry;
     entt::registry registry;
     entt::observer observer{registry, entt::collector.group<int>()};
     entt::observer observer{registry, entt::collector.group<int>()};
-    const auto entity = std::get<0>(registry.create<int>());
+    const auto entity = registry.create();
+    registry.assign<int>(entity);
 
 
     ASSERT_FALSE(observer.empty());
     ASSERT_FALSE(observer.empty());
     ASSERT_EQ(observer.size(), 1u);
     ASSERT_EQ(observer.size(), 1u);

+ 136 - 200
test/entt/entity/registry.cpp

@@ -279,6 +279,93 @@ TEST(Registry, RawData) {
     ASSERT_EQ(*std::as_const(registry).data<int>(), entity);
     ASSERT_EQ(*std::as_const(registry).data<int>(), entity);
 }
 }
 
 
+TEST(Registry, CreateManyEntitiesAtOnce) {
+    entt::registry registry;
+    entt::entity entities[3];
+
+    const auto entity = registry.create();
+    registry.destroy(registry.create());
+    registry.destroy(entity);
+    registry.destroy(registry.create());
+
+    registry.create(std::begin(entities), std::end(entities));
+
+    ASSERT_TRUE(registry.valid(entities[0]));
+    ASSERT_TRUE(registry.valid(entities[1]));
+    ASSERT_TRUE(registry.valid(entities[2]));
+
+    ASSERT_EQ(registry.entity(entities[0]), entt::entity{0});
+    ASSERT_EQ(registry.version(entities[0]), entt::registry::version_type{2});
+
+    ASSERT_EQ(registry.entity(entities[1]), entt::entity{1});
+    ASSERT_EQ(registry.version(entities[1]), entt::registry::version_type{1});
+
+    ASSERT_EQ(registry.entity(entities[2]), entt::entity{2});
+    ASSERT_EQ(registry.version(entities[2]), entt::registry::version_type{0});
+}
+
+TEST(Registry, CreateWithHint) {
+    entt::registry registry;
+    auto e3 = registry.create(entt::entity{3});
+    auto e2 = registry.create(entt::entity{3});
+
+    ASSERT_EQ(e2, entt::entity{2});
+    ASSERT_FALSE(registry.valid(entt::entity{1}));
+    ASSERT_EQ(e3, entt::entity{3});
+
+    registry.destroy(e2);
+
+    ASSERT_EQ(registry.version(e2), 0);
+    ASSERT_EQ(registry.current(e2), 1);
+
+    e2 = registry.create();
+    auto e1 = registry.create(entt::entity{2});
+
+    ASSERT_EQ(registry.entity(e2), entt::entity{2});
+    ASSERT_EQ(registry.version(e2), 1);
+
+    ASSERT_EQ(registry.entity(e1), entt::entity{1});
+    ASSERT_EQ(registry.version(e1), 0);
+
+    registry.destroy(e1);
+    registry.destroy(e2);
+    auto e0 = registry.create(entt::entity{0});
+
+    ASSERT_EQ(e0, entt::entity{0});
+    ASSERT_EQ(registry.version(e0), 0);
+}
+
+TEST(Registry, CreateDestroyEntities) {
+    entt::registry registry;
+    entt::entity pre{}, post{};
+
+    for(int i = 0; i < 10; ++i) {
+        const auto entity = registry.create();
+        registry.assign<double>(entity);
+    }
+
+    registry.reset();
+
+    for(int i = 0; i < 7; ++i) {
+        const auto entity = registry.create();
+        registry.assign<int>(entity);
+        if(i == 3) { pre = entity; }
+    }
+
+    registry.reset();
+
+    for(int i = 0; i < 5; ++i) {
+        const auto entity = registry.create();
+        if(i == 3) { post = entity; }
+    }
+
+    ASSERT_FALSE(registry.valid(pre));
+    ASSERT_TRUE(registry.valid(post));
+    ASSERT_NE(registry.version(pre), registry.version(post));
+    ASSERT_EQ(registry.version(pre) + 1, registry.version(post));
+    ASSERT_EQ(registry.current(pre), registry.current(post));
+}
+
 TEST(Registry, CreateDestroyCornerCase) {
 TEST(Registry, CreateDestroyCornerCase) {
     entt::registry registry;
     entt::registry registry;
 
 
@@ -316,9 +403,9 @@ TEST(Registry, Each) {
     entt::registry::size_type match;
     entt::registry::size_type match;
 
 
     registry.create();
     registry.create();
-    registry.create<int>();
+    registry.assign<int>(registry.create());
     registry.create();
     registry.create();
-    registry.create<int>();
+    registry.assign<int>(registry.create());
     registry.create();
     registry.create();
 
 
     tot = 0u;
     tot = 0u;
@@ -367,9 +454,9 @@ TEST(Registry, Orphans) {
     entt::registry registry;
     entt::registry registry;
     entt::registry::size_type tot{};
     entt::registry::size_type tot{};
 
 
-    registry.create<int>();
+    registry.assign<int>(registry.create());
     registry.create();
     registry.create();
-    registry.create<int>();
+    registry.assign<int>(registry.create());
 
 
     registry.orphans([&](auto) { ++tot; });
     registry.orphans([&](auto) { ++tot; });
     ASSERT_EQ(tot, 1u);
     ASSERT_EQ(tot, 1u);
@@ -385,37 +472,6 @@ TEST(Registry, Orphans) {
     ASSERT_EQ(tot, 0u);
     ASSERT_EQ(tot, 0u);
 }
 }
 
 
-TEST(Registry, CreateDestroyEntities) {
-    entt::registry registry;
-    entt::entity pre{}, post{};
-
-    for(int i = 0; i < 10; ++i) {
-        const auto entity = registry.create();
-        registry.assign<double>(entity);
-    }
-
-    registry.reset();
-
-    for(int i = 0; i < 7; ++i) {
-        const auto entity = registry.create();
-        registry.assign<int>(entity);
-        if(i == 3) { pre = entity; }
-    }
-
-    registry.reset();
-
-    for(int i = 0; i < 5; ++i) {
-        const auto entity = registry.create();
-        if(i == 3) { post = entity; }
-    }
-
-    ASSERT_FALSE(registry.valid(pre));
-    ASSERT_TRUE(registry.valid(post));
-    ASSERT_NE(registry.version(pre), registry.version(post));
-    ASSERT_EQ(registry.version(pre) + 1, registry.version(post));
-    ASSERT_EQ(registry.current(pre), registry.current(post));
-}
-
 TEST(Registry, View) {
 TEST(Registry, View) {
     entt::registry registry;
     entt::registry registry;
     auto mview = registry.view<int, char>();
     auto mview = registry.view<int, char>();
@@ -444,10 +500,14 @@ TEST(Registry, View) {
 
 
 TEST(Registry, NonOwningGroupInitOnFirstUse) {
 TEST(Registry, NonOwningGroupInitOnFirstUse) {
     entt::registry registry;
     entt::registry registry;
+    auto create = [&](auto... component) {
+        const auto entity = registry.create();
+        (registry.assign<decltype(component)>(entity, component), ...);
+    };
 
 
-    std::get<2>(registry.create<int, char>()) = 'c';
-    registry.create<int>();
-    std::get<2>(registry.create<int, char>()) = 'c';
+    create(0, 'c');
+    create(0);
+    create(0, 'c');
 
 
     std::size_t cnt{};
     std::size_t cnt{};
     auto group = registry.group<>(entt::get<int, char>);
     auto group = registry.group<>(entt::get<int, char>);
@@ -460,10 +520,14 @@ TEST(Registry, NonOwningGroupInitOnFirstUse) {
 TEST(Registry, NonOwningGroupInitOnAssign) {
 TEST(Registry, NonOwningGroupInitOnAssign) {
     entt::registry registry;
     entt::registry registry;
     auto group = registry.group<>(entt::get<int, char>);
     auto group = registry.group<>(entt::get<int, char>);
+    auto create = [&](auto... component) {
+        const auto entity = registry.create();
+        (registry.assign<decltype(component)>(entity, component), ...);
+    };
 
 
-    std::get<2>(registry.create<int, char>()) = 'c';
-    registry.create<int>();
-    std::get<2>(registry.create<int, char>()) = 'c';
+    create(0, 'c');
+    create(0);
+    create(0, 'c');
 
 
     std::size_t cnt{};
     std::size_t cnt{};
     group.each([&cnt](auto...) { ++cnt; });
     group.each([&cnt](auto...) { ++cnt; });
@@ -474,10 +538,14 @@ TEST(Registry, NonOwningGroupInitOnAssign) {
 
 
 TEST(Registry, FullOwningGroupInitOnFirstUse) {
 TEST(Registry, FullOwningGroupInitOnFirstUse) {
     entt::registry registry;
     entt::registry registry;
+    auto create = [&](auto... component) {
+        const auto entity = registry.create();
+        (registry.assign<decltype(component)>(entity, component), ...);
+    };
 
 
-    std::get<2>(registry.create<int, char>()) = 'c';
-    registry.create<int>();
-    std::get<2>(registry.create<int, char>()) = 'c';
+    create(0, 'c');
+    create(0);
+    create(0, 'c');
 
 
     std::size_t cnt{};
     std::size_t cnt{};
     auto group = registry.group<int, char>();
     auto group = registry.group<int, char>();
@@ -492,10 +560,14 @@ TEST(Registry, FullOwningGroupInitOnFirstUse) {
 TEST(Registry, FullOwningGroupInitOnAssign) {
 TEST(Registry, FullOwningGroupInitOnAssign) {
     entt::registry registry;
     entt::registry registry;
     auto group = registry.group<int, char>();
     auto group = registry.group<int, char>();
+    auto create = [&](auto... component) {
+        const auto entity = registry.create();
+        (registry.assign<decltype(component)>(entity, component), ...);
+    };
 
 
-    std::get<2>(registry.create<int, char>()) = 'c';
-    registry.create<int>();
-    std::get<2>(registry.create<int, char>()) = 'c';
+    create(0, 'c');
+    create(0);
+    create(0, 'c');
 
 
     std::size_t cnt{};
     std::size_t cnt{};
     group.each([&cnt](auto...) { ++cnt; });
     group.each([&cnt](auto...) { ++cnt; });
@@ -508,10 +580,14 @@ TEST(Registry, FullOwningGroupInitOnAssign) {
 
 
 TEST(Registry, PartialOwningGroupInitOnFirstUse) {
 TEST(Registry, PartialOwningGroupInitOnFirstUse) {
     entt::registry registry;
     entt::registry registry;
+    auto create = [&](auto... component) {
+        const auto entity = registry.create();
+        (registry.assign<decltype(component)>(entity, component), ...);
+    };
 
 
-    std::get<2>(registry.create<int, char>()) = 'c';
-    registry.create<int>();
-    std::get<2>(registry.create<int, char>()) = 'c';
+    create(0, 'c');
+    create(0);
+    create(0, 'c');
 
 
     std::size_t cnt{};
     std::size_t cnt{};
     auto group = registry.group<int>(entt::get<char>);
     auto group = registry.group<int>(entt::get<char>);
@@ -527,10 +603,14 @@ TEST(Registry, PartialOwningGroupInitOnFirstUse) {
 TEST(Registry, PartialOwningGroupInitOnAssign) {
 TEST(Registry, PartialOwningGroupInitOnAssign) {
     entt::registry registry;
     entt::registry registry;
     auto group = registry.group<int>(entt::get<char>);
     auto group = registry.group<int>(entt::get<char>);
+    auto create = [&](auto... component) {
+        const auto entity = registry.create();
+        (registry.assign<decltype(component)>(entity, component), ...);
+    };
 
 
-    std::get<2>(registry.create<int, char>()) = 'c';
-    registry.create<int>();
-    std::get<2>(registry.create<int, char>()) = 'c';
+    create(0, 'c');
+    create(0);
+    create(0, 'c');
 
 
     std::size_t cnt{};
     std::size_t cnt{};
     group.each([&cnt](auto...) { ++cnt; });
     group.each([&cnt](auto...) { ++cnt; });
@@ -669,7 +749,9 @@ TEST(Registry, NestedGroups) {
     entt::registry registry;
     entt::registry registry;
     entt::entity entities[10];
     entt::entity entities[10];
 
 
-    registry.create<int, char>(std::begin(entities), std::end(entities));
+    registry.create(std::begin(entities), std::end(entities));
+    registry.assign<int>(std::begin(entities), std::end(entities));
+    registry.assign<char>(std::begin(entities), std::end(entities));
     const auto g1 = registry.group<int>(entt::get<char>, entt::exclude<double>);
     const auto g1 = registry.group<int>(entt::get<char>, entt::exclude<double>);
 
 
     ASSERT_TRUE(g1.sortable());
     ASSERT_TRUE(g1.sortable());
@@ -1118,152 +1200,6 @@ TEST(Registry, RangeRemove) {
     ASSERT_TRUE(registry.has<int>(e2));
     ASSERT_TRUE(registry.has<int>(e2));
 }
 }
 
 
-TEST(Registry, CreateManyEntitiesAtOnce) {
-    entt::registry registry;
-    entt::entity entities[3];
-
-    const auto entity = registry.create();
-    registry.destroy(registry.create());
-    registry.destroy(entity);
-    registry.destroy(registry.create());
-
-    registry.create(std::begin(entities), std::end(entities));
-
-    ASSERT_TRUE(registry.valid(entities[0]));
-    ASSERT_TRUE(registry.valid(entities[1]));
-    ASSERT_TRUE(registry.valid(entities[2]));
-
-    ASSERT_EQ(registry.entity(entities[0]), entt::entity{0});
-    ASSERT_EQ(registry.version(entities[0]), entt::registry::version_type{2});
-
-    ASSERT_EQ(registry.entity(entities[1]), entt::entity{1});
-    ASSERT_EQ(registry.version(entities[1]), entt::registry::version_type{1});
-
-    ASSERT_EQ(registry.entity(entities[2]), entt::entity{2});
-    ASSERT_EQ(registry.version(entities[2]), entt::registry::version_type{0});
-}
-
-TEST(Registry, CreateAnEntityWithComponents) {
-    entt::registry registry;
-    auto &&[entity, ivalue, cvalue, evalue] = registry.create<int, char, empty_type>();
-    // suppress warnings
-    (void)evalue;
-
-    ASSERT_FALSE(registry.empty<int>());
-    ASSERT_FALSE(registry.empty<char>());
-    ASSERT_FALSE(registry.empty<empty_type>());
-
-    ASSERT_EQ(registry.size<int>(), entt::registry::size_type{1});
-    ASSERT_EQ(registry.size<char>(), entt::registry::size_type{1});
-    ASSERT_EQ(registry.size<empty_type>(), entt::registry::size_type{1});
-
-    ASSERT_TRUE((registry.has<int, char, empty_type>(entity)));
-
-    ivalue = 42;
-    cvalue = 'c';
-
-    ASSERT_EQ(registry.get<int>(entity), 42);
-    ASSERT_EQ(registry.get<char>(entity), 'c');
-}
-
-TEST(Registry, CreateManyEntitiesWithComponentsAtOnce) {
-    entt::registry registry;
-    entt::entity entities[3];
-
-    const auto entity = registry.create();
-    registry.destroy(registry.create());
-    registry.destroy(entity);
-    registry.destroy(registry.create());
-
-    const auto [iptr, cptr, eptr] = registry.create<int, char, empty_type>(std::begin(entities), std::end(entities));
-    static_assert(std::is_same_v<typename decltype(eptr)::reference, empty_type>);
-
-    ASSERT_FALSE(registry.empty<int>());
-    ASSERT_FALSE(registry.empty<char>());
-    ASSERT_FALSE(registry.empty<empty_type>());
-
-    ASSERT_EQ(registry.size<int>(), entt::registry::size_type{3});
-    ASSERT_EQ(registry.size<char>(), entt::registry::size_type{3});
-    ASSERT_EQ(registry.size<empty_type>(), entt::registry::size_type{3});
-
-    ASSERT_TRUE(registry.valid(entities[0]));
-    ASSERT_TRUE(registry.valid(entities[1]));
-    ASSERT_TRUE(registry.valid(entities[2]));
-
-    ASSERT_EQ(registry.entity(entities[0]), entt::entity{0});
-    ASSERT_EQ(registry.version(entities[0]), entt::registry::version_type{2});
-
-    ASSERT_EQ(registry.entity(entities[1]), entt::entity{1});
-    ASSERT_EQ(registry.version(entities[1]), entt::registry::version_type{1});
-
-    ASSERT_EQ(registry.entity(entities[2]), entt::entity{2});
-    ASSERT_EQ(registry.version(entities[2]), entt::registry::version_type{0});
-
-    ASSERT_TRUE((registry.has<int, char, empty_type>(entities[0])));
-    ASSERT_TRUE((registry.has<int, char, empty_type>(entities[1])));
-    ASSERT_TRUE((registry.has<int, char, empty_type>(entities[2])));
-
-    for(auto i = 0; i < 3; ++i) {
-        iptr[i] = i;
-        cptr[i] = char('a'+i);
-    }
-
-    for(auto i = 0; i < 3; ++i) {
-        ASSERT_EQ(registry.get<int>(entities[i]), i);
-        ASSERT_EQ(registry.get<char>(entities[i]), char('a'+i));
-    }
-}
-
-TEST(Registry, CreateManyEntitiesWithComponentsAtOnceWithListener) {
-    entt::registry registry;
-    entt::entity entities[3];
-    listener listener;
-
-    registry.on_construct<int>().connect<&listener::incr<int>>(listener);
-    registry.create<int, char>(std::begin(entities), std::end(entities));
-
-    ASSERT_EQ(listener.counter, 3);
-
-    registry.on_construct<int>().disconnect<&listener::incr<int>>(listener);
-    registry.on_construct<empty_type>().connect<&listener::incr<empty_type>>(listener);
-    registry.create<char, empty_type>(std::begin(entities), std::end(entities));
-
-    ASSERT_EQ(listener.counter, 6);
-}
-
-TEST(Registry, CreateWithHint) {
-    entt::registry registry;
-    auto e3 = registry.create(entt::entity{3});
-    auto e2 = registry.create(entt::entity{3});
-
-    ASSERT_EQ(e2, entt::entity{2});
-    ASSERT_FALSE(registry.valid(entt::entity{1}));
-    ASSERT_EQ(e3, entt::entity{3});
-
-    registry.destroy(e2);
-
-    ASSERT_EQ(registry.version(e2), 0);
-    ASSERT_EQ(registry.current(e2), 1);
-
-    e2 = registry.create();
-    auto e1 = registry.create(entt::entity{2});
-
-    ASSERT_EQ(registry.entity(e2), entt::entity{2});
-    ASSERT_EQ(registry.version(e2), 1);
-
-    ASSERT_EQ(registry.entity(e1), entt::entity{1});
-    ASSERT_EQ(registry.version(e1), 0);
-
-    registry.destroy(e1);
-    registry.destroy(e2);
-    auto e0 = std::get<0>(registry.create<int>(entt::entity{0}));
-
-    ASSERT_EQ(e0, entt::entity{0});
-    ASSERT_EQ(registry.version(e0), 0);
-    ASSERT_TRUE(registry.has<int>(e0));
-    ASSERT_FALSE(registry.has<char>(e0));
-}
-
 TEST(Registry, NonOwningGroupInterleaved) {
 TEST(Registry, NonOwningGroupInterleaved) {
     entt::registry registry;
     entt::registry registry;
     typename entt::entity entity = entt::null;
     typename entt::entity entity = entt::null;

+ 18 - 4
test/entt/entity/view.cpp

@@ -192,8 +192,14 @@ TEST(SingleComponentView, Find) {
 
 
 TEST(SingleComponentView, Less) {
 TEST(SingleComponentView, Less) {
     entt::registry registry;
     entt::registry registry;
-    const auto entity = std::get<0>(registry.create<int, entt::tag<"empty"_hs>>());
-    registry.create<char>();
+    auto create = [&](auto... component) {
+        const auto entity = registry.create();
+        (registry.assign<decltype(component)>(entity, component), ...);
+        return entity;
+    };
+
+    const auto entity = create(0, entt::tag<"empty"_hs>{});
+    create('c');
 
 
     registry.view<entt::tag<"empty"_hs>>().less([entity](const auto entt) {
     registry.view<entt::tag<"empty"_hs>>().less([entity](const auto entt) {
         ASSERT_EQ(entity, entt);
         ASSERT_EQ(entity, entt);
@@ -526,8 +532,16 @@ TEST(MultiComponentView, ExcludedComponents) {
 
 
 TEST(MultiComponentView, Less) {
 TEST(MultiComponentView, Less) {
     entt::registry registry;
     entt::registry registry;
-    const auto entity = std::get<0>(registry.create<int, char, double, entt::tag<"empty"_hs>>());
-    registry.create<int, char>();
+
+    const auto entity = registry.create();
+    registry.assign<int>(entity);
+    registry.assign<char>(entity);
+    registry.assign<double>(entity);
+    registry.assign<entt::tag<"empty"_hs>>(entity);
+
+    const auto other = registry.create();
+    registry.assign<int>(other);
+    registry.assign<char>(other);
 
 
     registry.view<int, char, entt::tag<"empty"_hs>>().less([entity](const auto entt, int, char) {
     registry.view<int, char, entt::tag<"empty"_hs>>().less([entity](const auto entt, int, char) {
         ASSERT_EQ(entity, entt);
         ASSERT_EQ(entity, entt);