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 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 + 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>
183 using entity_type =
typename Type::entity_type;
187 && std::apply([
entt](
auto *...cpool) { return (cpool->contains(entt) && ...); }, pools)
188 && std::apply([
entt](
auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
195 && std::apply([
entt](
auto *...cpool) { return (cpool->contains(entt) && ...); }, pools)
196 && std::apply([
entt](
auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
206 for(
const auto entity: *pools[0
u]) {
212 using common_type = Type;
214 template<
typename Allocator,
typename...
GType,
typename...
EType>
217 filter{std::apply([](
auto &&...
cpool) {
return std::array<common_type *, Exclude>{&
cpool...}; },
epool)},
232 template<std::
size_t Index>
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>
287 static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>,
type_list<
typename Get::element_type...,
typename Exclude::element_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{};
328 : descriptor{&
ref} {}
335 return descriptor->handle();
343 template<
typename Type>
344 [[nodiscard]]
auto *
storage() const noexcept {
353 template<std::
size_t Index>
354 [[nodiscard]]
auto *
storage() const noexcept {
356 return *
this ?
static_cast<type *
>(descriptor->template
storage<Index>()) :
nullptr;
379 descriptor->handle().shrink_to_fit();
387 [[nodiscard]]
bool empty() const noexcept {
388 return !*
this ||
handle().empty();
438 const auto it = begin();
439 return it != end() ? *it :
null;
448 const auto it = rbegin();
449 return it != rend() ? *it :
null;
475 [[nodiscard]]
explicit operator bool() const noexcept {
476 return descriptor !=
nullptr;
495 template<
typename Type,
typename... Other>
497 return get<index_of<Type>, index_of<Other>...>(
entt);
506 template<std::size_t... Index>
508 const auto cpools = pools_for(std::index_sequence_for<Get...>{});
510 if constexpr(
sizeof...(Index) == 0) {
511 return std::apply([
entt](
auto *...curr) {
return std::tuple_cat(curr->get_as_tuple(
entt)...); }, cpools);
512 }
else if constexpr(
sizeof...(Index) == 1) {
513 return (std::get<Index>(cpools)->get(
entt), ...);
515 return std::tuple_cat(std::get<Index>(cpools)->get_as_tuple(
entt)...);
541 template<
typename Func>
543 for(
const auto entt: *
this) {
544 if constexpr(
is_applicable_v<Func,
decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
545 std::apply(func, std::tuple_cat(std::make_tuple(
entt),
get(
entt)));
566 const auto cpools = pools_for(std::index_sequence_for<Get...>{});
567 return iterable{{begin(), cpools}, {end(), cpools}};
603 template<
typename Type,
typename... Other,
typename Compare,
typename Sort =
std_sort,
typename... Args>
604 void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
605 sort<index_of<Type>, index_of<Other>...>(std::move(compare), std::move(algo), std::forward<Args>(args)...);
621 template<std::size_t... Index,
typename Compare,
typename Sort = std_sort,
typename... Args>
622 void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
624 if constexpr(
sizeof...(Index) == 0) {
625 static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>,
"Invalid comparison function");
626 descriptor->handle().sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
628 auto comp = [&compare, cpools = pools_for(std::index_sequence_for<Get...>{})](
const entity_type lhs,
const entity_type rhs) {
629 if constexpr(
sizeof...(Index) == 1) {
630 return compare((std::get<Index>(cpools)->
get(lhs), ...), (std::get<Index>(cpools)->
get(rhs), ...));
632 return compare(std::forward_as_tuple(std::get<Index>(cpools)->
get(lhs)...), std::forward_as_tuple(std::get<Index>(cpools)->
get(rhs)...));
636 descriptor->handle().sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
651 template<
typename It>
654 descriptor->handle().sort_as(first, last);
693template<
typename... Owned,
typename... Get,
typename... Exclude>
695 static_assert(((Owned::storage_policy != deletion_policy::in_place) && ...),
"Groups do not support in-place delete");
697 using base_type = std::common_type_t<
typename Owned::base_type...,
typename Get::base_type...,
typename Exclude::base_type...>;
698 using underlying_type =
typename base_type::entity_type;
700 template<
typename Type>
701 static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>,
type_list<
typename Owned::element_type...,
typename Get::element_type...,
typename Exclude::element_type...>>;
703 template<std::size_t... Index, std::size_t... Other>
704 [[nodiscard]]
auto pools_for(std::index_sequence<Index...>, std::index_sequence<Other...>)
const noexcept {
705 using return_type = std::tuple<Owned *..., Get *...>;
706 return descriptor ? return_type{
static_cast<Owned *
>(descriptor->template
storage<Index>())...,
static_cast<Get *
>(descriptor->template
storage<
sizeof...(Owned) + Other>())...} : return_type{};
723 using handler = internal::group_handler<
common_type,
sizeof...(Owned),
sizeof...(Get),
sizeof...(Exclude)>;
742 : descriptor{&
ref} {}
757 template<
typename Type>
758 [[nodiscard]]
auto *
storage() const noexcept {
767 template<std::
size_t Index>
768 [[nodiscard]]
auto *
storage() const noexcept {
770 return *
this ?
static_cast<type *
>(descriptor->template
storage<Index>()) :
nullptr;
778 return *
this ? descriptor->length() :
size_type{};
785 [[nodiscard]]
bool empty() const noexcept {
786 return !*
this || !descriptor->length();
797 return *
this ? (
handle().end() - descriptor->length()) :
iterator{};
836 const auto it = begin();
837 return it != end() ? *it :
null;
846 const auto it = rbegin();
847 return it != rend() ? *it :
null;
858 return it >= begin() ? it :
iterator{};
874 [[nodiscard]]
explicit operator bool() const noexcept {
875 return descriptor !=
nullptr;
894 template<
typename Type,
typename... Other>
896 return get<index_of<Type>, index_of<Other>...>(
entt);
905 template<std::size_t... Index>
907 const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
909 if constexpr(
sizeof...(Index) == 0) {
910 return std::apply([
entt](
auto *...curr) {
return std::tuple_cat(curr->get_as_tuple(
entt)...); }, cpools);
911 }
else if constexpr(
sizeof...(Index) == 1) {
912 return (std::get<Index>(cpools)->get(
entt), ...);
914 return std::tuple_cat(std::get<Index>(cpools)->get_as_tuple(
entt)...);
940 template<
typename Func>
942 for(
auto args: each()) {
943 if constexpr(
is_applicable_v<Func,
decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
944 std::apply(func, args);
946 std::apply([&func](
auto,
auto &&...less) { func(std::forward<
decltype(less)>(less)...); }, args);
965 const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
966 return iterable{{begin(), cpools}, {end(), cpools}};
1003 template<
typename Type,
typename... Other,
typename Compare,
typename Sort =
std_sort,
typename... Args>
1004 void sort(Compare compare, Sort algo = Sort{}, Args &&...args)
const {
1005 sort<index_of<Type>, index_of<Other>...>(std::move(compare), std::move(algo), std::forward<Args>(args)...);
1021 template<std::size_t... Index,
typename Compare,
typename Sort = std_sort,
typename... Args>
1022 void sort(Compare compare, Sort algo = Sort{}, Args &&...args)
const {
1023 const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
1025 if constexpr(
sizeof...(Index) == 0) {
1026 static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>,
"Invalid comparison function");
1027 storage<0>()->sort_n(descriptor->length(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
1029 auto comp = [&compare, &cpools](
const entity_type lhs,
const entity_type rhs) {
1030 if constexpr(
sizeof...(Index) == 1) {
1031 return compare((std::get<Index>(cpools)->
get(lhs), ...), (std::get<Index>(cpools)->
get(rhs), ...));
1033 return compare(std::forward_as_tuple(std::get<Index>(cpools)->
get(lhs)...), std::forward_as_tuple(std::get<Index>(cpools)->
get(rhs)...));
1037 storage<0>()->sort_n(descriptor->length(), std::move(comp), std::move(algo), std::forward<Args>(args)...);
1040 auto cb = [
this](
auto *head,
auto *...other) {
1041 for(
auto next = descriptor->length(); next; --next) {
1042 const auto pos = next - 1;
1043 [[maybe_unused]]
const auto entt = head->data()[pos];
1044 (other->swap_elements(other->data()[pos],
entt), ...);
1048 std::apply(cb, cpools);
1052 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.
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.
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.
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.
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.
Non-owning handle to an entity.
entity
Default entity identifier.
constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args)
Uses-allocator construction utility (waiting for C++20).
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.
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.
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.