1#ifndef ENTT_ENTITY_GROUP_HPP
2#define ENTT_ENTITY_GROUP_HPP
7#include "../config/config.h"
8#include "../core/fwd.hpp"
9#include "../core/iterator.hpp"
10#include "../core/type_info.hpp"
11#include "../core/type_traits.hpp"
14#include "sparse_set.hpp"
22template<
typename,
typename,
typename>
23class extended_group_iterator;
25template<
typename It,
typename... Owned,
typename... Get>
26class extended_group_iterator<It, owned_t<Owned...>, get_t<Get...>> {
27 template<
typename Type>
28 auto index_to_element([[maybe_unused]] Type &cpool)
const {
29 if constexpr(Type::traits_type::page_size == 0u) {
30 return std::make_tuple();
32 return std::forward_as_tuple(cpool.rbegin()[it.index()]);
37 using iterator_type = It;
38 using difference_type = std::ptrdiff_t;
39 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({})...));
40 using pointer = input_iterator_pointer<value_type>;
41 using reference = value_type;
42 using iterator_category = std::input_iterator_tag;
43 using iterator_concept = std::forward_iterator_tag;
45 constexpr extended_group_iterator()
49 extended_group_iterator(iterator_type from,
const std::tuple<Owned *..., Get *...> &cpools)
53 extended_group_iterator &operator++() noexcept {
57 extended_group_iterator operator++(
int)
noexcept {
58 extended_group_iterator orig = *
this;
59 return ++(*this), orig;
62 [[nodiscard]] reference operator*() const noexcept {
63 return std::tuple_cat(std::make_tuple(*it), index_to_element(*std::get<Owned *>(pools))..., std::get<Get *>(pools)->get_as_tuple(*it)...);
66 [[nodiscard]] pointer operator->() const noexcept {
70 [[nodiscard]]
constexpr iterator_type base() const noexcept {
74 template<
typename... Lhs,
typename... Rhs>
75 friend constexpr bool operator==(
const extended_group_iterator<Lhs...> &,
const extended_group_iterator<Rhs...> &)
noexcept;
79 std::tuple<Owned *..., Get *...> pools;
82template<
typename... Lhs,
typename... Rhs>
83[[nodiscard]]
constexpr bool operator==(
const extended_group_iterator<Lhs...> &lhs,
const extended_group_iterator<Rhs...> &rhs)
noexcept {
84 return lhs.it == rhs.it;
87template<
typename... Lhs,
typename... Rhs>
88[[nodiscard]]
constexpr bool operator!=(
const extended_group_iterator<Lhs...> &lhs,
const extended_group_iterator<Rhs...> &rhs)
noexcept {
92struct group_descriptor {
93 using size_type = std::size_t;
94 virtual ~group_descriptor() =
default;
95 virtual size_type
owned(
const id_type *,
const size_type)
const noexcept {
100template<
typename,
typename,
typename>
103template<
typename... Owned,
typename... Get,
typename... Exclude>
104class group_handler<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> final:
public group_descriptor {
106 static_assert(!std::disjunction_v<std::bool_constant<Owned::traits_type::in_place_delete>...>,
"Groups do not support in-place delete");
107 static_assert(!std::disjunction_v<std::is_const<Owned>..., std::is_const<Get>..., std::is_const<Exclude>...>,
"Const storage type not allowed");
109 using base_type = std::common_type_t<
typename Owned::base_type...,
typename Get::base_type...,
typename Exclude::base_type...>;
110 using entity_type =
typename base_type::entity_type;
112 template<std::size_t... Index>
113 void swap_elements(
const std::size_t pos,
const entity_type
entt, std::index_sequence<Index...>) {
114 (std::get<Index>(pools)->swap_elements(std::get<Index>(pools)->data()[pos],
entt), ...);
117 void push_on_construct(
const entity_type
entt) {
118 if(std::apply([
entt, len = len](
auto *cpool,
auto *...other) {
return cpool->contains(
entt) && !(cpool->index(
entt) < len) && (other->contains(
entt) && ...); }, pools)
119 && std::apply([
entt](
auto *...cpool) {
return (!cpool->contains(
entt) && ...); }, filter)) {
120 swap_elements(len++,
entt, std::index_sequence_for<Owned...>{});
124 void push_on_destroy(
const entity_type
entt) {
125 if(std::apply([
entt, len = len](
auto *cpool,
auto *...other) {
return cpool->contains(
entt) && !(cpool->index(
entt) < len) && (other->contains(
entt) && ...); }, pools)
126 && std::apply([
entt](
auto *...cpool) {
return (0u + ... + cpool->contains(
entt)) == 1u; }, filter)) {
127 swap_elements(len++,
entt, std::index_sequence_for<Owned...>{});
131 void remove_if(
const entity_type
entt) {
132 if(std::get<0>(pools)->contains(
entt) && (std::get<0>(pools)->index(
entt) < len)) {
133 swap_elements(--len,
entt, std::index_sequence_for<Owned...>{});
138 using size_type =
typename base_type::size_type;
140 group_handler(Owned &...opool, Get &...gpool, Exclude &...epool)
141 : pools{&opool..., &gpool...},
144 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)), ...); }, pools);
145 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)), ...); }, filter);
148 for(
auto *first = std::get<0>(pools)->data(), *last = first + std::get<0>(pools)->size(); first != last; ++first) {
149 push_on_construct(*first);
153 size_type
owned(
const id_type *elem,
const size_type length)
const noexcept final {
156 for(
auto pos = 0u; pos < length; ++pos) {
163 [[nodiscard]] size_type length() const noexcept {
167 auto pools_as_tuple() const noexcept {
171 auto filter_as_tuple() const noexcept {
176 std::tuple<Owned *..., Get *...> pools;
177 std::tuple<Exclude *...> filter;
181template<
typename... Get,
typename... Exclude>
182class group_handler<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> final:
public group_descriptor {
184 static_assert(!std::disjunction_v<std::is_const<Get>..., std::is_const<Exclude>...>,
"Const storage type not allowed");
186 using base_type = std::common_type_t<
typename Get::base_type...,
typename Exclude::base_type...>;
187 using entity_type =
typename base_type::entity_type;
189 void push_on_construct(
const entity_type
entt) {
190 if(!elem.contains(
entt)
191 && std::apply([
entt](
auto *...cpool) { return (cpool->contains(entt) && ...); }, pools)
192 && std::apply([
entt](
auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
197 void push_on_destroy(
const entity_type
entt) {
198 if(!elem.contains(
entt)
199 && std::apply([
entt](
auto *...cpool) { return (cpool->contains(entt) && ...); }, pools)
200 && std::apply([
entt](
auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
205 void remove_if(
const entity_type
entt) {
210 using common_type = base_type;
212 template<
typename Alloc>
213 group_handler(
const Alloc &alloc, Get &...gpool, Exclude &...epool)
217 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)), ...); }, pools);
218 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)), ...); }, filter);
220 for(
const auto entity: static_cast<base_type &>(*std::
get<0>(pools))) {
221 push_on_construct(entity);
225 common_type &
handle() noexcept {
229 const common_type &
handle() const noexcept {
233 auto pools_as_tuple() const noexcept {
237 auto filter_as_tuple() const noexcept {
242 std::tuple<Get *...> pools;
243 std::tuple<Exclude *...> filter;
256template<
typename,
typename,
typename>
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 auto pools()
const noexcept {
290 using return_type = std::tuple<
Get *...>;
291 return descriptor ? descriptor->pools_as_tuple() : return_type{};
294 auto filter()
const noexcept {
295 using return_type = std::tuple<
Exclude *...>;
296 return descriptor ? descriptor->filter_as_tuple() : return_type{};
324 : descriptor{&
ref} {}
331 return descriptor->handle();
339 template<
typename Type>
349 template<std::
size_t Index>
351 constexpr auto offset =
sizeof...(Get);
354 return std::get<Index>(pools());
380 descriptor->handle().shrink_to_fit();
389 return !*
this ||
handle().empty();
439 const auto it = begin();
449 const auto it = rbegin();
477 return descriptor !=
nullptr;
496 template<
typename Type,
typename...
Other>
507 template<std::size_t...
Index>
509 const auto cpools = pools();
511 if constexpr(
sizeof...(Index) == 0) {
513 }
else if constexpr(
sizeof...(Index) == 1) {
514 return (std::get<Index>(
cpools)->get(
entt), ...);
516 return std::tuple_cat(std::get<Index>(
cpools)->get_as_tuple(
entt)...);
542 template<
typename Func>
544 for(
const auto entt: *
this) {
545 if constexpr(
is_applicable_v<Func,
decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
546 std::apply(func, std::tuple_cat(std::make_tuple(
entt),
get(
entt)));
567 const auto cpools = pools();
625 if constexpr(
sizeof...(Index) == 0) {
626 static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>,
"Invalid comparison function");
627 descriptor->handle().sort(std::move(
compare), std::move(
algo), std::forward<Args>(
args)...);
630 if constexpr(
sizeof...(Index) == 1) {
637 descriptor->handle().sort(std::move(
comp), std::move(
algo), std::forward<Args>(
args)...);
652 template<
typename It>
655 descriptor->handle().sort_as(first, last);
704 using base_type = std::common_type_t<
typename Owned::base_type...,
typename Get::base_type...,
typename Exclude::base_type...>;
705 using underlying_type =
typename base_type::entity_type;
707 template<
typename Type>
710 auto pools()
const noexcept {
711 using return_type = std::tuple<
Owned *...,
Get *...>;
712 return descriptor ? descriptor->pools_as_tuple() : return_type{};
715 auto filter()
const noexcept {
716 using return_type = std::tuple<
Exclude *...>;
717 return descriptor ? descriptor->filter_as_tuple() : return_type{};
745 : descriptor{&
ref} {}
760 template<
typename Type>
770 template<std::
size_t Index>
772 constexpr auto offset =
sizeof...(Owned) +
sizeof...(
Get);
775 return std::get<Index>(pools());
786 return *
this ? descriptor->length() :
size_type{};
794 return !*
this || !descriptor->length();
805 return *
this ? (
handle().end() - descriptor->length()) :
iterator{};
844 const auto it = begin();
854 const auto it = rbegin();
883 return descriptor !=
nullptr;
902 template<
typename Type,
typename...
Other>
913 template<std::size_t...
Index>
915 const auto cpools = pools();
917 if constexpr(
sizeof...(Index) == 0) {
919 }
else if constexpr(
sizeof...(Index) == 1) {
920 return (std::get<Index>(
cpools)->get(
entt), ...);
922 return std::tuple_cat(std::get<Index>(
cpools)->get_as_tuple(
entt)...);
948 template<
typename Func>
950 for(
auto args: each()) {
951 if constexpr(
is_applicable_v<Func,
decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
952 std::apply(func,
args);
954 std::apply([&func](
auto,
auto &&...
less) { func(std::forward<
decltype(
less)>(
less)...); },
args);
973 const auto cpools = pools();
1031 const auto cpools = pools();
1033 if constexpr(
sizeof...(Index) == 0) {
1034 static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>,
"Invalid comparison function");
1038 if constexpr(
sizeof...(Index) == 1) {
1045 storage<0>()->sort_n(descriptor->length(), std::move(
comp), std::move(
algo), std::forward<Args>(
args)...);
1048 auto cb = [
this](
auto *head,
auto *...other) {
1049 for(
auto next = descriptor->length(); next; --next) {
1050 const auto pos = next - 1;
1060 handler *descriptor;
const common_type & handle() const noexcept
Returns the leading storage of a group.
void each(Func func) const
Iterates entities and components and applies the given function object to them.
decltype(auto) get(const entity_type entt) const
Returns the components 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 component type, if any.
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
Reversed 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 components assigned to the given entity.
reverse_iterator rbegin() const noexcept
Returns an iterator to the first entity of the reversed group.
internal::group_handler< owned_t< std::remove_const_t< Owned >... >, get_t< std::remove_const_t< Get >... >, exclude_t< std::remove_const_t< Exclude >... > > handler
Group handler type.
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.
auto * storage() const noexcept
Returns the storage for a given component type, if any.
typename common_type::reverse_iterator reverse_iterator
Reversed 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 components assigned to the given entity.
bool empty() const noexcept
Checks whether a group is empty.
void sort_as(const common_type &other) const
Sort entities according to their order in a range.
void each(Func func) const
Iterates entities and components and applies the given function object to them.
internal::group_handler< owned_t<>, get_t< std::remove_const_t< Get >... >, exclude_t< std::remove_const_t< Exclude >... > > handler
Group handler type.
decltype(auto) get(const entity_type entt) const
Returns the components 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.
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.
Basic storage implementation.
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.
constexpr get_t< Type... > get
Variable template for lists of observed components.
constexpr owned_t< Type... > owned
Variable template for lists of owned components.
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.
Alias for exclusion lists.
Alias for lists of observed components.
Utility class to create an iterable object from a pair of iterators.
Alias for lists of owned components.
Function object to wrap std::sort in a class type.
static constexpr id_type value() noexcept
Returns the numeric representation of a given type.
A class to use to push around lists of types, nothing more.