nested groups: impl + tests
This commit is contained in:
@@ -63,11 +63,10 @@ class basic_group;
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Exclude Types of components used to filter the group.
|
||||
* @tparam Get Type of component observed by the group.
|
||||
* @tparam Other Other types of components observed by the group.
|
||||
* @tparam Get Type of components observed by the group.
|
||||
*/
|
||||
template<typename Entity, typename... Exclude, typename Get, typename... Other>
|
||||
class basic_group<Entity, exclude_t<Exclude...>, get_t<Get, Other...>> {
|
||||
template<typename Entity, typename... Exclude, typename... Get>
|
||||
class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>> {
|
||||
/*! @brief A registry is allowed to create groups. */
|
||||
friend class basic_registry<Entity>;
|
||||
|
||||
@@ -75,9 +74,9 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get, Other...>> {
|
||||
using pool_type = std::conditional_t<std::is_const_v<Component>, const storage<Entity, std::remove_const_t<Component>>, storage<Entity, Component>>;
|
||||
|
||||
// we could use pool_type<Type> *..., but vs complains about it and refuses to compile for unknown reasons (most likely a bug)
|
||||
basic_group(sparse_set<Entity> *ref, storage<Entity, std::remove_const_t<Get>> *get, storage<Entity, std::remove_const_t<Other>> *... other) ENTT_NOEXCEPT
|
||||
basic_group(sparse_set<Entity> *ref, storage<Entity, std::remove_const_t<Get>> *... get) ENTT_NOEXCEPT
|
||||
: handler{ref},
|
||||
pools{get, other...}
|
||||
pools{get...}
|
||||
{}
|
||||
|
||||
template<typename Func, typename... Weak>
|
||||
@@ -306,8 +305,8 @@ public:
|
||||
* forms:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type, Get &, Other &...);
|
||||
* void(Get &, Other &...);
|
||||
* void(const entity_type, Get &...);
|
||||
* void(Get &...);
|
||||
* @endcode
|
||||
*
|
||||
* @note
|
||||
@@ -320,7 +319,7 @@ public:
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
traverse(std::move(func), type_list<Get, Other...>{});
|
||||
traverse(std::move(func), type_list<Get...>{});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -345,9 +344,8 @@ public:
|
||||
*/
|
||||
template<typename Func>
|
||||
void less(Func func) const {
|
||||
using get_type_list = std::conditional_t<std::is_empty_v<Get>, type_list<>, type_list<Get>>;
|
||||
using other_type_list = type_list_cat_t<std::conditional_t<std::is_empty_v<Other>, type_list<>, type_list<Other>>...>;
|
||||
traverse(std::move(func), type_list_cat_t<get_type_list, other_type_list>{});
|
||||
using get_type_list = type_list_cat_t<std::conditional_t<std::is_empty_v<Get>, type_list<>, type_list<Get>>...>;
|
||||
traverse(std::move(func), get_type_list{});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -430,7 +428,7 @@ public:
|
||||
|
||||
private:
|
||||
sparse_set<entity_type> *handler;
|
||||
const std::tuple<pool_type<Get> *, pool_type<Other> *...> pools;
|
||||
const std::tuple<pool_type<Get> *...> pools;
|
||||
};
|
||||
|
||||
|
||||
@@ -479,11 +477,10 @@ private:
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Exclude Types of components used to filter the group.
|
||||
* @tparam Get Types of components observed by the group.
|
||||
* @tparam Owned Type of component owned by the group.
|
||||
* @tparam Other Other types of components owned by the group.
|
||||
* @tparam Owned Types of components owned by the group.
|
||||
*/
|
||||
template<typename Entity, typename... Exclude, typename... Get, typename Owned, typename... Other>
|
||||
class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned, Other...> {
|
||||
template<typename Entity, typename... Exclude, typename... Get, typename... Owned>
|
||||
class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...> {
|
||||
/*! @brief A registry is allowed to create groups. */
|
||||
friend class basic_registry<Entity>;
|
||||
|
||||
@@ -494,15 +491,16 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned, Other...>
|
||||
using component_iterator_type = decltype(std::declval<pool_type<Component>>().begin());
|
||||
|
||||
// we could use pool_type<Type> *..., but vs complains about it and refuses to compile for unknown reasons (most likely a bug)
|
||||
basic_group(const std::size_t *sz, storage<Entity, std::remove_const_t<Owned>> *owned, storage<Entity, std::remove_const_t<Other>> *... other, storage<Entity, std::remove_const_t<Get>> *... get) ENTT_NOEXCEPT
|
||||
: length{sz},
|
||||
pools{owned, other..., get...}
|
||||
basic_group(const std::size_t *ref, const std::size_t *extent, storage<Entity, std::remove_const_t<Owned>> *... owned, storage<Entity, std::remove_const_t<Get>> *... get) ENTT_NOEXCEPT
|
||||
: pools{owned..., get...},
|
||||
length{extent},
|
||||
super{ref}
|
||||
{}
|
||||
|
||||
template<typename Func, typename... Strong, typename... Weak>
|
||||
void traverse(Func func, type_list<Strong...>, type_list<Weak...>) const {
|
||||
[[maybe_unused]] auto raw = std::make_tuple((std::get<pool_type<Strong> *>(pools)->end() - *length)...);
|
||||
[[maybe_unused]] auto data = std::get<pool_type<Owned> *>(pools)->sparse_set<entity_type>::end() - *length;
|
||||
[[maybe_unused]] auto data = std::get<0>(pools)->sparse_set<entity_type>::end() - *length;
|
||||
|
||||
for(auto next = *length; next; --next) {
|
||||
if constexpr(std::is_invocable_v<Func, decltype(get<Strong>({}))..., decltype(get<Weak>({}))...>) {
|
||||
@@ -618,7 +616,7 @@ public:
|
||||
* @return A pointer to the array of entities.
|
||||
*/
|
||||
const entity_type * data() const ENTT_NOEXCEPT {
|
||||
return std::get<pool_type<Owned> *>(pools)->data();
|
||||
return std::get<0>(pools)->data();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -636,7 +634,7 @@ public:
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
iterator_type begin() const ENTT_NOEXCEPT {
|
||||
return std::get<pool_type<Owned> *>(pools)->sparse_set<entity_type>::end() - *length;
|
||||
return std::get<0>(pools)->sparse_set<entity_type>::end() - *length;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -655,7 +653,7 @@ public:
|
||||
* given components.
|
||||
*/
|
||||
iterator_type end() const ENTT_NOEXCEPT {
|
||||
return std::get<pool_type<Owned> *>(pools)->sparse_set<entity_type>::end();
|
||||
return std::get<0>(pools)->sparse_set<entity_type>::end();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -665,7 +663,7 @@ public:
|
||||
* iterator otherwise.
|
||||
*/
|
||||
iterator_type find(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
const auto it = std::get<pool_type<Owned> *>(pools)->find(entt);
|
||||
const auto it = std::get<0>(pools)->find(entt);
|
||||
return it != end() && it >= begin() && *it == entt ? it : end();
|
||||
}
|
||||
|
||||
@@ -726,8 +724,8 @@ public:
|
||||
* forms:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type, Owned &, Other &..., Get &...);
|
||||
* void(Owned &, Other &..., Get &...);
|
||||
* void(const entity_type, Owned &..., Get &...);
|
||||
* void(Owned &..., Get &...);
|
||||
* @endcode
|
||||
*
|
||||
* @note
|
||||
@@ -740,7 +738,7 @@ public:
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
traverse(std::move(func), type_list<Owned, Other...>{}, type_list<Get...>{});
|
||||
traverse(std::move(func), type_list<Owned...>{}, type_list<Get...>{});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -765,10 +763,18 @@ public:
|
||||
*/
|
||||
template<typename Func>
|
||||
void less(Func func) const {
|
||||
using owned_type_list = std::conditional_t<std::is_empty_v<Owned>, type_list<>, type_list<Owned>>;
|
||||
using other_type_list = type_list_cat_t<std::conditional_t<std::is_empty_v<Other>, type_list<>, type_list<Other>>...>;
|
||||
using owned_type_list = type_list_cat_t<std::conditional_t<std::is_empty_v<Owned>, type_list<>, type_list<Owned>>...>;
|
||||
using get_type_list = type_list_cat_t<std::conditional_t<std::is_empty_v<Get>, type_list<>, type_list<Get>>...>;
|
||||
traverse(std::move(func), type_list_cat_t<owned_type_list, other_type_list>{}, get_type_list{});
|
||||
traverse(std::move(func), owned_type_list{}, get_type_list{});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether the group can be sorted.
|
||||
* @return True if the group can be sorted, false otherwise.
|
||||
*/
|
||||
bool sortable() const ENTT_NOEXCEPT {
|
||||
constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude);
|
||||
return *super == size;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -815,7 +821,8 @@ public:
|
||||
*/
|
||||
template<typename... Component, typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(Compare compare, Sort algo = Sort{}, Args &&... args) {
|
||||
auto *cpool = std::get<pool_type<Owned> *>(pools);
|
||||
ENTT_ASSERT(sortable());
|
||||
auto *cpool = std::get<0>(pools);
|
||||
|
||||
if constexpr(sizeof...(Component) == 0) {
|
||||
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>);
|
||||
@@ -830,16 +837,19 @@ public:
|
||||
}, std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
for(auto next = *length; next; --next) {
|
||||
const auto pos = next - 1;
|
||||
[[maybe_unused]] const auto entt = cpool->data()[pos];
|
||||
(std::get<pool_type<Other> *>(pools)->swap(std::get<pool_type<Other> *>(pools)->data()[pos], entt), ...);
|
||||
}
|
||||
[](std::size_t length, auto *cpool, auto *... other) {
|
||||
for(auto next = length; next; --next) {
|
||||
const auto pos = next - 1;
|
||||
[[maybe_unused]] const auto entt = cpool->data()[pos];
|
||||
(other->swap(other->data()[pos], entt), ...);
|
||||
}
|
||||
}(*length, std::get<pool_type<Owned> *>(pools)...);
|
||||
}
|
||||
|
||||
private:
|
||||
const std::tuple<pool_type<Owned> *..., pool_type<Get> *...> pools;
|
||||
const size_type *length;
|
||||
const std::tuple<pool_type<Owned> *, pool_type<Other> *..., pool_type<Get> *...> pools;
|
||||
const size_type *super;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ class basic_registry {
|
||||
|
||||
template<typename Component>
|
||||
struct pool_handler: storage<Entity, Component> {
|
||||
std::size_t *owned;
|
||||
std::size_t super{};
|
||||
|
||||
pool_handler() ENTT_NOEXCEPT = default;
|
||||
|
||||
@@ -201,11 +201,11 @@ class basic_registry {
|
||||
};
|
||||
|
||||
struct group_data {
|
||||
const std::size_t extent[3];
|
||||
std::size_t extent[3];
|
||||
std::unique_ptr<void, void(*)(void *)> group;
|
||||
bool(* const owned)(const component) ENTT_NOEXCEPT;
|
||||
bool(* const get)(const component) ENTT_NOEXCEPT;
|
||||
bool(* const exclude)(const component) ENTT_NOEXCEPT;
|
||||
bool(* owned)(const component) ENTT_NOEXCEPT;
|
||||
bool(* get)(const component) ENTT_NOEXCEPT;
|
||||
bool(* exclude)(const component) ENTT_NOEXCEPT;
|
||||
};
|
||||
|
||||
struct ctx_variable {
|
||||
@@ -323,8 +323,7 @@ public:
|
||||
* @brief Returns the opaque identifier of a component.
|
||||
*
|
||||
* The given component doesn't need to be necessarily in use.<br/>
|
||||
* Do not use this functionality to generate numeric identifiers for types
|
||||
* at runtime. They aren't guaranteed to be stable between different runs.
|
||||
* Identifiers aren't guaranteed to be stable between different runs.
|
||||
*
|
||||
* @tparam Component Type of component to query.
|
||||
* @return Runtime the opaque identifier of the given type of component.
|
||||
@@ -373,8 +372,7 @@ public:
|
||||
*
|
||||
* If no components are specified, the capacity of the registry is
|
||||
* increased, that is the number of entities it contains. Otherwise the
|
||||
* capacity of the pools for the given components is increased.
|
||||
*
|
||||
* capacity of the pools for the given components is increased.<br/>
|
||||
* In both cases, if the new capacity is greater than the current capacity,
|
||||
* new storage is allocated, otherwise the method does nothing.
|
||||
*
|
||||
@@ -1049,10 +1047,9 @@ public:
|
||||
* this member function.
|
||||
*
|
||||
* @warning
|
||||
* Pools of components owned by a group are only partially sorted.<br/>
|
||||
* In other words, only the elements that aren't part of the group are
|
||||
* sorted by this function. Use the `sort` member function of a group to
|
||||
* sort the other half of the pool.
|
||||
* Pools of components owned by a group cannot be sorted.<br/>
|
||||
* An assertion will abort the execution at runtime in debug mode in case
|
||||
* the pool is owned by a group.
|
||||
*
|
||||
* @tparam Component Type of components to sort.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
@@ -1064,12 +1061,9 @@ public:
|
||||
*/
|
||||
template<typename Component, typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(Compare compare, Sort algo = Sort{}, Args &&... args) {
|
||||
if(auto *cpool = assure<Component>(); cpool->owned) {
|
||||
const auto last = cpool->end() - *cpool->owned;
|
||||
cpool->sort(cpool->begin(), last, std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
} else {
|
||||
cpool->sort(cpool->begin(), cpool->end(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
auto *cpool = assure<Component>();
|
||||
ENTT_ASSERT(!cpool->super);
|
||||
cpool->sort(cpool->begin(), cpool->end(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1100,7 +1094,7 @@ public:
|
||||
* Any subsequent change to `B` won't affect the order in `A`.
|
||||
*
|
||||
* @warning
|
||||
* Pools of components owned by a group cannot be sorted this way.<br/>
|
||||
* Pools of components owned by a group cannot be sorted.<br/>
|
||||
* An assertion will abort the execution at runtime in debug mode in case
|
||||
* the pool is owned by a group.
|
||||
*
|
||||
@@ -1109,8 +1103,9 @@ public:
|
||||
*/
|
||||
template<typename To, typename From>
|
||||
void sort() {
|
||||
ENTT_ASSERT(!owned<To>());
|
||||
assure<To>()->respect(*assure<From>());
|
||||
auto *cpool = assure<To>();
|
||||
ENTT_ASSERT(!cpool->super);
|
||||
cpool->respect(*assure<From>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1299,13 +1294,14 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a given component belongs to a group.
|
||||
* @tparam Component Type of component in which one is interested.
|
||||
* @return True if the component belongs to a group, false otherwise.
|
||||
* @brief Checks whether the given components belong to any group.
|
||||
* @tparam Component Types of components in which one is interested.
|
||||
* @return True if the pools of the given components are sortable, false
|
||||
* otherwise.
|
||||
*/
|
||||
template<typename Component>
|
||||
bool owned() const ENTT_NOEXCEPT {
|
||||
return assure<Component>()->owned;
|
||||
template<typename... Component>
|
||||
bool sortable() const ENTT_NOEXCEPT {
|
||||
return !(assure<Component>()->super || ...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1341,6 +1337,8 @@ public:
|
||||
static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1);
|
||||
|
||||
using handler_type = group_handler<exclude_t<Exclude...>, get_t<Get...>, Owned...>;
|
||||
|
||||
[[maybe_unused]] constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude);
|
||||
const auto cpools = std::make_tuple(assure<Owned>()..., assure<Get>()..., assure<Exclude>()...);
|
||||
const std::size_t extent[3]{sizeof...(Owned), sizeof...(Get), sizeof...(Exclude)};
|
||||
handler_type *curr = nullptr;
|
||||
@@ -1356,27 +1354,48 @@ public:
|
||||
}
|
||||
|
||||
if(!curr) {
|
||||
groups.push_back(group_data{
|
||||
const void *maybe_valid_if = nullptr;
|
||||
const void *discard_if = nullptr;
|
||||
|
||||
group_data gdata{
|
||||
{ sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) },
|
||||
decltype(group_data::group){new handler_type{cpools}, [](void *gptr) { delete static_cast<handler_type *>(gptr); }},
|
||||
[](const component ctype) ENTT_NOEXCEPT { return ((ctype == type<Owned>()) || ...); },
|
||||
[](const component ctype) ENTT_NOEXCEPT { return ((ctype == type<Get>()) || ...); },
|
||||
[](const component ctype) ENTT_NOEXCEPT { return ((ctype == type<Exclude>()) || ...); }
|
||||
});
|
||||
};
|
||||
|
||||
curr = static_cast<handler_type *>(groups.back().group.get());
|
||||
if constexpr(sizeof...(Owned) == 0) {
|
||||
curr = static_cast<handler_type *>(groups.emplace_back(std::move(gdata)).group.get());
|
||||
} else {
|
||||
ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [&extent](const auto &gdata) {
|
||||
const std::size_t diff[3]{ (0u + ... + gdata.owned(type<Owned>())), (0u + ... + gdata.get(type<Get>())), (0u + ... + gdata.exclude(type<Exclude>())) };
|
||||
return !diff[0] || ((std::equal(std::begin(diff), std::end(diff), extent) || std::equal(std::begin(diff), std::end(diff), gdata.extent)));
|
||||
}));
|
||||
|
||||
ENTT_ASSERT((!std::get<pool_type<Owned> *>(cpools)->owned && ...));
|
||||
((std::get<pool_type<Owned> *>(cpools)->owned = &curr->owned), ...);
|
||||
const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [&size](const auto &gdata) {
|
||||
const std::size_t diff = (0u + ... + gdata.owned(type<Owned>()));
|
||||
return !diff || (size > (gdata.extent[0] + gdata.extent[1] + gdata.extent[2]));
|
||||
});
|
||||
|
||||
(std::get<pool_type<Owned> *>(cpools)->on_construct().template connect<&handler_type::template maybe_valid_if<Owned>>(*curr), ...);
|
||||
(std::get<pool_type<Owned> *>(cpools)->on_destroy().template connect<&handler_type::discard_if>(*curr), ...);
|
||||
const auto prev = std::find_if(std::make_reverse_iterator(next), groups.crend(), [](const auto &gdata) {
|
||||
return (0u + ... + gdata.owned(type<Owned>()));
|
||||
});
|
||||
|
||||
(std::get<pool_type<Get> *>(cpools)->on_construct().template connect<&handler_type::template maybe_valid_if<Get>>(*curr), ...);
|
||||
(std::get<pool_type<Get> *>(cpools)->on_destroy().template connect<&handler_type::discard_if>(*curr), ...);
|
||||
maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get());
|
||||
discard_if = (prev == groups.crend() ? discard_if : prev->group.get());
|
||||
curr = static_cast<handler_type *>(groups.insert(next, std::move(gdata))->group.get());
|
||||
}
|
||||
|
||||
(std::get<pool_type<Exclude> *>(cpools)->on_destroy().template connect<&handler_type::template maybe_valid_if<Exclude>>(*curr), ...);
|
||||
(std::get<pool_type<Exclude> *>(cpools)->on_construct().template connect<&handler_type::discard_if>(*curr), ...);
|
||||
((std::get<pool_type<Owned> *>(cpools)->super = std::max(std::get<pool_type<Owned> *>(cpools)->super, size)), ...);
|
||||
|
||||
(std::get<pool_type<Owned> *>(cpools)->on_construct().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<Owned>>(*curr), ...);
|
||||
(std::get<pool_type<Get> *>(cpools)->on_construct().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<Get>>(*curr), ...);
|
||||
(std::get<pool_type<Exclude> *>(cpools)->on_destroy().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<Exclude>>(*curr), ...);
|
||||
|
||||
(std::get<pool_type<Owned> *>(cpools)->on_destroy().before(discard_if).template connect<&handler_type::discard_if>(*curr), ...);
|
||||
(std::get<pool_type<Get> *>(cpools)->on_destroy().before(discard_if).template connect<&handler_type::discard_if>(*curr), ...);
|
||||
(std::get<pool_type<Exclude> *>(cpools)->on_construct().before(discard_if).template connect<&handler_type::discard_if>(*curr), ...);
|
||||
|
||||
const auto *cpool = std::min({
|
||||
static_cast<sparse_set<Entity> *>(std::get<pool_type<Owned> *>(cpools))...,
|
||||
@@ -1394,8 +1413,10 @@ public:
|
||||
if constexpr(sizeof...(Owned) == 0) {
|
||||
curr->set.construct(entity);
|
||||
} else {
|
||||
const auto pos = curr->owned++;
|
||||
(std::get<pool_type<Owned> *>(cpools)->swap(std::get<pool_type<Owned> *>(cpools)->data()[pos], entity), ...);
|
||||
if(!(std::get<0>(cpools)->index(entity) < curr->owned)) {
|
||||
const auto pos = curr->owned++;
|
||||
(std::get<pool_type<Owned> *>(cpools)->swap(std::get<pool_type<Owned> *>(cpools)->data()[pos], entity), ...);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1404,7 +1425,7 @@ public:
|
||||
if constexpr(sizeof...(Owned) == 0) {
|
||||
return { &curr->set, std::get<pool_type<Get> *>(cpools)... };
|
||||
} else {
|
||||
return { &curr->owned, std::get<pool_type<Owned> *>(cpools)... , std::get<pool_type<Get> *>(cpools)... };
|
||||
return { &std::get<0>(cpools)->super, &curr->owned, std::get<pool_type<Owned> *>(cpools)... , std::get<pool_type<Get> *>(cpools)... };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -575,9 +575,8 @@ public:
|
||||
auto next = index(direct[curr]);
|
||||
|
||||
while(curr != next) {
|
||||
auto [page, offset] = map(direct[curr]);
|
||||
|
||||
apply(direct[curr], direct[next]);
|
||||
auto [page, offset] = map(direct[curr]);
|
||||
reverse[page][offset] = entity_type(curr);
|
||||
|
||||
curr = next;
|
||||
|
||||
@@ -321,9 +321,9 @@ TEST(Registry, Each) {
|
||||
entt::registry::size_type match;
|
||||
|
||||
registry.create();
|
||||
registry.assign<int>(registry.create());
|
||||
registry.create<int>();
|
||||
registry.create();
|
||||
registry.assign<int>(registry.create());
|
||||
registry.create<int>();
|
||||
registry.create();
|
||||
|
||||
tot = 0u;
|
||||
@@ -372,9 +372,9 @@ TEST(Registry, Orphans) {
|
||||
entt::registry registry;
|
||||
entt::registry::size_type tot{};
|
||||
|
||||
registry.assign<int>(registry.create());
|
||||
registry.create<int>();
|
||||
registry.create();
|
||||
registry.assign<int>(registry.create());
|
||||
registry.create<int>();
|
||||
|
||||
registry.orphans([&](auto) { ++tot; });
|
||||
ASSERT_EQ(tot, 1u);
|
||||
@@ -450,26 +450,15 @@ TEST(Registry, View) {
|
||||
TEST(Registry, NonOwningGroupInitOnFirstUse) {
|
||||
entt::registry registry;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0, 0);
|
||||
registry.assign<char>(e0, 'c');
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1, 0);
|
||||
|
||||
const auto e2 = registry.create();
|
||||
registry.assign<int>(e2, 0);
|
||||
registry.assign<char>(e2, 'c');
|
||||
|
||||
ASSERT_FALSE(registry.owned<int>());
|
||||
ASSERT_FALSE(registry.owned<char>());
|
||||
std::get<2>(registry.create<int, char>()) = 'c';
|
||||
registry.create<int>();
|
||||
std::get<2>(registry.create<int, char>()) = 'c';
|
||||
|
||||
std::size_t cnt{};
|
||||
auto group = registry.group<>(entt::get<int, char>);
|
||||
group.each([&cnt](auto...) { ++cnt; });
|
||||
|
||||
ASSERT_FALSE(registry.owned<int>());
|
||||
ASSERT_FALSE(registry.owned<char>());
|
||||
ASSERT_TRUE((registry.sortable<int, char>()));
|
||||
ASSERT_EQ(cnt, 2u);
|
||||
}
|
||||
|
||||
@@ -477,51 +466,31 @@ TEST(Registry, NonOwningGroupInitOnAssign) {
|
||||
entt::registry registry;
|
||||
auto group = registry.group<>(entt::get<int, char>);
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0, 0);
|
||||
registry.assign<char>(e0, 'c');
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1, 0);
|
||||
|
||||
const auto e2 = registry.create();
|
||||
registry.assign<int>(e2, 0);
|
||||
registry.assign<char>(e2, 'c');
|
||||
|
||||
ASSERT_FALSE(registry.owned<int>());
|
||||
ASSERT_FALSE(registry.owned<char>());
|
||||
std::get<2>(registry.create<int, char>()) = 'c';
|
||||
registry.create<int>();
|
||||
std::get<2>(registry.create<int, char>()) = 'c';
|
||||
|
||||
std::size_t cnt{};
|
||||
group.each([&cnt](auto...) { ++cnt; });
|
||||
|
||||
ASSERT_FALSE(registry.owned<int>());
|
||||
ASSERT_FALSE(registry.owned<char>());
|
||||
ASSERT_TRUE((registry.sortable<int, char>()));
|
||||
ASSERT_EQ(cnt, 2u);
|
||||
}
|
||||
|
||||
TEST(Registry, FullOwningGroupInitOnFirstUse) {
|
||||
entt::registry registry;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0, 0);
|
||||
registry.assign<char>(e0, 'c');
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1, 0);
|
||||
|
||||
const auto e2 = registry.create();
|
||||
registry.assign<int>(e2, 0);
|
||||
registry.assign<char>(e2, 'c');
|
||||
|
||||
ASSERT_FALSE(registry.owned<int>());
|
||||
ASSERT_FALSE(registry.owned<char>());
|
||||
std::get<2>(registry.create<int, char>()) = 'c';
|
||||
registry.create<int>();
|
||||
std::get<2>(registry.create<int, char>()) = 'c';
|
||||
|
||||
std::size_t cnt{};
|
||||
auto group = registry.group<int, char>();
|
||||
group.each([&cnt](auto...) { ++cnt; });
|
||||
|
||||
ASSERT_TRUE(registry.owned<int>());
|
||||
ASSERT_TRUE(registry.owned<char>());
|
||||
ASSERT_FALSE(registry.sortable<int>());
|
||||
ASSERT_FALSE(registry.sortable<char>());
|
||||
ASSERT_TRUE(registry.sortable<double>());
|
||||
ASSERT_EQ(cnt, 2u);
|
||||
}
|
||||
|
||||
@@ -529,51 +498,33 @@ TEST(Registry, FullOwningGroupInitOnAssign) {
|
||||
entt::registry registry;
|
||||
auto group = registry.group<int, char>();
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0, 0);
|
||||
registry.assign<char>(e0, 'c');
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1, 0);
|
||||
|
||||
const auto e2 = registry.create();
|
||||
registry.assign<int>(e2, 0);
|
||||
registry.assign<char>(e2, 'c');
|
||||
|
||||
ASSERT_TRUE(registry.owned<int>());
|
||||
ASSERT_TRUE(registry.owned<char>());
|
||||
std::get<2>(registry.create<int, char>()) = 'c';
|
||||
registry.create<int>();
|
||||
std::get<2>(registry.create<int, char>()) = 'c';
|
||||
|
||||
std::size_t cnt{};
|
||||
group.each([&cnt](auto...) { ++cnt; });
|
||||
|
||||
ASSERT_TRUE(registry.owned<int>());
|
||||
ASSERT_TRUE(registry.owned<char>());
|
||||
ASSERT_FALSE(registry.sortable<int>());
|
||||
ASSERT_FALSE(registry.sortable<char>());
|
||||
ASSERT_TRUE(registry.sortable<double>());
|
||||
ASSERT_EQ(cnt, 2u);
|
||||
}
|
||||
|
||||
TEST(Registry, PartialOwningGroupInitOnFirstUse) {
|
||||
entt::registry registry;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0, 0);
|
||||
registry.assign<char>(e0, 'c');
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1, 1);
|
||||
|
||||
const auto e2 = registry.create();
|
||||
registry.assign<int>(e2, 2);
|
||||
registry.assign<char>(e2, 'c');
|
||||
|
||||
ASSERT_FALSE(registry.owned<int>());
|
||||
ASSERT_FALSE(registry.owned<char>());
|
||||
std::get<2>(registry.create<int, char>()) = 'c';
|
||||
registry.create<int>();
|
||||
std::get<2>(registry.create<int, char>()) = 'c';
|
||||
|
||||
std::size_t cnt{};
|
||||
auto group = registry.group<int>(entt::get<char>);
|
||||
group.each([&cnt](auto...) { ++cnt; });
|
||||
|
||||
ASSERT_TRUE(registry.owned<int>());
|
||||
ASSERT_FALSE(registry.owned<char>());
|
||||
ASSERT_FALSE((registry.sortable<int, char>()));
|
||||
ASSERT_FALSE(registry.sortable<int>());
|
||||
ASSERT_TRUE(registry.sortable<char>());
|
||||
ASSERT_EQ(cnt, 2u);
|
||||
|
||||
}
|
||||
@@ -582,25 +533,16 @@ TEST(Registry, PartialOwningGroupInitOnAssign) {
|
||||
entt::registry registry;
|
||||
auto group = registry.group<int>(entt::get<char>);
|
||||
|
||||
const auto e0 = registry.create();
|
||||
registry.assign<int>(e0, 0);
|
||||
registry.assign<char>(e0, 'c');
|
||||
|
||||
const auto e1 = registry.create();
|
||||
registry.assign<int>(e1, 0);
|
||||
|
||||
const auto e2 = registry.create();
|
||||
registry.assign<int>(e2, 0);
|
||||
registry.assign<char>(e2, 'c');
|
||||
|
||||
ASSERT_TRUE(registry.owned<int>());
|
||||
ASSERT_FALSE(registry.owned<char>());
|
||||
std::get<2>(registry.create<int, char>()) = 'c';
|
||||
registry.create<int>();
|
||||
std::get<2>(registry.create<int, char>()) = 'c';
|
||||
|
||||
std::size_t cnt{};
|
||||
group.each([&cnt](auto...) { ++cnt; });
|
||||
|
||||
ASSERT_TRUE(registry.owned<int>());
|
||||
ASSERT_FALSE(registry.owned<char>());
|
||||
ASSERT_FALSE((registry.sortable<int, char>()));
|
||||
ASSERT_FALSE(registry.sortable<int>());
|
||||
ASSERT_TRUE(registry.sortable<char>());
|
||||
ASSERT_EQ(cnt, 2u);
|
||||
}
|
||||
|
||||
@@ -728,6 +670,107 @@ TEST(Registry, CleanPartialOwningGroupViewAfterReset) {
|
||||
ASSERT_EQ(group.size(), entt::registry::size_type{0});
|
||||
}
|
||||
|
||||
TEST(Registry, NestedGroups) {
|
||||
entt::registry registry;
|
||||
entt::entity entities[10];
|
||||
|
||||
registry.create<int, char>(std::begin(entities), std::end(entities));
|
||||
const auto g1 = registry.group<int>(entt::get<char>, entt::exclude<double>);
|
||||
|
||||
ASSERT_TRUE(g1.sortable());
|
||||
ASSERT_EQ(g1.size(), 10u);
|
||||
|
||||
const auto g2 = registry.group<int>(entt::get<char>);
|
||||
|
||||
ASSERT_TRUE(g1.sortable());
|
||||
ASSERT_FALSE(g2.sortable());
|
||||
ASSERT_EQ(g1.size(), 10u);
|
||||
ASSERT_EQ(g2.size(), 10u);
|
||||
|
||||
for(auto i = 0u; i < 5u; ++i) {
|
||||
ASSERT_TRUE(g1.contains(entities[i*2+1]));
|
||||
ASSERT_TRUE(g1.contains(entities[i*2]));
|
||||
ASSERT_TRUE(g2.contains(entities[i*2+1]));
|
||||
ASSERT_TRUE(g2.contains(entities[i*2]));
|
||||
registry.assign<double>(entities[i*2]);
|
||||
}
|
||||
|
||||
ASSERT_EQ(g1.size(), 5u);
|
||||
ASSERT_EQ(g2.size(), 10u);
|
||||
|
||||
for(auto i = 0u; i < 5u; ++i) {
|
||||
ASSERT_TRUE(g1.contains(entities[i*2+1]));
|
||||
ASSERT_FALSE(g1.contains(entities[i*2]));
|
||||
ASSERT_TRUE(g2.contains(entities[i*2+1]));
|
||||
ASSERT_TRUE(g2.contains(entities[i*2]));
|
||||
registry.remove<int>(entities[i*2+1]);
|
||||
}
|
||||
|
||||
ASSERT_EQ(g1.size(), 0u);
|
||||
ASSERT_EQ(g2.size(), 5u);
|
||||
|
||||
const auto g3= registry.group<int, float>(entt::get<char>, entt::exclude<double>);
|
||||
|
||||
ASSERT_FALSE(g1.sortable());
|
||||
ASSERT_FALSE(g2.sortable());
|
||||
ASSERT_TRUE(g3.sortable());
|
||||
|
||||
ASSERT_EQ(g1.size(), 0u);
|
||||
ASSERT_EQ(g2.size(), 5u);
|
||||
ASSERT_EQ(g3.size(), 0u);
|
||||
|
||||
for(auto i = 0u; i < 5u; ++i) {
|
||||
ASSERT_FALSE(g1.contains(entities[i*2+1]));
|
||||
ASSERT_FALSE(g1.contains(entities[i*2]));
|
||||
ASSERT_FALSE(g2.contains(entities[i*2+1]));
|
||||
ASSERT_TRUE(g2.contains(entities[i*2]));
|
||||
ASSERT_FALSE(g3.contains(entities[i*2+1]));
|
||||
ASSERT_FALSE(g3.contains(entities[i*2]));
|
||||
registry.assign<int>(entities[i*2+1]);
|
||||
}
|
||||
|
||||
ASSERT_EQ(g1.size(), 5u);
|
||||
ASSERT_EQ(g2.size(), 10u);
|
||||
ASSERT_EQ(g3.size(), 0u);
|
||||
|
||||
for(auto i = 0u; i < 5u; ++i) {
|
||||
ASSERT_TRUE(g1.contains(entities[i*2+1]));
|
||||
ASSERT_FALSE(g1.contains(entities[i*2]));
|
||||
ASSERT_TRUE(g2.contains(entities[i*2+1]));
|
||||
ASSERT_TRUE(g2.contains(entities[i*2]));
|
||||
ASSERT_FALSE(g3.contains(entities[i*2+1]));
|
||||
ASSERT_FALSE(g3.contains(entities[i*2]));
|
||||
registry.assign<float>(entities[i*2]);
|
||||
}
|
||||
|
||||
ASSERT_EQ(g1.size(), 5u);
|
||||
ASSERT_EQ(g2.size(), 10u);
|
||||
ASSERT_EQ(g3.size(), 0u);
|
||||
|
||||
for(auto i = 0u; i < 5u; ++i) {
|
||||
registry.remove<double>(entities[i*2]);
|
||||
}
|
||||
|
||||
ASSERT_EQ(g1.size(), 10u);
|
||||
ASSERT_EQ(g2.size(), 10u);
|
||||
ASSERT_EQ(g3.size(), 5u);
|
||||
|
||||
for(auto i = 0u; i < 5u; ++i) {
|
||||
ASSERT_TRUE(g1.contains(entities[i*2+1]));
|
||||
ASSERT_TRUE(g1.contains(entities[i*2]));
|
||||
ASSERT_TRUE(g2.contains(entities[i*2+1]));
|
||||
ASSERT_TRUE(g2.contains(entities[i*2]));
|
||||
ASSERT_FALSE(g3.contains(entities[i*2+1]));
|
||||
ASSERT_TRUE(g3.contains(entities[i*2]));
|
||||
registry.remove<int>(entities[i*2+1]);
|
||||
registry.remove<int>(entities[i*2]);
|
||||
}
|
||||
|
||||
ASSERT_EQ(g1.size(), 0u);
|
||||
ASSERT_EQ(g2.size(), 0u);
|
||||
ASSERT_EQ(g3.size(), 0u);
|
||||
}
|
||||
|
||||
TEST(Registry, SortSingle) {
|
||||
entt::registry registry;
|
||||
|
||||
@@ -780,67 +823,6 @@ TEST(Registry, SortMulti) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Registry, SortOwned) {
|
||||
entt::registry registry;
|
||||
registry.group<int>(entt::get<char>);
|
||||
|
||||
for(auto i = 0; i < 5; ++i) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<int>(entity, i);
|
||||
|
||||
if(i < 2) {
|
||||
registry.assign<char>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_TRUE((registry.has<int, char>(*(registry.data<int>()+0))));
|
||||
ASSERT_TRUE((registry.has<int, char>(*(registry.data<int>()+1))));
|
||||
|
||||
ASSERT_EQ(*(registry.raw<int>()+0), 0);
|
||||
ASSERT_EQ(*(registry.raw<int>()+1), 1);
|
||||
ASSERT_EQ(*(registry.raw<int>()+2), 2);
|
||||
ASSERT_EQ(*(registry.raw<int>()+3), 3);
|
||||
ASSERT_EQ(*(registry.raw<int>()+4), 4);
|
||||
|
||||
registry.sort<int>(std::less{});
|
||||
|
||||
ASSERT_EQ(*(registry.raw<int>()+0), 0);
|
||||
ASSERT_EQ(*(registry.raw<int>()+1), 1);
|
||||
ASSERT_EQ(*(registry.raw<int>()+2), 4);
|
||||
ASSERT_EQ(*(registry.raw<int>()+3), 3);
|
||||
ASSERT_EQ(*(registry.raw<int>()+4), 2);
|
||||
|
||||
registry.reset<char>();
|
||||
registry.sort<int>(std::less{});
|
||||
|
||||
ASSERT_EQ(*(registry.raw<int>()+0), 4);
|
||||
ASSERT_EQ(*(registry.raw<int>()+1), 3);
|
||||
ASSERT_EQ(*(registry.raw<int>()+2), 2);
|
||||
ASSERT_EQ(*(registry.raw<int>()+3), 1);
|
||||
ASSERT_EQ(*(registry.raw<int>()+4), 0);
|
||||
|
||||
registry.each([®istry](const auto entity) {
|
||||
registry.assign<char>(entity);
|
||||
});
|
||||
|
||||
registry.sort<int>(std::greater{});
|
||||
|
||||
ASSERT_EQ(*(registry.raw<int>()+0), 4);
|
||||
ASSERT_EQ(*(registry.raw<int>()+1), 3);
|
||||
ASSERT_EQ(*(registry.raw<int>()+2), 2);
|
||||
ASSERT_EQ(*(registry.raw<int>()+3), 1);
|
||||
ASSERT_EQ(*(registry.raw<int>()+4), 0);
|
||||
|
||||
registry.reset<char>();
|
||||
registry.sort<int>(std::greater{});
|
||||
|
||||
ASSERT_EQ(*(registry.raw<int>()+0), 0);
|
||||
ASSERT_EQ(*(registry.raw<int>()+1), 1);
|
||||
ASSERT_EQ(*(registry.raw<int>()+2), 2);
|
||||
ASSERT_EQ(*(registry.raw<int>()+3), 3);
|
||||
ASSERT_EQ(*(registry.raw<int>()+4), 4);
|
||||
}
|
||||
|
||||
TEST(Registry, ComponentsWithTypesFromStandardTemplateLibrary) {
|
||||
// see #37 - the test shouldn't crash, that's all
|
||||
entt::registry registry;
|
||||
|
||||
Reference in New Issue
Block a user