1#ifndef ENTT_ENTITY_GROUP_HPP
2#define ENTT_ENTITY_GROUP_HPP
4#include "../config/config.h"
5#include "../core/algorithm.hpp"
6#include "../core/fwd.hpp"
7#include "../core/iterator.hpp"
8#include "../core/type_info.hpp"
9#include "../core/type_traits.hpp"
10#include "../stl/array.hpp"
11#include "../stl/concepts.hpp"
12#include "../stl/cstddef.hpp"
13#include "../stl/iterator.hpp"
14#include "../stl/tuple.hpp"
15#include "../stl/type_traits.hpp"
16#include "../stl/utility.hpp"
25template<
typename,
typename,
typename>
26class extended_group_iterator;
28template<
typename It,
typename... Owned,
typename... Get>
29class extended_group_iterator<It, owned_t<Owned...>, get_t<Get...>> {
30 template<
typename Type>
31 [[nodiscard]]
auto index_to_element([[maybe_unused]] Type &cpool)
const {
32 if constexpr(stl::is_void_v<typename Type::value_type>) {
33 return stl::make_tuple();
35 return stl::forward_as_tuple(cpool.rbegin()[it.index()]);
40 using iterator_type = It;
41 using value_type =
decltype(stl::tuple_cat(stl::make_tuple(*stl::declval<It>()), stl::declval<Owned>().get_as_tuple({})..., stl::declval<Get>().get_as_tuple({})...));
42 using pointer = input_iterator_pointer<value_type>;
43 using reference = value_type;
44 using difference_type = stl::ptrdiff_t;
45 using iterator_category = stl::input_iterator_tag;
46 using iterator_concept = stl::forward_iterator_tag;
48 constexpr extended_group_iterator()
52 extended_group_iterator(iterator_type from, stl::tuple<Owned *..., Get *...> cpools)
54 pools{stl::move(cpools)} {}
56 extended_group_iterator &operator++() noexcept {
60 extended_group_iterator operator++(
int)
noexcept {
61 const extended_group_iterator orig = *
this;
62 return ++(*this), orig;
65 [[nodiscard]] reference operator*() const noexcept {
66 return stl::tuple_cat(stl::make_tuple(*it), index_to_element(*stl::get<Owned *>(pools))..., stl::get<Get *>(pools)->get_as_tuple(*it)...);
69 [[nodiscard]] pointer operator->() const noexcept {
73 [[nodiscard]]
constexpr iterator_type base() const noexcept {
77 template<
typename... Args>
78 [[nodiscard]]
constexpr bool operator==(
const extended_group_iterator<Args...> &other)
const noexcept {
79 return it == other.it;
84 stl::tuple<Owned *..., Get *...> pools;
87struct group_descriptor {
88 using size_type = stl::size_t;
89 virtual ~group_descriptor() =
default;
90 [[nodiscard]]
virtual bool owned(
const id_type)
const noexcept {
95template<
typename Type, stl::
size_t Owned, stl::
size_t Get, stl::
size_t Exclude>
96class group_handler final:
public group_descriptor {
97 using entity_type = Type::entity_type;
99 void swap_elements(
const stl::size_t pos,
const entity_type entt) {
100 for(size_type next{}; next < Owned; ++next) {
101 pools[next]->swap_elements((*pools[next])[pos], entt);
105 void push_on_construct(
const entity_type entt) {
106 if(stl::apply([entt, pos = len](
auto *cpool,
auto *...other) {
return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools)
107 && stl::apply([entt](
auto *...cpool) {
return (!cpool->contains(entt) && ...); }, filter)) {
108 swap_elements(len++, entt);
112 void push_on_destroy(
const entity_type entt) {
113 if(stl::apply([entt, pos = len](
auto *cpool,
auto *...other) {
return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools)
114 && stl::apply([entt](
auto *...cpool) {
return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
115 swap_elements(len++, entt);
119 void remove_if(
const entity_type entt) {
120 if(pools[0u]->contains(entt) && (pools[0u]->index(entt) < len)) {
121 swap_elements(--len, entt);
125 void common_setup() {
127 for(
auto first = pools[0u]->rbegin(), last = first +
static_cast<decltype(pools)::difference_type
>(pools[0u]->size()); first != last; ++first) {
128 push_on_construct(*first);
133 using common_type = Type;
134 using size_type = Type::size_type;
136 template<
typename... OGType,
typename... EType>
137 group_handler(stl::tuple<OGType &...> ogpool, stl::tuple<EType &...> epool)
138 : pools{stl::apply([](auto &&...cpool) {
return stl::array<common_type *, (Owned + Get)>{&cpool...}; }, ogpool)},
139 filter{stl::apply([](
auto &&...cpool) {
return stl::array<common_type *, Exclude>{&cpool...}; }, epool)} {
140 stl::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);
141 stl::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);
145 [[nodiscard]]
bool owned(
const id_type hash)
const noexcept override {
146 for(size_type pos{}; pos < Owned; ++pos) {
147 if(pools[pos]->info().hash() == hash) {
155 [[nodiscard]] size_type length() const noexcept {
159 template<stl::
size_t Index>
160 [[nodiscard]] common_type *
storage() const noexcept {
161 if constexpr(Index < (Owned + Get)) {
164 return filter[Index - (Owned + Get)];
169 stl::array<common_type *, (Owned + Get)> pools;
170 stl::array<common_type *, Exclude> filter;
174template<
typename Type, stl::
size_t Get, stl::
size_t Exclude>
175class group_handler<Type, 0u, Get, Exclude> final:
public group_descriptor {
176 using entity_type = Type::entity_type;
178 void push_on_construct(
const entity_type entt) {
179 if(!elem.contains(entt)
180 && stl::apply([entt](
auto *...cpool) { return (cpool->contains(entt) && ...); }, pools)
181 && stl::apply([entt](
auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
186 void push_on_destroy(
const entity_type entt) {
187 if(!elem.contains(entt)
188 && stl::apply([entt](
auto *...cpool) { return (cpool->contains(entt) && ...); }, pools)
189 && stl::apply([entt](
auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
194 void remove_if(
const entity_type entt) {
198 void common_setup() {
199 for(
const auto entity: *pools[0u]) {
200 push_on_construct(entity);
205 using common_type = Type;
207 template<
typename Allocator,
typename... GType,
typename... EType>
208 group_handler(
const Allocator &allocator, stl::tuple<GType &...> gpool, stl::tuple<EType &...> epool)
209 : pools{stl::apply([](auto &&...cpool) {
return stl::array<common_type *, Get>{&cpool...}; }, gpool)},
210 filter{stl::apply([](
auto &&...cpool) {
return stl::array<common_type *, Exclude>{&cpool...}; }, epool)},
212 stl::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);
213 stl::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);
217 [[nodiscard]] common_type &
handle() noexcept {
221 [[nodiscard]]
const common_type &
handle() const noexcept {
225 template<stl::
size_t Index>
226 [[nodiscard]] common_type *
storage() const noexcept {
227 if constexpr(Index < Get) {
230 return filter[Index - Get];
235 stl::array<common_type *, Get> pools;
236 stl::array<common_type *, Exclude> filter;
249template<
typename,
typename,
typename>
274template<
typename... Get,
typename... Exclude>
276 using base_type = stl::common_type_t<
typename Get::base_type...,
typename Exclude::base_type...>;
277 using underlying_type = base_type::entity_type;
279 template<
typename Type>
282 template<stl::size_t... Index>
283 [[nodiscard]]
auto pools_for(stl::index_sequence<Index...>)
const noexcept {
284 using return_type = stl::tuple<Get *...>;
285 return descriptor ? return_type{
static_cast<Get *
>(descriptor->template
storage<Index>())...} : return_type{};
323 : descriptor{&
ref} {}
330 return descriptor->handle();
338 template<
typename Type>
339 [[nodiscard]]
auto *
storage() const noexcept {
348 template<stl::
size_t Index>
349 [[nodiscard]]
auto *
storage() const noexcept {
374 descriptor->handle().shrink_to_fit();
382 [[nodiscard]]
bool empty() const noexcept {
383 return !*
this ||
handle().empty();
433 const auto it =
begin();
434 return it !=
end() ? *it :
null;
470 [[nodiscard]]
explicit operator bool() const noexcept {
471 return descriptor !=
nullptr;
490 template<
typename Type,
typename... Other>
501 template<stl::size_t... Index>
503 const auto cpools = pools_for(stl::index_sequence_for<Get...>{});
505 if constexpr(
sizeof...(Index) == 0) {
506 return stl::apply([
entt](
auto *...curr) {
return stl::tuple_cat(curr->get_as_tuple(
entt)...); }, cpools);
507 }
else if constexpr(
sizeof...(Index) == 1) {
508 return (stl::get<Index>(cpools)->
get(
entt), ...);
510 return stl::tuple_cat(stl::get<Index>(cpools)->get_as_tuple(
entt)...);
536 template<
typename Func>
538 for(
const auto entt: *
this) {
539 if constexpr(
is_applicable_v<Func,
decltype(stl::tuple_cat(stl::tuple<entity_type>{}, stl::declval<basic_group>().get({})))>) {
540 stl::apply(func, stl::tuple_cat(stl::make_tuple(
entt),
get(
entt)));
561 const auto cpools = pools_for(stl::index_sequence_for<Get...>{});
598 template<
typename Type,
typename... Other,
typename Compare,
typename Sort =
std_sort,
typename... Args>
599 void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
600 sort<index_of<Type>, index_of<Other>...>(stl::move(compare), stl::move(algo), stl::forward<Args>(args)...);
616 template<stl::size_t... Index,
typename Compare,
typename Sort = std_sort,
typename... Args>
617 void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
619 if constexpr(
sizeof...(Index) == 0) {
620 static_assert(stl::is_invocable_v<Compare, const entity_type, const entity_type>,
"Invalid comparison function");
621 descriptor->handle().sort(stl::move(compare), stl::move(algo), stl::forward<Args>(args)...);
623 auto comp = [&compare, cpools = pools_for(stl::index_sequence_for<Get...>{})](
const entity_type lhs,
const entity_type rhs) {
624 if constexpr(
sizeof...(Index) == 1) {
625 return compare((stl::get<Index>(cpools)->
get(lhs), ...), (stl::get<Index>(cpools)->
get(rhs), ...));
627 return compare(stl::forward_as_tuple(stl::get<Index>(cpools)->
get(lhs)...), stl::forward_as_tuple(stl::get<Index>(cpools)->
get(rhs)...));
631 descriptor->handle().sort(stl::move(comp), stl::move(algo), stl::forward<Args>(args)...);
645 void sort_as(stl::input_iterator
auto first, stl::input_iterator
auto last)
const {
647 descriptor->handle().sort_as(first, last);
686template<
typename... Owned,
typename... Get,
typename... Exclude>
690 using base_type = stl::common_type_t<
typename Owned::base_type...,
typename Get::base_type...,
typename Exclude::base_type...>;
691 using underlying_type = base_type::entity_type;
693 template<
typename Type>
696 template<stl::size_t... Index, stl::size_t... Other>
697 [[nodiscard]]
auto pools_for(stl::index_sequence<Index...>, stl::index_sequence<Other...>)
const noexcept {
698 using return_type = stl::tuple<Owned *..., Get *...>;
699 return descriptor ? return_type{
static_cast<Owned *
>(descriptor->template
storage<Index>())...,
static_cast<Get *
>(descriptor->template
storage<
sizeof...(Owned) + Other>())...} : return_type{};
718 using handler = internal::group_handler<
common_type,
sizeof...(Owned),
sizeof...(Get),
sizeof...(Exclude)>;
737 : descriptor{&
ref} {}
752 template<
typename Type>
753 [[nodiscard]]
auto *
storage() const noexcept {
762 template<stl::
size_t Index>
763 [[nodiscard]]
auto *
storage() const noexcept {
773 return *
this ? descriptor->length() :
size_type{};
780 [[nodiscard]]
bool empty() const noexcept {
781 return !*
this || !descriptor->length();
831 const auto it =
begin();
832 return it !=
end() ? *it :
null;
869 [[nodiscard]]
explicit operator bool() const noexcept {
870 return descriptor !=
nullptr;
889 template<
typename Type,
typename... Other>
900 template<stl::size_t... Index>
902 const auto cpools = pools_for(stl::index_sequence_for<Owned...>{}, stl::index_sequence_for<Get...>{});
904 if constexpr(
sizeof...(Index) == 0) {
905 return stl::apply([
entt](
auto *...curr) {
return stl::tuple_cat(curr->get_as_tuple(
entt)...); }, cpools);
906 }
else if constexpr(
sizeof...(Index) == 1) {
907 return (stl::get<Index>(cpools)->
get(
entt), ...);
909 return stl::tuple_cat(stl::get<Index>(cpools)->get_as_tuple(
entt)...);
935 template<
typename Func>
937 for(
auto args:
each()) {
938 if constexpr(
is_applicable_v<Func,
decltype(stl::tuple_cat(stl::tuple<entity_type>{}, stl::declval<basic_group>().get({})))>) {
939 stl::apply(func, args);
941 stl::apply([&func](
auto,
auto &&...less) { func(stl::forward<
decltype(less)>(less)...); }, args);
960 const auto cpools = pools_for(stl::index_sequence_for<Owned...>{}, stl::index_sequence_for<Get...>{});
998 template<
typename Type,
typename... Other,
typename Compare,
typename Sort =
std_sort,
typename... Args>
999 void sort(Compare compare, Sort algo = Sort{}, Args &&...args)
const {
1000 sort<index_of<Type>, index_of<Other>...>(stl::move(compare), stl::move(algo), stl::forward<Args>(args)...);
1016 template<stl::size_t... Index,
typename Compare,
typename Sort = std_sort,
typename... Args>
1017 void sort(Compare compare, Sort algo = Sort{}, Args &&...args)
const {
1018 const auto cpools = pools_for(stl::index_sequence_for<Owned...>{}, stl::index_sequence_for<Get...>{});
1020 if constexpr(
sizeof...(Index) == 0) {
1021 static_assert(stl::is_invocable_v<Compare, const entity_type, const entity_type>,
"Invalid comparison function");
1022 storage<0>()->sort_n(descriptor->length(), stl::move(compare), stl::move(algo), stl::forward<Args>(args)...);
1024 auto comp = [&compare, &cpools](
const entity_type lhs,
const entity_type rhs) {
1025 if constexpr(
sizeof...(Index) == 1) {
1026 return compare((stl::get<Index>(cpools)->
get(lhs), ...), (stl::get<Index>(cpools)->
get(rhs), ...));
1028 return compare(stl::forward_as_tuple(stl::get<Index>(cpools)->
get(lhs)...), stl::forward_as_tuple(stl::get<Index>(cpools)->
get(rhs)...));
1032 storage<0>()->sort_n(descriptor->length(), stl::move(comp), stl::move(algo), stl::forward<Args>(args)...);
1035 auto cb = [
this](
auto *head,
auto *...other) {
1036 for(
auto next = descriptor->length(); next; --next) {
1037 const auto pos = next - 1;
1038 [[maybe_unused]]
const auto entt = head->data()[pos];
1039 (other->swap_elements(other->data()[pos], entt), ...);
1043 stl::apply(cb, cpools);
1047 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.
stl::size_t size_type
Unsigned integer type.
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.
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.
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.
common_type::reverse_iterator reverse_iterator
Reverse iterator type.
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.
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.
common_type::iterator iterator
Random access iterator 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.
stl::ptrdiff_t difference_type
Signed integer 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.
static id_type group_id() noexcept
Group opaque identifier.
auto * storage() const noexcept
Returns the storage for a given element type, if any.
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.
stl::ptrdiff_t difference_type
Signed integer type.
stl::size_t size_type
Unsigned integer type.
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.
common_type::iterator iterator
Random access iterator type.
bool empty() const noexcept
Checks whether a group is empty.
void each(Func func) const
Iterates entities and elements and applies the given function object to them.
common_type::reverse_iterator reverse_iterator
Reverse iterator type.
decltype(auto) get(const entity_type entt) const
Returns the elements assigned to the given entity.
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.
entity_type back() const noexcept
Returns the last entity of the group, if any.
void sort_as(stl::input_iterator auto first, stl::input_iterator auto last) const
Sort entities according to their order in a range.
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.
@ in_place
In-place deletion policy.
type_list_element< Index, List >::type type_list_element_t
Helper type.
constexpr null_t null
Compile-time constant for null entities.
basic_handle< registry > handle
Alias declaration for the most common use case.
constexpr bool is_applicable_v
Helper variable template.
constexpr stl::size_t type_list_index_v
Helper variable template.
constexpr get_t< Type... > get
Variable template for lists of observed elements.
constexpr owned_t< Type... > owned
Variable template for lists of owned elements.
@ ref
Aliasing mode, non-const reference.
stl::uint32_t id_type
Alias declaration for type identifiers.
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 stl::sort in a class type.
A class to use to push around lists of types, nothing more.