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 void swap_elements(
const std::size_t pos,
const entity_type
entt) {
113 std::apply([pos,
entt](
auto *...cpool) { (cpool->swap_elements(cpool->data()[pos],
entt), ...); }, pools);
116 void push_on_construct(
const entity_type
entt) {
117 if(std::apply([
entt, len = len](
auto *cpool,
auto *...other) {
return cpool->contains(
entt) && !(cpool->index(
entt) < len) && (other->contains(
entt) && ...); }, pools)
118 && std::apply([
entt](
auto *...cpool) {
return (!cpool->contains(
entt) && ...); }, filter)) {
119 swap_elements(len++,
entt);
123 void push_on_destroy(
const entity_type
entt) {
124 if(std::apply([
entt, len = len](
auto *cpool,
auto *...other) {
return cpool->contains(
entt) && !(cpool->index(
entt) < len) && (other->contains(
entt) && ...); }, pools)
125 && std::apply([
entt](
auto *...cpool) {
return (0u + ... + cpool->contains(
entt)) == 1u; }, filter)) {
126 swap_elements(len++,
entt);
130 void remove_if(
const entity_type
entt) {
131 if(std::get<0>(pools)->contains(
entt) && (std::get<0>(pools)->index(
entt) < len)) {
132 swap_elements(--len,
entt);
137 using size_type =
typename base_type::size_type;
139 group_handler(Owned &...opool, Get &...gpool, Exclude &...epool)
140 : pools{&opool..., &gpool...},
143 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);
144 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);
147 for(
auto *first = std::get<0>(pools)->data(), *last = first + std::get<0>(pools)->size(); first != last; ++first) {
148 push_on_construct(*first);
152 size_type
owned(
const id_type *elem,
const size_type length)
const noexcept final {
155 for(
auto pos = 0u; pos < length; ++pos) {
162 [[nodiscard]] size_type length() const noexcept {
166 template<
typename Type>
167 Type pools_as() const noexcept {
171 template<
typename Type>
172 Type filter_as() const noexcept {
177 std::tuple<Owned *..., Get *...> pools;
178 std::tuple<Exclude *...> filter;
182template<
typename... Get,
typename... Exclude>
183class group_handler<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> final:
public group_descriptor {
185 static_assert(!std::disjunction_v<std::is_const<Get>..., std::is_const<Exclude>...>,
"Const storage type not allowed");
187 using base_type = std::common_type_t<
typename Get::base_type...,
typename Exclude::base_type...>;
188 using entity_type =
typename base_type::entity_type;
190 void push_on_construct(
const entity_type
entt) {
191 if(!elem.contains(
entt)
192 && std::apply([
entt](
auto *...cpool) { return (cpool->contains(entt) && ...); }, pools)
193 && std::apply([
entt](
auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
198 void push_on_destroy(
const entity_type
entt) {
199 if(!elem.contains(
entt)
200 && std::apply([
entt](
auto *...cpool) { return (cpool->contains(entt) && ...); }, pools)
201 && std::apply([
entt](
auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
206 void remove_if(
const entity_type
entt) {
211 using common_type = base_type;
213 template<
typename Alloc>
214 group_handler(
const Alloc &alloc, Get &...gpool, Exclude &...epool)
218 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);
219 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);
221 for(
const auto entity: static_cast<base_type &>(*std::
get<0>(pools))) {
222 push_on_construct(entity);
226 common_type &
handle() noexcept {
230 const common_type &
handle() const noexcept {
234 template<
typename Type>
235 Type pools_as() const noexcept {
239 template<
typename Type>
240 Type filter_as() const noexcept {
245 std::tuple<Get *...> pools;
246 std::tuple<Exclude *...> filter;
259template<
typename,
typename,
typename>
284template<
typename... Get,
typename... Exclude>
286 using base_type = std::common_type_t<
typename Get::base_type...,
typename Exclude::base_type...>;
287 using underlying_type =
typename base_type::entity_type;
289 template<
typename Type>
290 static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>,
type_list<
typename Get::value_type...,
typename Exclude::value_type...>>;
292 auto pools()
const noexcept {
293 using return_type = std::tuple<Get *...>;
294 return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
297 auto filter()
const noexcept {
298 using return_type = std::tuple<Exclude *...>;
299 return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
327 : descriptor{&
ref} {}
334 return descriptor->handle();
342 template<
typename Type>
343 [[nodiscard]]
auto *
storage() const noexcept {
352 template<std::
size_t Index>
353 [[nodiscard]]
auto *
storage() const noexcept {
354 constexpr auto offset =
sizeof...(Get);
356 if constexpr(Index < offset) {
357 return std::get<Index>(pools());
359 return std::get<Index - offset>(filter());
383 descriptor->handle().shrink_to_fit();
391 [[nodiscard]]
bool empty() const noexcept {
392 return !*
this ||
handle().empty();
442 const auto it = begin();
443 return it != end() ? *it :
null;
452 const auto it = rbegin();
453 return it != rend() ? *it :
null;
479 [[nodiscard]]
explicit operator bool() const noexcept {
480 return descriptor !=
nullptr;
499 template<
typename Type,
typename... Other>
501 return get<index_of<Type>, index_of<Other>...>(
entt);
510 template<std::size_t... Index>
512 const auto cpools = pools();
514 if constexpr(
sizeof...(Index) == 0) {
515 return std::apply([
entt](
auto *...curr) {
return std::tuple_cat(curr->get_as_tuple(
entt)...); }, cpools);
516 }
else if constexpr(
sizeof...(Index) == 1) {
517 return (std::get<Index>(cpools)->get(
entt), ...);
519 return std::tuple_cat(std::get<Index>(cpools)->get_as_tuple(
entt)...);
545 template<
typename Func>
547 for(
const auto entt: *
this) {
548 if constexpr(
is_applicable_v<Func,
decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
549 std::apply(func, std::tuple_cat(std::make_tuple(
entt),
get(
entt)));
570 const auto cpools = pools();
571 return iterable{{begin(), cpools}, {end(), cpools}};
607 template<
typename Type,
typename... Other,
typename Compare,
typename Sort =
std_sort,
typename... Args>
608 void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
609 sort<index_of<Type>, index_of<Other>...>(std::move(compare), std::move(algo), std::forward<Args>(args)...);
625 template<std::size_t... Index,
typename Compare,
typename Sort = std_sort,
typename... Args>
626 void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
628 if constexpr(
sizeof...(Index) == 0) {
629 static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>,
"Invalid comparison function");
630 descriptor->handle().sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
632 auto comp = [&compare, cpools = pools()](
const entity_type lhs,
const entity_type rhs) {
633 if constexpr(
sizeof...(Index) == 1) {
634 return compare((std::get<Index>(cpools)->
get(lhs), ...), (std::get<Index>(cpools)->
get(rhs), ...));
636 return compare(std::forward_as_tuple(std::get<Index>(cpools)->
get(lhs)...), std::forward_as_tuple(std::get<Index>(cpools)->
get(rhs)...));
640 descriptor->handle().sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
655 template<
typename It>
658 descriptor->handle().sort_as(first, last);
667 sort_as(other.begin(), other.end());
705template<
typename... Owned,
typename... Get,
typename... Exclude>
707 using base_type = std::common_type_t<
typename Owned::base_type...,
typename Get::base_type...,
typename Exclude::base_type...>;
708 using underlying_type =
typename base_type::entity_type;
710 template<
typename Type>
711 static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>,
type_list<
typename Owned::value_type...,
typename Get::value_type...,
typename Exclude::value_type...>>;
713 auto pools()
const noexcept {
714 using return_type = std::tuple<Owned *..., Get *...>;
715 return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
718 auto filter()
const noexcept {
719 using return_type = std::tuple<Exclude *...>;
720 return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
748 : descriptor{&
ref} {}
763 template<
typename Type>
764 [[nodiscard]]
auto *
storage() const noexcept {
773 template<std::
size_t Index>
774 [[nodiscard]]
auto *
storage() const noexcept {
775 constexpr auto offset =
sizeof...(Owned) +
sizeof...(Get);
777 if constexpr(Index < offset) {
778 return std::get<Index>(pools());
780 return std::get<Index - offset>(filter());
789 return *
this ? descriptor->length() :
size_type{};
796 [[nodiscard]]
bool empty() const noexcept {
797 return !*
this || !descriptor->length();
808 return *
this ? (
handle().end() - descriptor->length()) :
iterator{};
847 const auto it = begin();
848 return it != end() ? *it :
null;
857 const auto it = rbegin();
858 return it != rend() ? *it :
null;
869 return it >= begin() ? it :
iterator{};
885 [[nodiscard]]
explicit operator bool() const noexcept {
886 return descriptor !=
nullptr;
905 template<
typename Type,
typename... Other>
907 return get<index_of<Type>, index_of<Other>...>(
entt);
916 template<std::size_t... Index>
918 const auto cpools = pools();
920 if constexpr(
sizeof...(Index) == 0) {
921 return std::apply([
entt](
auto *...curr) {
return std::tuple_cat(curr->get_as_tuple(
entt)...); }, cpools);
922 }
else if constexpr(
sizeof...(Index) == 1) {
923 return (std::get<Index>(cpools)->get(
entt), ...);
925 return std::tuple_cat(std::get<Index>(cpools)->get_as_tuple(
entt)...);
951 template<
typename Func>
953 for(
auto args: each()) {
954 if constexpr(
is_applicable_v<Func,
decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_group>().get({})))>) {
955 std::apply(func, args);
957 std::apply([&func](
auto,
auto &&...less) { func(std::forward<
decltype(less)>(less)...); }, args);
976 const auto cpools = pools();
977 return {{begin(), cpools}, {end(), cpools}};
1014 template<
typename Type,
typename... Other,
typename Compare,
typename Sort =
std_sort,
typename... Args>
1015 void sort(Compare compare, Sort algo = Sort{}, Args &&...args)
const {
1016 sort<index_of<Type>, index_of<Other>...>(std::move(compare), std::move(algo), std::forward<Args>(args)...);
1032 template<std::size_t... Index,
typename Compare,
typename Sort = std_sort,
typename... Args>
1033 void sort(Compare compare, Sort algo = Sort{}, Args &&...args)
const {
1034 const auto cpools = pools();
1036 if constexpr(
sizeof...(Index) == 0) {
1037 static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>,
"Invalid comparison function");
1038 storage<0>()->
sort_n(descriptor->length(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
1040 auto comp = [&compare, &cpools](
const entity_type lhs,
const entity_type rhs) {
1041 if constexpr(
sizeof...(Index) == 1) {
1042 return compare((std::get<Index>(cpools)->
get(lhs), ...), (std::get<Index>(cpools)->
get(rhs), ...));
1044 return compare(std::forward_as_tuple(std::get<Index>(cpools)->
get(lhs)...), std::forward_as_tuple(std::get<Index>(cpools)->
get(rhs)...));
1048 storage<0>()->sort_n(descriptor->length(), std::move(comp), std::move(algo), std::forward<Args>(args)...);
1051 auto cb = [
this](
auto *head,
auto *...other) {
1052 for(
auto next = descriptor->length(); next; --next) {
1053 const auto pos = next - 1;
1054 [[maybe_unused]]
const auto entt = head->data()[pos];
1055 (other->swap_elements(other->data()[pos],
entt), ...);
1059 std::apply(cb, cpools);
1063 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.
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.
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.
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.
internal::group_handler< owned_t<>, get_t< std::remove_const_t< Get >... >, exclude_t< std::remove_const_t< Exclude >... > > handler
Group handler type.
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.
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.
void sort_n(const size_type length, Compare compare, Sort algo=Sort{}, Args &&...args)
Sort the first count elements according to the given comparison function.
Basic storage implementation.
basic_handle< registry > handle
Alias declaration for the most common use case.
constexpr null_t null
Compile-time constant for null entities.
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.