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

This commit is contained in:
Michele Caini
2020-01-06 23:46:58 +01:00
parent 2a863ee851
commit 469276b8be
8 changed files with 203 additions and 290 deletions

2
TODO
View File

@@ -13,9 +13,9 @@
* 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)
* 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>)
* 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?
* 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)
- get -> all, exclude -> none

View File

@@ -172,10 +172,6 @@ auto view = registry.view<a_component, another_component>();
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
slightly different identifier. In particular, the version of an entity is
increased after destruction.<br/>

View File

@@ -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>
const pool_handler<Component> & assure(Args &&... args) const {
static std::size_t index{pools.size()};
@@ -476,33 +451,28 @@ public:
/**
* @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.
* * 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 {
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
*
* 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.
* @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.
* @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
* 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.
* @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(const entity_type hint) {
entity_type create(const entity_type hint) {
ENTT_ASSERT(hint != null);
entity_type entt;
@@ -559,7 +515,7 @@ public:
entt = entities.emplace_back(hint);
} else if(const auto curr = (to_integral(entities[req]) & traits_type::entity_mask); req == curr) {
entt = generate();
entt = create();
} else {
auto *it = &destroyed;
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;
}
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.
*
* 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
* 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
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 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 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.
*/
template<typename Component, typename It, typename... Args>

View File

@@ -114,7 +114,9 @@ TEST(Benchmark, ConstructManyWithComponents) {
std::cout << "Constructing 1000000 entities at once with components" << std::endl;
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();
}

View File

@@ -468,7 +468,11 @@ TEST(NonOwningGroup, TrackEntitiesOnComponentDestruction) {
TEST(NonOwningGroup, Less) {
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) {
ASSERT_EQ(entity, entt);
@@ -1040,7 +1044,11 @@ TEST(OwningGroup, TrackEntitiesOnComponentDestruction) {
TEST(OwningGroup, Less) {
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) {
ASSERT_EQ(entity, entt);

View File

@@ -14,7 +14,8 @@ TEST(Observer, Functionalities) {
ASSERT_EQ(observer.data(), nullptr);
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_FALSE(observer.empty());
@@ -263,7 +264,8 @@ TEST(Observer, CrossRulesCornerCase) {
TEST(Observer, Each) {
entt::registry registry;
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_EQ(observer.size(), 1u);

View File

@@ -279,6 +279,93 @@ TEST(Registry, RawData) {
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) {
entt::registry registry;
@@ -316,9 +403,9 @@ TEST(Registry, Each) {
entt::registry::size_type match;
registry.create();
registry.create<int>();
registry.assign<int>(registry.create());
registry.create();
registry.create<int>();
registry.assign<int>(registry.create());
registry.create();
tot = 0u;
@@ -367,9 +454,9 @@ TEST(Registry, Orphans) {
entt::registry registry;
entt::registry::size_type tot{};
registry.create<int>();
registry.assign<int>(registry.create());
registry.create();
registry.create<int>();
registry.assign<int>(registry.create());
registry.orphans([&](auto) { ++tot; });
ASSERT_EQ(tot, 1u);
@@ -385,37 +472,6 @@ TEST(Registry, Orphans) {
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) {
entt::registry registry;
auto mview = registry.view<int, char>();
@@ -444,10 +500,14 @@ TEST(Registry, View) {
TEST(Registry, NonOwningGroupInitOnFirstUse) {
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{};
auto group = registry.group<>(entt::get<int, char>);
@@ -460,10 +520,14 @@ TEST(Registry, NonOwningGroupInitOnFirstUse) {
TEST(Registry, NonOwningGroupInitOnAssign) {
entt::registry registry;
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{};
group.each([&cnt](auto...) { ++cnt; });
@@ -474,10 +538,14 @@ TEST(Registry, NonOwningGroupInitOnAssign) {
TEST(Registry, FullOwningGroupInitOnFirstUse) {
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{};
auto group = registry.group<int, char>();
@@ -492,10 +560,14 @@ TEST(Registry, FullOwningGroupInitOnFirstUse) {
TEST(Registry, FullOwningGroupInitOnAssign) {
entt::registry registry;
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{};
group.each([&cnt](auto...) { ++cnt; });
@@ -508,10 +580,14 @@ TEST(Registry, FullOwningGroupInitOnAssign) {
TEST(Registry, PartialOwningGroupInitOnFirstUse) {
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{};
auto group = registry.group<int>(entt::get<char>);
@@ -527,10 +603,14 @@ TEST(Registry, PartialOwningGroupInitOnFirstUse) {
TEST(Registry, PartialOwningGroupInitOnAssign) {
entt::registry registry;
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{};
group.each([&cnt](auto...) { ++cnt; });
@@ -669,7 +749,9 @@ TEST(Registry, NestedGroups) {
entt::registry registry;
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>);
ASSERT_TRUE(g1.sortable());
@@ -1118,152 +1200,6 @@ TEST(Registry, RangeRemove) {
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) {
entt::registry registry;
typename entt::entity entity = entt::null;

View File

@@ -192,8 +192,14 @@ TEST(SingleComponentView, Find) {
TEST(SingleComponentView, Less) {
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) {
ASSERT_EQ(entity, entt);
@@ -526,8 +532,16 @@ TEST(MultiComponentView, ExcludedComponents) {
TEST(MultiComponentView, Less) {
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) {
ASSERT_EQ(entity, entt);