1#ifndef ENTT_ENTITY_GROUP_HPP
2#define ENTT_ENTITY_GROUP_HPP
10#include "../config/config.h"
11#include "../core/algorithm.hpp"
12#include "../core/fwd.hpp"
13#include "../core/iterator.hpp"
14#include "../core/type_info.hpp"
15#include "../core/type_traits.hpp"
24template<
typename,
typename,
typename>
25class extended_group_iterator;
27template<
typename It,
typename... Owned,
typename... Get>
28class extended_group_iterator<It, owned_t<Owned...>, get_t<Get...>> {
29 template<
typename Type>
30 [[nodiscard]]
auto index_to_element([[maybe_unused]] Type &cpool)
const {
31 if constexpr(std::is_void_v<typename Type::value_type>) {
32 return std::make_tuple();
34 return std::forward_as_tuple(cpool.rbegin()[it.index()]);
39 using iterator_type = It;
40 using value_type =
decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Owned>().get_as_tuple({})..., std::declval<Get>().get_as_tuple({})...));
41 using pointer = input_iterator_pointer<value_type>;
42 using reference = value_type;
43 using difference_type = std::ptrdiff_t;
44 using iterator_category = std::input_iterator_tag;
45 using iterator_concept = std::forward_iterator_tag;
47 constexpr extended_group_iterator()
51 extended_group_iterator(iterator_type from, std::tuple<Owned *..., Get *...> cpools)
53 pools{std::move(cpools)} {}
55 extended_group_iterator &operator++() noexcept {
59 extended_group_iterator operator++(
int)
noexcept {
60 const extended_group_iterator orig = *
this;
61 return ++(*this), orig;
64 [[nodiscard]] reference operator*() const noexcept {
65 return std::tuple_cat(std::make_tuple(*it), index_to_element(*std::get<Owned *>(pools))..., std::get<Get *>(pools)->get_as_tuple(*it)...);
68 [[nodiscard]] pointer operator->() const noexcept {
72 [[nodiscard]]
constexpr iterator_type base() const noexcept {
76 template<
typename... Lhs,
typename... Rhs>
77 friend constexpr bool operator==(
const extended_group_iterator<Lhs...> &,
const extended_group_iterator<Rhs...> &)
noexcept;
81 std::tuple<Owned *..., Get *...> pools;
84template<
typename... Lhs,
typename... Rhs>
85[[nodiscard]]
constexpr bool operator==(
const extended_group_iterator<Lhs...> &lhs,
const extended_group_iterator<Rhs...> &rhs)
noexcept {
86 return lhs.it == rhs.it;
89template<
typename... Lhs,
typename... Rhs>
90[[nodiscard]]
constexpr bool operator!=(
const extended_group_iterator<Lhs...> &lhs,
const extended_group_iterator<Rhs...> &rhs)
noexcept {
94struct group_descriptor {
95 using size_type = std::size_t;
96 virtual ~group_descriptor() =
default;
97 [[nodiscard]]
virtual bool owned(
const id_type)
const noexcept {
102template<
typename Type, std::
size_t Owned, std::
size_t Get, std::
size_t Exclude>
103class group_handler final:
public group_descriptor {
104 using entity_type =
typename Type::entity_type;
106 void swap_elements(
const std::size_t pos,
const entity_type entt) {
107 for(size_type next{}; next < Owned; ++next) {
108 pools[next]->swap_elements((*pools[next])[pos], entt);
112 void push_on_construct(
const entity_type entt) {
113 if(std::apply([entt, pos = len](
auto *cpool,
auto *...other) {
return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools)
114 && std::apply([entt](
auto *...cpool) {
return (!cpool->contains(entt) && ...); }, filter)) {
115 swap_elements(len++, entt);
119 void push_on_destroy(
const entity_type entt) {
120 if(std::apply([entt, pos = len](
auto *cpool,
auto *...other) {
return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools)
121 && std::apply([entt](
auto *...cpool) {
return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
122 swap_elements(len++, entt);
126 void remove_if(
const entity_type entt) {
127 if(pools[0u]->contains(entt) && (pools[0u]->index(entt) < len)) {
128 swap_elements(--len, entt);
132 void common_setup() {
134 for(
auto first = pools[0u]->rbegin(), last = first +
static_cast<typename decltype(pools)::difference_type
>(pools[0u]->size()); first != last; ++first) {
135 push_on_construct(*first);
140 using common_type = Type;
141 using size_type =
typename Type::size_type;
143 template<
typename... OGType,
typename... EType>
144 group_handler(std::tuple<OGType &...> ogpool, std::tuple<EType &...> epool)
145 : pools{std::apply([](auto &&...cpool) {
return std::array<common_type *, (Owned + Get)>{&cpool...}; }, ogpool)},
146 filter{std::apply([](
auto &&...cpool) {
return std::array<common_type *, Exclude>{&cpool...}; }, epool)} {
147 std::apply([
this](
auto &...cpool) { ((cpool.on_construct().
template connect<&group_handler::push_on_construct>(*
this), cpool.on_destroy().template connect<&group_handler::remove_if>(*
this)), ...); }, ogpool);
148 std::apply([
this](
auto &...cpool) { ((cpool.on_construct().
template connect<&group_handler::remove_if>(*
this), cpool.on_destroy().template connect<&group_handler::push_on_destroy>(*
this)), ...); }, epool);
152 [[nodiscard]]
bool owned(
const id_type hash)
const noexcept override {
153 for(size_type pos{}; pos < Owned; ++pos) {
154 if(pools[pos]->type().hash() == hash) {
162 [[nodiscard]] size_type length() const noexcept {
166 template<std::
size_t Index>
167 [[nodiscard]] common_type *
storage() const noexcept {
168 if constexpr(Index < (Owned + Get)) {
171 return filter[Index - (Owned + Get)];
176 std::array<common_type *, (Owned + Get)> pools;
177 std::array<common_type *, Exclude> filter;
181template<
typename Type, std::
size_t Get, std::
size_t Exclude>
182class group_handler<Type, 0u, Get, Exclude> final:
public group_descriptor {
183 using entity_type =
typename Type::entity_type;
185 void push_on_construct(
const entity_type entt) {
186 if(!elem.contains(entt)
187 && std::apply([entt](
auto *...cpool) { return (cpool->contains(entt) && ...); }, pools)
188 && std::apply([entt](
auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
193 void push_on_destroy(
const entity_type entt) {
194 if(!elem.contains(entt)
195 && std::apply([entt](
auto *...cpool) { return (cpool->contains(entt) && ...); }, pools)
196 && std::apply([entt](
auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
201 void remove_if(
const entity_type entt) {
205 void common_setup() {
206 for(
const auto entity: *pools[0u]) {
207 push_on_construct(entity);
212 using common_type = Type;
214 template<
typename Allocator,
typename... GType,
typename... EType>
215 group_handler(
const Allocator &allocator, std::tuple<GType &...> gpool, std::tuple<EType &...> epool)
216 : pools{std::apply([](auto &&...cpool) {
return std::array<common_type *, Get>{&cpool...}; }, gpool)},
217 filter{std::apply([](
auto &&...cpool) {
return std::array<common_type *, Exclude>{&cpool...}; }, epool)},
219 std::apply([
this](
auto &...cpool) { ((cpool.on_construct().
template connect<&group_handler::push_on_construct>(*
this), cpool.on_destroy().template connect<&group_handler::remove_if>(*
this)), ...); }, gpool);
220 std::apply([
this](
auto &...cpool) { ((cpool.on_construct().
template connect<&group_handler::remove_if>(*
this), cpool.on_destroy().template connect<&group_handler::push_on_destroy>(*
this)), ...); }, epool);
224 [[nodiscard]] common_type &
handle() noexcept {
228 [[nodiscard]]
const common_type &
handle() const noexcept {
232 template<std::
size_t Index>
233 [[nodiscard]] common_type *
storage() const noexcept {
234 if constexpr(Index < Get) {
237 return filter[Index - Get];
242 std::array<common_type *, Get> pools;
243 std::array<common_type *, Exclude> filter;
256template<
typename,
typename,
typename>
281template<
typename... Get,
typename... Exclude>
283 using base_type = std::common_type_t<
typename Get::base_type...,
typename Exclude::base_type...>;
284 using underlying_type =
typename base_type::entity_type;
286 template<
typename Type>
289 template<std::size_t... Index>
290 [[nodiscard]]
auto pools_for(std::index_sequence<Index...>)
const noexcept {
291 using return_type = std::tuple<Get *...>;
292 return descriptor ? return_type{
static_cast<Get *
>(descriptor->template
storage<Index>())...} : return_type{};
330 : descriptor{&
ref} {}
337 return descriptor->handle();
345 template<
typename Type>
346 [[nodiscard]]
auto *
storage() const noexcept {
355 template<std::
size_t Index>
356 [[nodiscard]]
auto *
storage() const noexcept {
358 return *
this ?
static_cast<type *
>(descriptor->template
storage<Index>()) :
nullptr;
381 descriptor->handle().shrink_to_fit();
389 [[nodiscard]]
bool empty() const noexcept {
390 return !*
this ||
handle().empty();
440 const auto it =
begin();
441 return it !=
end() ? *it :
null;
477 [[nodiscard]]
explicit operator bool() const noexcept {
478 return descriptor !=
nullptr;
497 template<
typename Type,
typename... Other>
508 template<std::size_t... Index>
510 const auto cpools = pools_for(std::index_sequence_for<Get...>{});
512 if constexpr(
sizeof...(Index) == 0) {
513 return std::apply([
entt](
auto *...curr) {
return std::tuple_cat(curr->get_as_tuple(
entt)...); }, cpools);
514 }
else if constexpr(
sizeof...(Index) == 1) {
515 return (std::get<Index>(cpools)->
get(
entt), ...);
517 return std::tuple_cat(std::get<Index>(cpools)->get_as_tuple(
entt)...);
543 template<
typename Func>
545 for(
const auto entt: *
this) {
546 if constexpr(
is_applicable_v<Func,
decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
547 std::apply(func, std::tuple_cat(std::make_tuple(
entt),
get(
entt)));
568 const auto cpools = pools_for(std::index_sequence_for<Get...>{});
605 template<
typename Type,
typename... Other,
typename Compare,
typename Sort =
std_sort,
typename... Args>
606 void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
607 sort<index_of<Type>, index_of<Other>...>(std::move(compare), std::move(algo), std::forward<Args>(args)...);
623 template<std::size_t... Index,
typename Compare,
typename Sort = std_sort,
typename... Args>
624 void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
626 if constexpr(
sizeof...(Index) == 0) {
627 static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>,
"Invalid comparison function");
628 descriptor->handle().sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
630 auto comp = [&compare, cpools = pools_for(std::index_sequence_for<Get...>{})](
const entity_type lhs,
const entity_type rhs) {
631 if constexpr(
sizeof...(Index) == 1) {
632 return compare((std::get<Index>(cpools)->
get(lhs), ...), (std::get<Index>(cpools)->
get(rhs), ...));
634 return compare(std::forward_as_tuple(std::get<Index>(cpools)->
get(lhs)...), std::forward_as_tuple(std::get<Index>(cpools)->
get(rhs)...));
638 descriptor->handle().sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
653 template<
typename It>
656 descriptor->handle().sort_as(first, last);
695template<
typename... Owned,
typename... Get,
typename... Exclude>
699 using base_type = std::common_type_t<
typename Owned::base_type...,
typename Get::base_type...,
typename Exclude::base_type...>;
700 using underlying_type =
typename base_type::entity_type;
702 template<
typename Type>
705 template<std::size_t... Index, std::size_t... Other>
706 [[nodiscard]]
auto pools_for(std::index_sequence<Index...>, std::index_sequence<Other...>)
const noexcept {
707 using return_type = std::tuple<Owned *..., Get *...>;
708 return descriptor ? return_type{
static_cast<Owned *
>(descriptor->template
storage<Index>())...,
static_cast<Get *
>(descriptor->template
storage<
sizeof...(Owned) + Other>())...} : return_type{};
727 using handler = internal::group_handler<
common_type,
sizeof...(Owned),
sizeof...(Get),
sizeof...(Exclude)>;
746 : descriptor{&
ref} {}
761 template<
typename Type>
762 [[nodiscard]]
auto *
storage() const noexcept {
771 template<std::
size_t Index>
772 [[nodiscard]]
auto *
storage() const noexcept {
774 return *
this ?
static_cast<type *
>(descriptor->template
storage<Index>()) :
nullptr;
782 return *
this ? descriptor->length() :
size_type{};
789 [[nodiscard]]
bool empty() const noexcept {
790 return !*
this || !descriptor->length();
840 const auto it =
begin();
841 return it !=
end() ? *it :
null;
878 [[nodiscard]]
explicit operator bool() const noexcept {
879 return descriptor !=
nullptr;
898 template<
typename Type,
typename... Other>
909 template<std::size_t... Index>
911 const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
913 if constexpr(
sizeof...(Index) == 0) {
914 return std::apply([
entt](
auto *...curr) {
return std::tuple_cat(curr->get_as_tuple(
entt)...); }, cpools);
915 }
else if constexpr(
sizeof...(Index) == 1) {
916 return (std::get<Index>(cpools)->
get(
entt), ...);
918 return std::tuple_cat(std::get<Index>(cpools)->get_as_tuple(
entt)...);
944 template<
typename Func>
946 for(
auto args:
each()) {
947 if constexpr(
is_applicable_v<Func,
decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
948 std::apply(func, args);
950 std::apply([&func](
auto,
auto &&...less) { func(std::forward<
decltype(less)>(less)...); }, args);
969 const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
1007 template<
typename Type,
typename... Other,
typename Compare,
typename Sort =
std_sort,
typename... Args>
1008 void sort(Compare compare, Sort algo = Sort{}, Args &&...args)
const {
1009 sort<index_of<Type>, index_of<Other>...>(std::move(compare), std::move(algo), std::forward<Args>(args)...);
1025 template<std::size_t... Index,
typename Compare,
typename Sort = std_sort,
typename... Args>
1026 void sort(Compare compare, Sort algo = Sort{}, Args &&...args)
const {
1027 const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
1029 if constexpr(
sizeof...(Index) == 0) {
1030 static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>,
"Invalid comparison function");
1031 storage<0>()->sort_n(descriptor->length(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
1033 auto comp = [&compare, &cpools](
const entity_type lhs,
const entity_type rhs) {
1034 if constexpr(
sizeof...(Index) == 1) {
1035 return compare((std::get<Index>(cpools)->
get(lhs), ...), (std::get<Index>(cpools)->
get(rhs), ...));
1037 return compare(std::forward_as_tuple(std::get<Index>(cpools)->
get(lhs)...), std::forward_as_tuple(std::get<Index>(cpools)->
get(rhs)...));
1041 storage<0>()->sort_n(descriptor->length(), std::move(comp), std::move(algo), std::forward<Args>(args)...);
1044 auto cb = [
this](
auto *head,
auto *...other) {
1045 for(
auto next = descriptor->length(); next; --next) {
1046 const auto pos = next - 1;
1047 [[maybe_unused]]
const auto entt = head->data()[pos];
1048 (other->swap_elements(other->data()[pos], entt), ...);
1052 std::apply(cb, cpools);
1056 handler *descriptor;
const common_type & handle() const noexcept
Returns the leading storage of a group.
void each(Func func) const
Iterates entities and elements and applies the given function object to them.
decltype(auto) get(const entity_type entt) const
Returns the elements assigned to the given entity.
basic_group() noexcept
Default constructor to use to create empty, invalid groups.
entity_type back() const noexcept
Returns the last entity of the group, if any.
iterator begin() const noexcept
Returns an iterator to the first entity of the group.
std::size_t size_type
Unsigned integer type.
base_type common_type
Common type among all storage types.
auto * storage() const noexcept
Returns the storage for a given element type, if any.
internal::group_handler< common_type, sizeof...(Owned), sizeof...(Get), sizeof...(Exclude)> handler
Group handler type.
typename common_type::iterator iterator
Random access iterator type.
iterator find(const entity_type entt) const noexcept
Finds an entity.
bool contains(const entity_type entt) const noexcept
Checks if a group contains an entity.
entity_type front() const noexcept
Returns the first entity of the group, if any.
basic_group(handler &ref) noexcept
Constructs a group from a set of storage classes.
iterator end() const noexcept
Returns an iterator that is past the last entity of the group.
typename common_type::reverse_iterator reverse_iterator
Reverse iterator type.
entity_type operator[](const size_type pos) const
Returns the identifier that occupies the given position.
size_type size() const noexcept
Returns the number of entities that that are part of the group.
iterable each() const noexcept
Returns an iterable object to use to visit a group.
reverse_iterator rend() const noexcept
Returns an iterator that is past the last entity of the reversed group.
void sort(Compare compare, Sort algo=Sort{}, Args &&...args) const
Sort a group according to the given comparison function.
underlying_type entity_type
Underlying entity identifier.
std::ptrdiff_t difference_type
Signed integer type.
decltype(auto) get(const entity_type entt) const
Returns the elements assigned to the given entity.
reverse_iterator rbegin() const noexcept
Returns an iterator to the first entity of the reversed group.
static id_type group_id() noexcept
Group opaque identifier.
bool empty() const noexcept
Checks whether a group is empty.
iterable_adaptor< internal::extended_group_iterator< iterator, owned_t< Owned... >, get_t< Get... > > > iterable
Iterable group type.
void sort(Compare compare, Sort algo=Sort{}, Args &&...args) const
Sort a group according to the given comparison function.
iterator begin() const noexcept
Returns an iterator to the first entity of the group.
void sort_as(It first, It last) const
Sort entities according to their order in a range.
static id_type group_id() noexcept
Group opaque identifier.
auto * storage() const noexcept
Returns the storage for a given element type, if any.
typename common_type::reverse_iterator reverse_iterator
Reverse iterator type.
basic_group(handler &ref) noexcept
Constructs a group from a set of storage classes.
entity_type operator[](const size_type pos) const
Returns the identifier that occupies the given position.
bool contains(const entity_type entt) const noexcept
Checks if a group contains an entity.
void sort(Compare compare, Sort algo=Sort{}, Args &&...args)
Sort a group according to the given comparison function.
reverse_iterator rbegin() const noexcept
Returns an iterator to the first entity of the reversed group.
iterable_adaptor< internal::extended_group_iterator< iterator, owned_t<>, get_t< Get... > > > iterable
Iterable group type.
const common_type & handle() const noexcept
Returns the leading storage of a group.
void sort(Compare compare, Sort algo=Sort{}, Args &&...args)
Sort a group according to the given comparison function.
decltype(auto) get(const entity_type entt) const
Returns the elements assigned to the given entity.
bool empty() const noexcept
Checks whether a group is empty.
std::ptrdiff_t difference_type
Signed integer type.
void each(Func func) const
Iterates entities and elements and applies the given function object to them.
decltype(auto) get(const entity_type entt) const
Returns the elements assigned to the given entity.
std::size_t size_type
Unsigned integer type.
size_type capacity() const noexcept
Returns the number of elements that a group has currently allocated space for.
void shrink_to_fit()
Requests the removal of unused capacity.
entity_type front() const noexcept
Returns the first entity of the group, if any.
reverse_iterator rend() const noexcept
Returns an iterator that is past the last entity of the reversed group.
internal::group_handler< common_type, 0u, sizeof...(Get), sizeof...(Exclude)> handler
Group handler type.
underlying_type entity_type
Underlying entity identifier.
iterator find(const entity_type entt) const noexcept
Finds an entity.
iterator end() const noexcept
Returns an iterator that is past the last entity of the group.
typename common_type::iterator iterator
Random access iterator type.
entity_type back() const noexcept
Returns the last entity of the group, if any.
iterable each() const noexcept
Returns an iterable object to use to visit a group.
base_type common_type
Common type among all storage types.
basic_group() noexcept
Default constructor to use to create empty, invalid groups.
size_type size() const noexcept
Returns the number of entities that are part of the group.
constexpr null_t null
Compile-time constant for null entities.
constexpr std::size_t type_list_index_v
Helper variable template.
basic_handle< registry > handle
Alias declaration for the most common use case.
constexpr bool is_applicable_v
Helper variable template.
std::uint32_t id_type
Alias declaration for type identifiers.
constexpr get_t< Type... > get
Variable template for lists of observed elements.
typename type_list_element< Index, List >::type type_list_element_t
Helper type.
@ in_place
In-place deletion policy.
constexpr owned_t< Type... > owned
Variable template for lists of owned elements.
constexpr bool operator!=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
@ ref
Aliasing mode, the object points to a non-const element.
constexpr bool operator==(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
basic_storage< Type > storage
Alias declaration for the most common use case.
Alias for exclusion lists.
Alias for lists of observed elements.
Utility class to create an iterable object from a pair of iterators.
Alias for lists of owned elements.
Function object to wrap std::sort in a class type.
A class to use to push around lists of types, nothing more.