EnTT  3.8.0
sparse_set.hpp
1 #ifndef ENTT_ENTITY_SPARSE_SET_HPP
2 #define ENTT_ENTITY_SPARSE_SET_HPP
3 
4 
5 #include <cstddef>
6 #include <iterator>
7 #include <memory>
8 #include <type_traits>
9 #include <utility>
10 #include "../config/config.h"
11 #include "../core/algorithm.hpp"
12 #include "../core/fwd.hpp"
13 #include "entity.hpp"
14 #include "fwd.hpp"
15 
16 
17 namespace entt {
18 
19 
21 enum class deletion_policy: std::uint8_t {
23  swap_and_pop = 0u,
25  in_place = 1u
26 };
27 
28 
52 template<typename Entity, typename Allocator>
54  static constexpr auto growth_factor = 1.5;
55  static constexpr auto sparse_page = ENTT_SPARSE_PAGE;
56 
58 
59  using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Entity>;
60  using alloc_pointer = typename alloc_traits::pointer;
61  using alloc_const_pointer = typename alloc_traits::const_pointer;
62 
63  using bucket_alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<alloc_pointer>;
64  using bucket_alloc_pointer = typename bucket_alloc_traits::pointer;
65 
66  static_assert(alloc_traits::propagate_on_container_move_assignment::value);
67  static_assert(bucket_alloc_traits::propagate_on_container_move_assignment::value);
68 
69  struct sparse_set_iterator final {
70  using difference_type = typename traits_type::difference_type;
71  using value_type = Entity;
72  using pointer = const value_type *;
73  using reference = const value_type &;
74  using iterator_category = std::random_access_iterator_tag;
75 
76  sparse_set_iterator() ENTT_NOEXCEPT = default;
77 
78  sparse_set_iterator(const alloc_const_pointer *ref, const difference_type idx) ENTT_NOEXCEPT
79  : packed{ref},
80  index{idx}
81  {}
82 
83  sparse_set_iterator & operator++() ENTT_NOEXCEPT {
84  return --index, *this;
85  }
86 
87  sparse_set_iterator operator++(int) ENTT_NOEXCEPT {
88  iterator orig = *this;
89  return ++(*this), orig;
90  }
91 
92  sparse_set_iterator & operator--() ENTT_NOEXCEPT {
93  return ++index, *this;
94  }
95 
96  sparse_set_iterator operator--(int) ENTT_NOEXCEPT {
97  sparse_set_iterator orig = *this;
98  return operator--(), orig;
99  }
100 
101  sparse_set_iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
102  index -= value;
103  return *this;
104  }
105 
106  sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
107  sparse_set_iterator copy = *this;
108  return (copy += value);
109  }
110 
111  sparse_set_iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
112  return (*this += -value);
113  }
114 
115  sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
116  return (*this + -value);
117  }
118 
119  difference_type operator-(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
120  return other.index - index;
121  }
122 
123  [[nodiscard]] reference operator[](const difference_type value) const {
124  const auto pos = size_type(index-value-1u);
125  return (*packed)[pos];
126  }
127 
128  [[nodiscard]] bool operator==(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
129  return other.index == index;
130  }
131 
132  [[nodiscard]] bool operator!=(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
133  return !(*this == other);
134  }
135 
136  [[nodiscard]] bool operator<(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
137  return index > other.index;
138  }
139 
140  [[nodiscard]] bool operator>(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
141  return index < other.index;
142  }
143 
144  [[nodiscard]] bool operator<=(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
145  return !(*this > other);
146  }
147 
148  [[nodiscard]] bool operator>=(const sparse_set_iterator &other) const ENTT_NOEXCEPT {
149  return !(*this < other);
150  }
151 
152  [[nodiscard]] pointer operator->() const {
153  const auto pos = size_type(index-1u);
154  return std::addressof((*packed)[pos]);
155  }
156 
157  [[nodiscard]] reference operator*() const {
158  return *operator->();
159  }
160 
161  private:
162  const alloc_const_pointer *packed;
163  difference_type index;
164  };
165 
166  [[nodiscard]] static auto page(const Entity entt) ENTT_NOEXCEPT {
167  return size_type{traits_type::to_entity(entt) / sparse_page};
168  }
169 
170  [[nodiscard]] static auto offset(const Entity entt) ENTT_NOEXCEPT {
171  return size_type{traits_type::to_entity(entt) & (sparse_page - 1)};
172  }
173 
174  [[nodiscard]] auto assure_page(const std::size_t idx) {
175  if(!(idx < bucket)) {
176  const size_type sz = idx + 1u;
177  const auto mem = bucket_alloc_traits::allocate(bucket_allocator, sz);
178 
179  std::uninitialized_value_construct(mem + bucket, mem + sz);
180  std::uninitialized_copy(sparse, sparse + bucket, mem);
181 
182  std::destroy(sparse, sparse + bucket);
183  bucket_alloc_traits::deallocate(bucket_allocator, sparse, bucket);
184 
185  sparse = mem;
186  bucket = sz;
187  }
188 
189  if(!sparse[idx]) {
190  sparse[idx] = alloc_traits::allocate(allocator, sparse_page);
191  std::uninitialized_fill(sparse[idx], sparse[idx] + sparse_page, null);
192  }
193 
194  return sparse[idx];
195  }
196 
197  void resize_packed(const std::size_t req) {
198  ENTT_ASSERT((req != reserved) && !(req < count), "Invalid request");
199  const auto mem = alloc_traits::allocate(allocator, req);
200 
201  std::uninitialized_copy(packed, packed + count, mem);
202  std::uninitialized_fill(mem + count, mem + req, tombstone);
203 
204  std::destroy(packed, packed + reserved);
205  alloc_traits::deallocate(allocator, packed, reserved);
206 
207  packed = mem;
208  reserved = req;
209  }
210 
211  void release_memory() {
212  if(packed) {
213  for(size_type pos{}; pos < bucket; ++pos) {
214  if(sparse[pos]) {
215  std::destroy(sparse[pos], sparse[pos] + sparse_page);
216  alloc_traits::deallocate(allocator, sparse[pos], sparse_page);
217  }
218  }
219 
220  std::destroy(packed, packed + reserved);
221  std::destroy(sparse, sparse + bucket);
222  alloc_traits::deallocate(allocator, packed, reserved);
223  bucket_alloc_traits::deallocate(bucket_allocator, sparse, bucket);
224  }
225  }
226 
227 protected:
233  virtual void swap_at([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) {}
234 
240  virtual void move_and_pop([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) {}
241 
247  virtual void swap_and_pop(const Entity entt, [[maybe_unused]] void *ud) {
248  auto &ref = sparse[page(entt)][offset(entt)];
249  const auto pos = size_type{traits_type::to_entity(ref)};
250  ENTT_ASSERT(packed[pos] == entt, "Invalid entity identifier");
251  auto &last = packed[--count];
252 
253  packed[pos] = last;
254  sparse[page(last)][offset(last)] = ref;
255  // lazy self-assignment guard
256  ref = null;
257  // unnecessary but it helps to detect nasty bugs
258  ENTT_ASSERT((last = tombstone, true), "");
259  }
260 
266  virtual void in_place_pop(const Entity entt, [[maybe_unused]] void *ud) {
267  auto &ref = sparse[page(entt)][offset(entt)];
268  const auto pos = size_type{traits_type::to_entity(ref)};
269  ENTT_ASSERT(packed[pos] == entt, "Invalid entity identifier");
270 
271  packed[pos] = std::exchange(free_list, traits_type::construct(static_cast<typename traits_type::entity_type>(pos)));
272  // lazy self-assignment guard
273  ref = null;
274  }
275 
276 public:
278  using allocator_type = typename alloc_traits::allocator_type;
280  using entity_type = Entity;
282  using size_type = std::size_t;
284  using pointer = alloc_const_pointer;
286  using iterator = sparse_set_iterator;
288  using reverse_iterator = std::reverse_iterator<iterator>;
289 
295  explicit basic_sparse_set(deletion_policy pol, const allocator_type &alloc = {})
296  : allocator{alloc},
297  bucket_allocator{alloc},
298  sparse{bucket_alloc_traits::allocate(bucket_allocator, 0u)},
299  packed{alloc_traits::allocate(allocator, 0u)},
300  bucket{0u},
301  count{0u},
302  reserved{0u},
303  free_list{tombstone},
304  mode{pol}
305  {}
306 
311  explicit basic_sparse_set(const allocator_type &alloc = {})
313  {}
314 
319  basic_sparse_set(basic_sparse_set &&other) ENTT_NOEXCEPT
320  : allocator{std::move(other.allocator)},
321  bucket_allocator{std::move(other.bucket_allocator)},
322  sparse{std::exchange(other.sparse, bucket_alloc_pointer{})},
323  packed{std::exchange(other.packed, alloc_pointer{})},
324  bucket{std::exchange(other.bucket, 0u)},
325  count{std::exchange(other.count, 0u)},
326  reserved{std::exchange(other.reserved, 0u)},
327  free_list{std::exchange(other.free_list, tombstone)},
328  mode{other.mode}
329  {}
330 
332  virtual ~basic_sparse_set() {
333  release_memory();
334  }
335 
341  basic_sparse_set & operator=(basic_sparse_set &&other) ENTT_NOEXCEPT {
342  release_memory();
343 
344  allocator = std::move(other.allocator);
345  bucket_allocator = std::move(other.bucket_allocator);
346  sparse = std::exchange(other.sparse, bucket_alloc_pointer{});
347  packed = std::exchange(other.packed, alloc_pointer{});
348  bucket = std::exchange(other.bucket, 0u);
349  count = std::exchange(other.count, 0u);
350  reserved = std::exchange(other.reserved, 0u);
351  free_list = std::exchange(other.free_list, tombstone);
352  mode = other.mode;
353 
354  return *this;
355  }
356 
361  [[nodiscard]] deletion_policy policy() const ENTT_NOEXCEPT {
362  return mode;
363  }
364 
369  [[nodiscard]] size_type slot() const ENTT_NOEXCEPT {
370  return free_list == null ? count : size_type{traits_type::to_entity(free_list)};
371  }
372 
381  void reserve(const size_type cap) {
382  if(cap > reserved) {
383  resize_packed(cap);
384  }
385  }
386 
392  [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT {
393  return reserved;
394  }
395 
397  void shrink_to_fit() {
398  if(count < reserved) {
399  resize_packed(count);
400  }
401  }
402 
413  [[nodiscard]] size_type extent() const ENTT_NOEXCEPT {
414  return bucket * sparse_page;
415  }
416 
427  [[nodiscard]] size_type size() const ENTT_NOEXCEPT {
428  return count;
429  }
430 
435  [[nodiscard]] bool empty() const ENTT_NOEXCEPT {
436  return (count == size_type{});
437  }
438 
443  [[nodiscard]] pointer data() const ENTT_NOEXCEPT {
444  return packed;
445  }
446 
456  [[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
457  return iterator{std::addressof(packed), static_cast<typename traits_type::difference_type>(count)};
458  }
459 
470  [[nodiscard]] iterator end() const ENTT_NOEXCEPT {
471  return iterator{std::addressof(packed), {}};
472  }
473 
484  [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
485  return std::make_reverse_iterator(end());
486  }
487 
498  [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
499  return std::make_reverse_iterator(begin());
500  }
501 
508  [[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT {
509  return contains(entt) ? --(end() - index(entt)) : end();
510  }
511 
517  [[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT {
518  ENTT_ASSERT(entt != tombstone && entt != null, "Invalid entity");
519  const auto curr = page(entt);
520  // testing versions permits to avoid accessing the packed array
521  return (curr < bucket && sparse[curr] && sparse[curr][offset(entt)] != null);
522  }
523 
534  [[nodiscard]] size_type index(const entity_type entt) const ENTT_NOEXCEPT {
535  ENTT_ASSERT(contains(entt), "Set does not contain entity");
536  return size_type{traits_type::to_entity(sparse[page(entt)][offset(entt)])};
537  }
538 
544  [[nodiscard]] entity_type at(const size_type pos) const ENTT_NOEXCEPT {
545  return pos < count ? packed[pos] : null;
546  }
547 
553  [[nodiscard]] entity_type operator[](const size_type pos) const ENTT_NOEXCEPT {
554  ENTT_ASSERT(pos < count, "Position is out of bounds");
555  return packed[pos];
556  }
557 
569  ENTT_ASSERT(!contains(entt), "Set already contains entity");
570 
571  if(count == reserved) {
572  const size_type sz = static_cast<size_type>(reserved * growth_factor);
573  resize_packed(sz + !(sz > reserved));
574  }
575 
576  assure_page(page(entt))[offset(entt)] = traits_type::construct(static_cast<typename traits_type::entity_type>(count));
577  packed[count] = entt;
578  return count++;
579  }
580 
592  if(free_list == null) {
593  return emplace_back(entt);
594  } else {
595  ENTT_ASSERT(!contains(entt), "Set already contains entity");
596  const auto pos = size_type{traits_type::to_entity(free_list)};
597  sparse[page(entt)][offset(entt)] = traits_type::construct(static_cast<typename traits_type::entity_type>(pos));
598  free_list = std::exchange(packed[pos], entt);
599  return pos;
600  }
601  }
602 
614  template<typename It>
615  void insert(It first, It last) {
616  reserve(count + std::distance(first, last));
617 
618  for(; first != last; ++first) {
619  const auto entt = *first;
620  ENTT_ASSERT(!contains(entt), "Set already contains entity");
621  assure_page(page(entt))[offset(entt)] = traits_type::construct(static_cast<typename traits_type::entity_type>(count));
622  packed[count++] = entt;
623  }
624  }
625 
636  void erase(const entity_type entt, void *ud = nullptr) {
637  ENTT_ASSERT(contains(entt), "Set does not contain entity");
639  }
640 
651  template<typename It>
652  void erase(It first, It last, void *ud = nullptr) {
653  for(; first != last; ++first) {
654  erase(*first, ud);
655  }
656  }
657 
664  bool remove(const entity_type entt, void *ud = nullptr) {
665  return contains(entt) && (erase(entt, ud), true);
666  }
667 
676  template<typename It>
677  size_type remove(It first, It last, void *ud = nullptr) {
678  size_type found{};
679 
680  for(; first != last; ++first) {
681  found += remove(*first, ud);
682  }
683 
684  return found;
685  }
686 
688  void compact() {
689  size_type next = count;
690  for(; next && packed[next - 1u] == tombstone; --next);
691 
692  for(auto *it = &free_list; *it != null && next; it = std::addressof(packed[traits_type::to_entity(*it)])) {
693  if(const size_type pos = traits_type::to_entity(*it); pos < next) {
694  --next;
695  move_and_pop(next, pos);
696  std::swap(packed[next], packed[pos]);
697  sparse[page(packed[pos])][offset(packed[pos])] = traits_type::construct(static_cast<const typename traits_type::entity_type>(pos));
698  *it = traits_type::construct(static_cast<typename traits_type::entity_type>(next));
699  for(; next && packed[next - 1u] == tombstone; --next);
700  }
701  }
702 
703  free_list = tombstone;
704  count = next;
705  }
706 
720  void swap(const entity_type lhs, const entity_type rhs) {
721  ENTT_ASSERT(contains(lhs), "Set does not contain entity");
722  ENTT_ASSERT(contains(rhs), "Set does not contain entity");
723 
724  auto &entt = sparse[page(lhs)][offset(lhs)];
725  auto &other = sparse[page(rhs)][offset(rhs)];
726 
727  const auto from = size_type{traits_type::to_entity(entt)};
728  const auto to = size_type{traits_type::to_entity(other)};
729 
730  // basic no-leak guarantee (with invalid state) if swapping throws
731  swap_at(from, to);
732  std::swap(entt, other);
733  std::swap(packed[from], packed[to]);
734  }
735 
766  template<typename Compare, typename Sort = std_sort, typename... Args>
767  void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&... args) {
768  // basic no-leak guarantee (with invalid state) if sorting throws
769  ENTT_ASSERT(!(length > count), "Length exceeds the number of elements");
770  compact();
771 
772  algo(std::make_reverse_iterator(packed + length), std::make_reverse_iterator(packed), std::move(compare), std::forward<Args>(args)...);
773 
774  for(size_type pos{}; pos < length; ++pos) {
775  auto curr = pos;
776  auto next = index(packed[curr]);
777 
778  while(curr != next) {
779  const auto idx = index(packed[next]);
780  const auto entt = packed[curr];
781 
782  swap_at(next, idx);
783  sparse[page(entt)][offset(entt)] = traits_type::construct(static_cast<typename traits_type::entity_type>(curr));
784  curr = std::exchange(next, idx);
785  }
786  }
787  }
788 
801  template<typename Compare, typename Sort = std_sort, typename... Args>
802  void sort(Compare compare, Sort algo = Sort{}, Args &&... args) {
803  sort_n(count, std::move(compare), std::move(algo), std::forward<Args>(args)...);
804  }
805 
821  void respect(const basic_sparse_set &other) {
822  compact();
823 
824  const auto to = other.end();
825  auto from = other.begin();
826 
827  for(size_type pos = count - 1; pos && from != to; ++from) {
828  if(contains(*from)) {
829  if(*from != packed[pos]) {
830  // basic no-leak guarantee (with invalid state) if swapping throws
831  swap(packed[pos], *from);
832  }
833 
834  --pos;
835  }
836  }
837  }
838 
843  void clear(void *ud = nullptr) {
844  for(auto &&entity: *this) {
845  if(entity != tombstone) {
846  in_place_pop(entity, ud);
847  }
848  }
849 
850  free_list = tombstone;
851  count = 0u;
852  }
853 
854 private:
855  typename alloc_traits::allocator_type allocator;
856  typename bucket_alloc_traits::allocator_type bucket_allocator;
857  bucket_alloc_pointer sparse;
858  alloc_pointer packed;
859  std::size_t bucket;
860  std::size_t count;
861  std::size_t reserved;
862  entity_type free_list;
863  deletion_policy mode;
864 };
865 
866 
867 }
868 
869 
870 #endif
Basic sparse set implementation.
Definition: sparse_set.hpp:53
virtual void swap_and_pop(const Entity entt, [[maybe_unused]] void *ud)
Attempts to erase an entity from the internal packed array.
Definition: sparse_set.hpp:247
size_type remove(It first, It last, void *ud=nullptr)
Removes entities from a sparse set if they exist.
Definition: sparse_set.hpp:677
deletion_policy policy() const
Returns the deletion policy of a sparse set.
Definition: sparse_set.hpp:361
sparse_set_iterator iterator
Random access iterator type.
Definition: sparse_set.hpp:286
void reserve(const size_type cap)
Increases the capacity of a sparse set.
Definition: sparse_set.hpp:381
iterator begin() const
Returns an iterator to the beginning.
Definition: sparse_set.hpp:456
virtual void move_and_pop([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to)
Moves an entity in the internal packed array.
Definition: sparse_set.hpp:240
void clear(void *ud=nullptr)
Clears a sparse set.
Definition: sparse_set.hpp:843
void insert(It first, It last)
Assigns one or more entities to a sparse set.
Definition: sparse_set.hpp:615
size_type index(const entity_type entt) const
Returns the position of an entity in a sparse set.
Definition: sparse_set.hpp:534
virtual void in_place_pop(const Entity entt, [[maybe_unused]] void *ud)
Attempts to erase an entity from the internal packed array.
Definition: sparse_set.hpp:266
size_type capacity() const
Returns the number of elements that a sparse set has currently allocated space for.
Definition: sparse_set.hpp:392
reverse_iterator rend() const
Returns a reverse iterator to the end.
Definition: sparse_set.hpp:498
void shrink_to_fit()
Requests the removal of unused capacity.
Definition: sparse_set.hpp:397
size_type emplace(const entity_type entt)
Assigns an entity to a sparse set.
Definition: sparse_set.hpp:591
std::size_t size_type
Unsigned integer type.
Definition: sparse_set.hpp:282
alloc_const_pointer pointer
Pointer type to contained entities.
Definition: sparse_set.hpp:284
iterator end() const
Returns an iterator to the end.
Definition: sparse_set.hpp:470
bool empty() const
Checks whether a sparse set is empty.
Definition: sparse_set.hpp:435
size_type slot() const
Returns the next slot available for insertion.
Definition: sparse_set.hpp:369
entity_type operator[](const size_type pos) const
Returns the entity at specified location, without bounds checking.
Definition: sparse_set.hpp:553
basic_sparse_set(deletion_policy pol, const allocator_type &alloc={})
Constructs an empty container with the given policy and allocator.
Definition: sparse_set.hpp:295
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.
Definition: sparse_set.hpp:767
basic_sparse_set(const allocator_type &alloc={})
Constructs an empty container with the given allocator.
Definition: sparse_set.hpp:311
size_type size() const
Returns the number of elements in a sparse set.
Definition: sparse_set.hpp:427
reverse_iterator rbegin() const
Returns a reverse iterator to the beginning.
Definition: sparse_set.hpp:484
iterator find(const entity_type entt) const
Finds an entity.
Definition: sparse_set.hpp:508
void swap(const entity_type lhs, const entity_type rhs)
Swaps two entities in the internal packed array.
Definition: sparse_set.hpp:720
Entity entity_type
Underlying entity identifier.
Definition: sparse_set.hpp:280
bool remove(const entity_type entt, void *ud=nullptr)
Removes an entity from a sparse set if it exists.
Definition: sparse_set.hpp:664
size_type extent() const
Returns the extent of a sparse set.
Definition: sparse_set.hpp:413
entity_type at(const size_type pos) const
Returns the entity at specified location, with bounds checking.
Definition: sparse_set.hpp:544
basic_sparse_set(basic_sparse_set &&other)
Move constructor.
Definition: sparse_set.hpp:319
size_type emplace_back(const entity_type entt)
Appends an entity to a sparse set.
Definition: sparse_set.hpp:568
void sort(Compare compare, Sort algo=Sort{}, Args &&... args)
Sort all elements according to the given comparison function.
Definition: sparse_set.hpp:802
void erase(It first, It last, void *ud=nullptr)
Erases entities from a set.
Definition: sparse_set.hpp:652
virtual ~basic_sparse_set()
Default destructor.
Definition: sparse_set.hpp:332
bool contains(const entity_type entt) const
Checks if a sparse set contains an entity.
Definition: sparse_set.hpp:517
void respect(const basic_sparse_set &other)
Sort entities according to their order in another sparse set.
Definition: sparse_set.hpp:821
pointer data() const
Direct access to the internal packed array.
Definition: sparse_set.hpp:443
void compact()
Removes all tombstones from the packed array of a sparse set.
Definition: sparse_set.hpp:688
void erase(const entity_type entt, void *ud=nullptr)
Erases an entity from a sparse set.
Definition: sparse_set.hpp:636
virtual void swap_at([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs)
Swaps two entities in the internal packed array.
Definition: sparse_set.hpp:233
basic_sparse_set & operator=(basic_sparse_set &&other)
Move assignment operator.
Definition: sparse_set.hpp:341
std::reverse_iterator< iterator > reverse_iterator
Reverse iterator type.
Definition: sparse_set.hpp:288
typename alloc_traits::allocator_type allocator_type
Allocator type.
Definition: sparse_set.hpp:278
Entity traits.
Definition: entity.hpp:77
static constexpr entity_type to_entity(const value_type value)
Returns the entity part once converted to the underlying type.
Definition: entity.hpp:104
typename traits_type::entity_type entity_type
Underlying entity type.
Definition: entity.hpp:84
static constexpr value_type construct(const entity_type entity=traits_type::entity_mask, const version_type version=traits_type::version_mask)
Constructs an identifier from its parts.
Definition: entity.hpp:128
typename traits_type::difference_type difference_type
Difference type.
Definition: entity.hpp:88
EnTT default namespace.
Definition: algorithm.hpp:13
entity
Default entity identifier.
Definition: fwd.hpp:61
constexpr tombstone_t tombstone
Compile-time constant for tombstone entities.
Definition: entity.hpp:345
constexpr type_list< Type..., Other... > operator+(type_list< Type... >, type_list< Other... >)
Concatenates multiple type lists.
deletion_policy
Sparse set deletion policy.
Definition: sparse_set.hpp:21
@ swap_and_pop
Swap-and-pop deletion policy.
@ in_place
In-place deletion policy.
bool operator!=(const basic_any< Len, Align > &lhs, const basic_any< Len, Align > &rhs)
Checks if two wrappers differ in their content.
Definition: any.hpp:368
constexpr bool operator==(const Entity entity, const null_t other)
Compares a null object and an entity identifier of any type.
Definition: entity.hpp:219
Function object to wrap std::sort in a class type.
Definition: algorithm.hpp:24