EnTT  3.10.0
registry.hpp
1 #ifndef ENTT_ENTITY_REGISTRY_HPP
2 #define ENTT_ENTITY_REGISTRY_HPP
3 
4 #include <algorithm>
5 #include <cstddef>
6 #include <iterator>
7 #include <memory>
8 #include <tuple>
9 #include <type_traits>
10 #include <utility>
11 #include <vector>
12 #include "../config/config.h"
13 #include "../container/dense_map.hpp"
14 #include "../core/algorithm.hpp"
15 #include "../core/any.hpp"
16 #include "../core/fwd.hpp"
17 #include "../core/iterator.hpp"
18 #include "../core/type_info.hpp"
19 #include "../core/type_traits.hpp"
20 #include "../core/utility.hpp"
21 #include "component.hpp"
22 #include "entity.hpp"
23 #include "fwd.hpp"
24 #include "group.hpp"
25 #include "runtime_view.hpp"
26 #include "sparse_set.hpp"
27 #include "storage.hpp"
28 #include "utility.hpp"
29 #include "view.hpp"
30 
31 namespace entt {
32 
38 namespace internal {
39 
40 template<typename It>
41 class storage_proxy_iterator final {
42  template<typename Other>
43  friend class storage_proxy_iterator;
44 
45  using mapped_type = std::remove_reference_t<decltype(std::declval<It>()->second)>;
46 
47 public:
48  using value_type = std::pair<id_type, constness_as_t<typename mapped_type::element_type, mapped_type> &>;
49  using pointer = input_iterator_pointer<value_type>;
50  using reference = value_type;
51  using difference_type = std::ptrdiff_t;
52  using iterator_category = std::input_iterator_tag;
53 
54  storage_proxy_iterator() ENTT_NOEXCEPT
55  : it{} {}
56 
57  storage_proxy_iterator(const It iter) ENTT_NOEXCEPT
58  : it{iter} {}
59 
60  template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
61  storage_proxy_iterator(const storage_proxy_iterator<Other> &other) ENTT_NOEXCEPT
62  : it{other.it} {}
63 
64  storage_proxy_iterator &operator++() ENTT_NOEXCEPT {
65  return ++it, *this;
66  }
67 
68  storage_proxy_iterator operator++(int) ENTT_NOEXCEPT {
69  storage_proxy_iterator orig = *this;
70  return ++(*this), orig;
71  }
72 
73  storage_proxy_iterator &operator--() ENTT_NOEXCEPT {
74  return --it, *this;
75  }
76 
77  storage_proxy_iterator operator--(int) ENTT_NOEXCEPT {
78  storage_proxy_iterator orig = *this;
79  return operator--(), orig;
80  }
81 
82  storage_proxy_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
83  it += value;
84  return *this;
85  }
86 
87  storage_proxy_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
88  storage_proxy_iterator copy = *this;
89  return (copy += value);
90  }
91 
92  storage_proxy_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
93  return (*this += -value);
94  }
95 
96  storage_proxy_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
97  return (*this + -value);
98  }
99 
100  [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
101  return {it[value].first, *it[value].second};
102  }
103 
104  [[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
105  return {it->first, *it->second};
106  }
107 
108  [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
109  return operator*();
110  }
111 
112  template<typename ILhs, typename IRhs>
113  friend std::ptrdiff_t operator-(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT;
114 
115  template<typename ILhs, typename IRhs>
116  friend bool operator==(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT;
117 
118  template<typename ILhs, typename IRhs>
119  friend bool operator<(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT;
120 
121 private:
122  It it;
123 };
124 
125 template<typename ILhs, typename IRhs>
126 [[nodiscard]] std::ptrdiff_t operator-(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
127  return lhs.it - rhs.it;
128 }
129 
130 template<typename ILhs, typename IRhs>
131 [[nodiscard]] bool operator==(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
132  return lhs.it == rhs.it;
133 }
134 
135 template<typename ILhs, typename IRhs>
136 [[nodiscard]] bool operator!=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
137  return !(lhs == rhs);
138 }
139 
140 template<typename ILhs, typename IRhs>
141 [[nodiscard]] bool operator<(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
142  return lhs.it < rhs.it;
143 }
144 
145 template<typename ILhs, typename IRhs>
146 [[nodiscard]] bool operator>(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
147  return rhs < lhs;
148 }
149 
150 template<typename ILhs, typename IRhs>
151 [[nodiscard]] bool operator<=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
152  return !(lhs > rhs);
153 }
154 
155 template<typename ILhs, typename IRhs>
156 [[nodiscard]] bool operator>=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
157  return !(lhs < rhs);
158 }
159 
160 struct registry_context {
161  template<typename Type, typename... Args>
162  Type &emplace_hint(const id_type id, Args &&...args) {
163  return any_cast<Type &>(data.try_emplace(id, std::in_place_type<Type>, std::forward<Args>(args)...).first->second);
164  }
165 
166  template<typename Type, typename... Args>
167  Type &emplace(Args &&...args) {
168  return emplace_hint<Type>(type_id<Type>().hash(), std::forward<Args>(args)...);
169  }
170 
171  template<typename Type>
172  bool erase(const id_type id = type_id<Type>().hash()) {
173  const auto it = data.find(id);
174  return it != data.end() && it->second.type() == type_id<Type>() ? (data.erase(it), true) : false;
175  }
176 
177  template<typename Type>
178  [[nodiscard]] std::add_const_t<Type> &at(const id_type id = type_id<Type>().hash()) const {
179  return any_cast<std::add_const_t<Type> &>(data.at(id));
180  }
181 
182  template<typename Type>
183  [[nodiscard]] Type &at(const id_type id = type_id<Type>().hash()) {
184  return any_cast<Type &>(data.at(id));
185  }
186 
187  template<typename Type>
188  [[nodiscard]] std::add_const_t<Type> *find(const id_type id = type_id<Type>().hash()) const {
189  const auto it = data.find(id);
190  return it != data.cend() ? any_cast<std::add_const_t<Type>>(&it->second) : nullptr;
191  }
192 
193  template<typename Type>
194  [[nodiscard]] Type *find(const id_type id = type_id<Type>().hash()) {
195  const auto it = data.find(id);
196  return it != data.end() ? any_cast<Type>(&it->second) : nullptr;
197  }
198 
199  template<typename Type>
200  [[nodiscard]] bool contains(const id_type id = type_id<Type>().hash()) const {
201  const auto it = data.find(id);
202  return it != data.end() && it->second.type() == type_id<Type>();
203  }
204 
205 private:
206  dense_map<id_type, basic_any<0u>, identity> data;
207 };
208 
209 } // namespace internal
210 
220 template<typename Entity>
224 
225  template<typename Component>
226  using storage_type = typename storage_traits<Entity, Component>::storage_type;
227 
228  template<typename...>
229  struct group_handler;
230 
231  template<typename... Exclude, typename... Get, typename... Owned>
232  struct group_handler<exclude_t<Exclude...>, get_t<Get...>, Owned...> {
233  // nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here
234  static_assert(!std::disjunction_v<std::bool_constant<component_traits<Owned>::in_place_delete>...>, "Groups do not support in-place delete");
235  std::conditional_t<sizeof...(Owned) == 0, basic_common_type, std::size_t> current{};
236 
237  template<typename Component>
238  void maybe_valid_if(basic_registry &owner, const Entity entt) {
239  [[maybe_unused]] const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...);
240 
241  const auto is_valid = ((std::is_same_v<Component, Owned> || std::get<storage_type<Owned> &>(cpools).contains(entt)) && ...)
242  && ((std::is_same_v<Component, Get> || owner.assure<Get>().contains(entt)) && ...)
243  && ((std::is_same_v<Component, Exclude> || !owner.assure<Exclude>().contains(entt)) && ...);
244 
245  if constexpr(sizeof...(Owned) == 0) {
246  if(is_valid && !current.contains(entt)) {
247  current.emplace(entt);
248  }
249  } else {
250  if(is_valid && !(std::get<0>(cpools).index(entt) < current)) {
251  const auto pos = current++;
252  (std::get<storage_type<Owned> &>(cpools).swap_elements(std::get<storage_type<Owned> &>(cpools).data()[pos], entt), ...);
253  }
254  }
255  }
256 
257  void discard_if([[maybe_unused]] basic_registry &owner, const Entity entt) {
258  if constexpr(sizeof...(Owned) == 0) {
259  current.remove(entt);
260  } else {
261  if(const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...); std::get<0>(cpools).contains(entt) && (std::get<0>(cpools).index(entt) < current)) {
262  const auto pos = --current;
263  (std::get<storage_type<Owned> &>(cpools).swap_elements(std::get<storage_type<Owned> &>(cpools).data()[pos], entt), ...);
264  }
265  }
266  }
267  };
268 
269  struct group_data {
270  std::size_t size;
271  std::unique_ptr<void, void (*)(void *)> group;
272  bool (*owned)(const id_type) ENTT_NOEXCEPT;
273  bool (*get)(const id_type) ENTT_NOEXCEPT;
274  bool (*exclude)(const id_type) ENTT_NOEXCEPT;
275  };
276 
277  template<typename Component>
278  [[nodiscard]] auto &assure(const id_type id = type_hash<Component>::value()) {
279  static_assert(std::is_same_v<Component, std::decay_t<Component>>, "Non-decayed types not allowed");
280  auto &&cpool = pools[id];
281 
282  if(!cpool) {
283  cpool.reset(new storage_type<Component>{});
284  cpool->bind(forward_as_any(*this));
285  }
286 
287  ENTT_ASSERT(cpool->type() == type_id<Component>(), "Unexpected type");
288  return static_cast<storage_type<Component> &>(*cpool);
289  }
290 
291  template<typename Component>
292  [[nodiscard]] const auto &assure(const id_type id = type_hash<Component>::value()) const {
293  static_assert(std::is_same_v<Component, std::decay_t<Component>>, "Non-decayed types not allowed");
294 
295  if(const auto it = pools.find(id); it != pools.cend()) {
296  ENTT_ASSERT(it->second->type() == type_id<Component>(), "Unexpected type");
297  return static_cast<const storage_type<Component> &>(*it->second);
298  }
299 
300  static storage_type<Component> placeholder{};
301  return placeholder;
302  }
303 
304  auto generate_identifier(const std::size_t pos) ENTT_NOEXCEPT {
305  ENTT_ASSERT(pos < entity_traits::to_integral(null), "No entities available");
306  return entity_traits::combine(static_cast<typename entity_traits::entity_type>(pos), {});
307  }
308 
309  auto recycle_identifier() ENTT_NOEXCEPT {
310  ENTT_ASSERT(free_list != null, "No entities available");
311  const auto curr = entity_traits::to_entity(free_list);
312  free_list = entity_traits::combine(entity_traits::to_integral(entities[curr]), tombstone);
313  return (entities[curr] = entity_traits::combine(curr, entity_traits::to_integral(entities[curr])));
314  }
315 
316  auto release_entity(const Entity entity, const typename entity_traits::version_type version) {
317  const typename entity_traits::version_type vers = version + (version == entity_traits::to_version(tombstone));
320  return vers;
321  }
322 
323 public:
325  using entity_type = Entity;
329  using size_type = std::size_t;
333  using context = internal::registry_context;
334 
337  : pools{},
338  groups{},
339  entities{},
340  free_list{tombstone},
341  vars{} {}
342 
348  : pools{},
349  groups{},
350  entities{},
351  free_list{tombstone},
352  vars{} {
353  pools.reserve(count);
354  }
355 
361  : pools{std::move(other.pools)},
362  groups{std::move(other.groups)},
363  entities{std::move(other.entities)},
364  free_list{other.free_list},
365  vars{std::move(other.vars)} {
366  for(auto &&curr: pools) {
367  curr.second->bind(forward_as_any(*this));
368  }
369  }
370 
377  pools = std::move(other.pools);
378  groups = std::move(other.groups);
379  entities = std::move(other.entities);
380  free_list = other.free_list;
381  vars = std::move(other.vars);
382 
383  for(auto &&curr: pools) {
384  curr.second->bind(forward_as_any(*this));
385  }
386 
387  return *this;
388  }
389 
398  [[nodiscard]] auto storage() ENTT_NOEXCEPT {
399  return iterable_adaptor{internal::storage_proxy_iterator{pools.begin()}, internal::storage_proxy_iterator{pools.end()}};
400  }
401 
403  [[nodiscard]] auto storage() const ENTT_NOEXCEPT {
404  return iterable_adaptor{internal::storage_proxy_iterator{pools.cbegin()}, internal::storage_proxy_iterator{pools.cend()}};
405  }
406 
413  [[nodiscard]] auto storage(const id_type id) {
414  return internal::storage_proxy_iterator{pools.find(id)};
415  }
416 
423  [[nodiscard]] auto storage(const id_type id) const {
424  return internal::storage_proxy_iterator{pools.find(id)};
425  }
426 
433  template<typename Component>
434  decltype(auto) storage(const id_type id = type_hash<std::remove_const_t<Component>>::value()) {
435  if constexpr(std::is_const_v<Component>) {
436  return std::as_const(*this).template storage<std::remove_const_t<Component>>(id);
437  } else {
438  return assure<Component>(id);
439  }
440  }
441 
453  template<typename Component>
454  decltype(auto) storage(const id_type id = type_hash<std::remove_const_t<Component>>::value()) const {
455  return assure<std::remove_const_t<Component>>(id);
456  }
457 
462  [[nodiscard]] size_type size() const ENTT_NOEXCEPT {
463  return entities.size();
464  }
465 
470  [[nodiscard]] size_type alive() const {
471  auto sz = entities.size();
472 
473  for(auto curr = free_list; curr != null; --sz) {
474  curr = entities[entity_traits::to_entity(curr)];
475  }
476 
477  return sz;
478  }
479 
484  void reserve(const size_type cap) {
485  entities.reserve(cap);
486  }
487 
493  [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT {
494  return entities.capacity();
495  }
496 
501  [[nodiscard]] bool empty() const {
502  return !alive();
503  }
504 
517  [[nodiscard]] const entity_type *data() const ENTT_NOEXCEPT {
518  return entities.data();
519  }
520 
529  [[nodiscard]] entity_type released() const ENTT_NOEXCEPT {
530  return free_list;
531  }
532 
538  [[nodiscard]] bool valid(const entity_type entity) const {
539  const auto pos = size_type(entity_traits::to_entity(entity));
540  return (pos < entities.size() && entities[pos] == entity);
541  }
542 
549  [[nodiscard]] version_type current(const entity_type entity) const {
550  const auto pos = size_type(entity_traits::to_entity(entity));
551  return entity_traits::to_version(pos < entities.size() ? entities[pos] : tombstone);
552  }
553 
558  [[nodiscard]] entity_type create() {
559  return (free_list == null) ? entities.emplace_back(generate_identifier(entities.size())) : recycle_identifier();
560  }
561 
571  [[nodiscard]] entity_type create(const entity_type hint) {
572  const auto length = entities.size();
573 
574  if(hint == null || hint == tombstone) {
575  return create();
576  } else if(const auto req = entity_traits::to_entity(hint); !(req < length)) {
577  entities.resize(size_type(req) + 1u, null);
578 
579  for(auto pos = length; pos < req; ++pos) {
580  release_entity(generate_identifier(pos), {});
581  }
582 
583  return (entities[req] = hint);
584  } else if(const auto curr = entity_traits::to_entity(entities[req]); req == curr) {
585  return create();
586  } else {
587  auto *it = &free_list;
588  for(; entity_traits::to_entity(*it) != req; it = &entities[entity_traits::to_entity(*it)]) {}
590  return (entities[req] = hint);
591  }
592  }
593 
603  template<typename It>
604  void create(It first, It last) {
605  for(; free_list != null && first != last; ++first) {
606  *first = recycle_identifier();
607  }
608 
609  const auto length = entities.size();
610  entities.resize(length + std::distance(first, last), null);
611 
612  for(auto pos = length; first != last; ++first, ++pos) {
613  *first = entities[pos] = generate_identifier(pos);
614  }
615  }
616 
634  template<typename It>
635  void assign(It first, It last, const entity_type destroyed) {
636  ENTT_ASSERT(!alive(), "Entities still alive");
637  entities.assign(first, last);
638  free_list = destroyed;
639  }
640 
653  return release(entity, static_cast<version_type>(entity_traits::to_version(entity) + 1u));
654  }
655 
669  ENTT_ASSERT(orphan(entity), "Non-orphan entity");
670  return release_entity(entity, version);
671  }
672 
682  template<typename It>
683  void release(It first, It last) {
684  for(; first != last; ++first) {
685  release(*first);
686  }
687  }
688 
703  return destroy(entity, static_cast<version_type>(entity_traits::to_version(entity) + 1u));
704  }
705 
719  ENTT_ASSERT(valid(entity), "Invalid entity");
720 
721  for(size_type pos = pools.size(); pos; --pos) {
722  pools.begin()[pos - 1u].second->remove(entity);
723  }
724 
725  return release_entity(entity, version);
726  }
727 
737  template<typename It>
738  void destroy(It first, It last) {
739  for(; first != last; ++first) {
740  destroy(*first);
741  }
742  }
743 
759  template<typename Component, typename... Args>
760  decltype(auto) emplace(const entity_type entity, Args &&...args) {
761  ENTT_ASSERT(valid(entity), "Invalid entity");
762  return assure<Component>().emplace(entity, std::forward<Args>(args)...);
763  }
764 
776  template<typename Component, typename It>
777  void insert(It first, It last, const Component &value = {}) {
778  ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
779  assure<Component>().insert(first, last, value);
780  }
781 
794  template<typename Component, typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, Component>>>
795  void insert(EIt first, EIt last, CIt from) {
796  ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
797  assure<Component>().insert(first, last, from);
798  }
799 
812  template<typename Component, typename... Args>
813  decltype(auto) emplace_or_replace(const entity_type entity, Args &&...args) {
814  ENTT_ASSERT(valid(entity), "Invalid entity");
815  auto &cpool = assure<Component>();
816 
817  return cpool.contains(entity)
818  ? cpool.patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward<Args>(args)...}), ...); })
819  : cpool.emplace(entity, std::forward<Args>(args)...);
820  }
821 
846  template<typename Component, typename... Func>
847  decltype(auto) patch(const entity_type entity, Func &&...func) {
848  ENTT_ASSERT(valid(entity), "Invalid entity");
849  return assure<Component>().patch(entity, std::forward<Func>(func)...);
850  }
851 
867  template<typename Component, typename... Args>
868  decltype(auto) replace(const entity_type entity, Args &&...args) {
869  ENTT_ASSERT(valid(entity), "Invalid entity");
870  return assure<Component>().patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward<Args>(args)...}), ...); });
871  }
872 
884  template<typename Component, typename... Other>
886  ENTT_ASSERT(valid(entity), "Invalid entity");
887  return (assure<Component>().remove(entity) + ... + assure<Other>().remove(entity));
888  }
889 
902  template<typename Component, typename... Other, typename It>
903  size_type remove(It first, It last) {
904  if constexpr(sizeof...(Other) == 0u) {
905  ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
906  return assure<Component>().remove(std::move(first), std::move(last));
907  } else {
908  size_type count{};
909 
910  for(auto cpools = std::forward_as_tuple(assure<Component>(), assure<Other>()...); first != last; ++first) {
911  ENTT_ASSERT(valid(*first), "Invalid entity");
912  count += std::apply([entt = *first](auto &...curr) { return (curr.remove(entt) + ... + 0u); }, cpools);
913  }
914 
915  return count;
916  }
917  }
918 
930  template<typename Component, typename... Other>
931  void erase(const entity_type entity) {
932  ENTT_ASSERT(valid(entity), "Invalid entity");
933  (assure<Component>().erase(entity), (assure<Other>().erase(entity), ...));
934  }
935 
947  template<typename Component, typename... Other, typename It>
948  void erase(It first, It last) {
949  if constexpr(sizeof...(Other) == 0u) {
950  ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity");
951  assure<Component>().erase(std::move(first), std::move(last));
952  } else {
953  for(auto cpools = std::forward_as_tuple(assure<Component>(), assure<Other>()...); first != last; ++first) {
954  ENTT_ASSERT(valid(*first), "Invalid entity");
955  std::apply([entt = *first](auto &...curr) { (curr.erase(entt), ...); }, cpools);
956  }
957  }
958  }
959 
965  template<typename... Component>
966  void compact() {
967  if constexpr(sizeof...(Component) == 0) {
968  for(auto &&curr: pools) {
969  curr.second->compact();
970  }
971  } else {
972  (assure<Component>().compact(), ...);
973  }
974  }
975 
986  template<typename... Component>
987  [[nodiscard]] bool all_of(const entity_type entity) const {
988  ENTT_ASSERT(valid(entity), "Invalid entity");
989  return (assure<std::remove_const_t<Component>>().contains(entity) && ...);
990  }
991 
1003  template<typename... Component>
1004  [[nodiscard]] bool any_of(const entity_type entity) const {
1005  ENTT_ASSERT(valid(entity), "Invalid entity");
1006  return (assure<std::remove_const_t<Component>>().contains(entity) || ...);
1007  }
1008 
1020  template<typename... Component>
1021  [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) const {
1022  ENTT_ASSERT(valid(entity), "Invalid entity");
1023  return view<Component...>().template get<const Component...>(entity);
1024  }
1025 
1027  template<typename... Component>
1028  [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) {
1029  ENTT_ASSERT(valid(entity), "Invalid entity");
1030  return view<Component...>().template get<Component...>(entity);
1031  }
1032 
1048  template<typename Component, typename... Args>
1049  [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&...args) {
1050  ENTT_ASSERT(valid(entity), "Invalid entity");
1051  auto &cpool = assure<Component>();
1052  return cpool.contains(entity) ? cpool.get(entity) : cpool.emplace(entity, std::forward<Args>(args)...);
1053  }
1054 
1068  template<typename... Component>
1069  [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) const {
1070  ENTT_ASSERT(valid(entity), "Invalid entity");
1071 
1072  if constexpr(sizeof...(Component) == 1) {
1073  const auto &cpool = assure<std::remove_const_t<Component>...>();
1074  return cpool.contains(entity) ? std::addressof(cpool.get(entity)) : nullptr;
1075  } else {
1076  return std::make_tuple(try_get<Component>(entity)...);
1077  }
1078  }
1079 
1081  template<typename... Component>
1082  [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) {
1083  if constexpr(sizeof...(Component) == 1) {
1084  return (const_cast<Component *>(std::as_const(*this).template try_get<Component>(entity)), ...);
1085  } else {
1086  return std::make_tuple(try_get<Component>(entity)...);
1087  }
1088  }
1089 
1094  template<typename... Component>
1095  void clear() {
1096  if constexpr(sizeof...(Component) == 0) {
1097  for(auto &&curr: pools) {
1098  curr.second->clear();
1099  }
1100 
1101  each([this](const auto entity) { this->release(entity); });
1102  } else {
1103  (assure<Component>().clear(), ...);
1104  }
1105  }
1106 
1121  template<typename Func>
1122  void each(Func func) const {
1123  if(free_list == null) {
1124  for(auto pos = entities.size(); pos; --pos) {
1125  func(entities[pos - 1]);
1126  }
1127  } else {
1128  for(auto pos = entities.size(); pos; --pos) {
1129  if(const auto entity = entities[pos - 1]; entity_traits::to_entity(entity) == (pos - 1)) {
1130  func(entity);
1131  }
1132  }
1133  }
1134  }
1135 
1141  [[nodiscard]] bool orphan(const entity_type entity) const {
1142  ENTT_ASSERT(valid(entity), "Invalid entity");
1143  return std::none_of(pools.cbegin(), pools.cend(), [entity](auto &&curr) { return curr.second->contains(entity); });
1144  }
1145 
1164  template<typename Component>
1165  [[nodiscard]] auto on_construct() {
1166  return assure<Component>().on_construct();
1167  }
1168 
1187  template<typename Component>
1188  [[nodiscard]] auto on_update() {
1189  return assure<Component>().on_update();
1190  }
1191 
1210  template<typename Component>
1211  [[nodiscard]] auto on_destroy() {
1212  return assure<Component>().on_destroy();
1213  }
1214 
1228  template<typename Component, typename... Other, typename... Exclude>
1229  [[nodiscard]] basic_view<entity_type, get_t<std::add_const_t<Component>, std::add_const_t<Other>...>, exclude_t<Exclude...>> view(exclude_t<Exclude...> = {}) const {
1230  return {assure<std::remove_const_t<Component>>(), assure<std::remove_const_t<Other>>()..., assure<Exclude>()...};
1231  }
1232 
1234  template<typename Component, typename... Other, typename... Exclude>
1235  [[nodiscard]] basic_view<entity_type, get_t<Component, Other...>, exclude_t<Exclude...>> view(exclude_t<Exclude...> = {}) {
1236  return {assure<std::remove_const_t<Component>>(), assure<std::remove_const_t<Other>>()..., assure<Exclude>()...};
1237  }
1238 
1263  template<typename... Owned, typename... Get, typename... Exclude>
1264  [[nodiscard]] basic_group<entity_type, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> group(get_t<Get...>, exclude_t<Exclude...> = {}) {
1265  static_assert(sizeof...(Owned) + sizeof...(Get) > 0, "Exclusion-only groups are not supported");
1266  static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1, "Single component groups are not allowed");
1267 
1268  using handler_type = group_handler<exclude_t<std::remove_const_t<Exclude>...>, get_t<std::remove_const_t<Get>...>, std::remove_const_t<Owned>...>;
1269 
1270  const auto cpools = std::forward_as_tuple(assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()...);
1271  constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude);
1272  handler_type *handler = nullptr;
1273 
1274  auto it = std::find_if(groups.cbegin(), groups.cend(), [size](const auto &gdata) {
1275  return gdata.size == size
1276  && (gdata.owned(type_hash<std::remove_const_t<Owned>>::value()) && ...)
1277  && (gdata.get(type_hash<std::remove_const_t<Get>>::value()) && ...)
1278  && (gdata.exclude(type_hash<Exclude>::value()) && ...);
1279  });
1280 
1281  if(it != groups.cend()) {
1282  handler = static_cast<handler_type *>(it->group.get());
1283  } else {
1284  group_data candidate = {
1285  size,
1286  {new handler_type{}, [](void *instance) { delete static_cast<handler_type *>(instance); }},
1287  []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash<std::remove_const_t<Owned>>::value()) || ...); },
1288  []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash<std::remove_const_t<Get>>::value()) || ...); },
1289  []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash<Exclude>::value()) || ...); },
1290  };
1291 
1292  handler = static_cast<handler_type *>(candidate.group.get());
1293 
1294  const void *maybe_valid_if = nullptr;
1295  const void *discard_if = nullptr;
1296 
1297  if constexpr(sizeof...(Owned) == 0) {
1298  groups.push_back(std::move(candidate));
1299  } else {
1300  [[maybe_unused]] auto has_conflict = [size](const auto &gdata) {
1301  const auto overlapping = (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value()));
1302  const auto sz = overlapping + (0u + ... + gdata.get(type_hash<std::remove_const_t<Get>>::value())) + (0u + ... + gdata.exclude(type_hash<Exclude>::value()));
1303  return !overlapping || ((sz == size) || (sz == gdata.size));
1304  };
1305 
1306  ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), std::move(has_conflict)), "Conflicting groups");
1307 
1308  const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [size](const auto &gdata) {
1309  return !(0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value())) || (size > gdata.size);
1310  });
1311 
1312  const auto prev = std::find_if(std::make_reverse_iterator(next), groups.crend(), [](const auto &gdata) {
1313  return (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value()));
1314  });
1315 
1316  maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get());
1317  discard_if = (prev == groups.crend() ? discard_if : prev->group.get());
1318  groups.insert(next, std::move(candidate));
1319  }
1320 
1321  (on_construct<std::remove_const_t<Owned>>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<std::remove_const_t<Owned>>>(*handler), ...);
1322  (on_construct<std::remove_const_t<Get>>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<std::remove_const_t<Get>>>(*handler), ...);
1323  (on_destroy<Exclude>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<Exclude>>(*handler), ...);
1324 
1325  (on_destroy<std::remove_const_t<Owned>>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...);
1326  (on_destroy<std::remove_const_t<Get>>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...);
1327  (on_construct<Exclude>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...);
1328 
1329  if constexpr(sizeof...(Owned) == 0) {
1330  for(const auto entity: view<Owned..., Get...>(exclude<Exclude...>)) {
1331  handler->current.emplace(entity);
1332  }
1333  } else {
1334  // we cannot iterate backwards because we want to leave behind valid entities in case of owned types
1335  for(auto *first = std::get<0>(cpools).data(), *last = first + std::get<0>(cpools).size(); first != last; ++first) {
1336  handler->template maybe_valid_if<type_list_element_t<0, type_list<std::remove_const_t<Owned>...>>>(*this, *first);
1337  }
1338  }
1339  }
1340 
1341  return {handler->current, std::get<storage_type<std::remove_const_t<Owned>> &>(cpools)..., std::get<storage_type<std::remove_const_t<Get>> &>(cpools)...};
1342  }
1343 
1345  template<typename... Owned, typename... Get, typename... Exclude>
1347  auto it = std::find_if(groups.cbegin(), groups.cend(), [](const auto &gdata) {
1348  return gdata.size == (sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude))
1349  && (gdata.owned(type_hash<std::remove_const_t<Owned>>::value()) && ...)
1350  && (gdata.get(type_hash<std::remove_const_t<Get>>::value()) && ...)
1351  && (gdata.exclude(type_hash<Exclude>::value()) && ...);
1352  });
1353 
1354  if(it == groups.cend()) {
1355  return {};
1356  } else {
1357  using handler_type = group_handler<exclude_t<std::remove_const_t<Exclude>...>, get_t<std::remove_const_t<Get>...>, std::remove_const_t<Owned>...>;
1358  return {static_cast<handler_type *>(it->group.get())->current, assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()...};
1359  }
1360  }
1361 
1363  template<typename... Owned, typename... Exclude>
1364  [[nodiscard]] basic_group<entity_type, owned_t<Owned...>, get_t<>, exclude_t<Exclude...>> group(exclude_t<Exclude...> = {}) {
1365  return group<Owned...>(get_t<>{}, exclude<Exclude...>);
1366  }
1367 
1369  template<typename... Owned, typename... Exclude>
1371  return group_if_exists<std::add_const_t<Owned>...>(get_t<>{}, exclude<Exclude...>);
1372  }
1373 
1380  template<typename... Component>
1381  [[nodiscard]] bool owned() const {
1382  return std::any_of(groups.cbegin(), groups.cend(), [](auto &&gdata) { return (gdata.owned(type_hash<std::remove_const_t<Component>>::value()) || ...); });
1383  }
1384 
1392  template<typename... Owned, typename... Get, typename... Exclude>
1394  constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude);
1395  auto pred = [size](const auto &gdata) { return (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value())) && (size < gdata.size); };
1396  return std::find_if(groups.cbegin(), groups.cend(), std::move(pred)) == groups.cend();
1397  }
1398 
1434  template<typename Component, typename Compare, typename Sort = std_sort, typename... Args>
1435  void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
1436  ENTT_ASSERT(!owned<Component>(), "Cannot sort owned storage");
1437  auto &cpool = assure<Component>();
1438 
1439  if constexpr(std::is_invocable_v<Compare, decltype(cpool.get({})), decltype(cpool.get({}))>) {
1440  auto comp = [&cpool, compare = std::move(compare)](const auto lhs, const auto rhs) { return compare(std::as_const(cpool.get(lhs)), std::as_const(cpool.get(rhs))); };
1441  cpool.sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
1442  } else {
1443  cpool.sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
1444  }
1445  }
1446 
1466  template<typename To, typename From>
1467  void sort() {
1468  ENTT_ASSERT(!owned<To>(), "Cannot sort owned storage");
1469  assure<To>().respect(assure<From>());
1470  }
1471 
1476  context &ctx() ENTT_NOEXCEPT {
1477  return vars;
1478  }
1479 
1481  const context &ctx() const ENTT_NOEXCEPT {
1482  return vars;
1483  }
1484 
1485 private:
1487  std::vector<group_data> groups;
1488  std::vector<entity_type> entities;
1489  entity_type free_list;
1490  context vars;
1491 };
1492 
1493 } // namespace entt
1494 
1495 #endif
Group.
Definition: fwd.hpp:26
Fast and reliable entity-component system.
Definition: registry.hpp:221
size_type capacity() const
Returns the number of entities that a registry has currently allocated space for.
Definition: registry.hpp:493
const context & ctx() const
Returns the context object, that is, a general purpose container.
Definition: registry.hpp:1481
bool any_of(const entity_type entity) const
Checks if an entity has at least one of the given components.
Definition: registry.hpp:1004
context & ctx()
Returns the context object, that is, a general purpose container.
Definition: registry.hpp:1476
auto storage(const id_type id)
Finds the storage associated with a given name, if any.
Definition: registry.hpp:413
bool sortable(const basic_group< entity_type, owned_t< Owned... >, get_t< Get... >, exclude_t< Exclude... >> &)
Checks whether a group can be sorted.
Definition: registry.hpp:1393
bool orphan(const entity_type entity) const
Checks if an entity has components assigned.
Definition: registry.hpp:1141
internal::registry_context context
Context type.
Definition: registry.hpp:333
bool valid(const entity_type entity) const
Checks if an identifier refers to a valid entity.
Definition: registry.hpp:538
version_type current(const entity_type entity) const
Returns the actual version for an identifier.
Definition: registry.hpp:549
void insert(EIt first, EIt last, CIt from)
Assigns each entity in a range the given components.
Definition: registry.hpp:795
basic_registry & operator=(basic_registry &&other)
Move assignment operator.
Definition: registry.hpp:376
void compact()
Removes all tombstones from a registry or only the pools for the given components.
Definition: registry.hpp:966
basic_view< entity_type, get_t< std::add_const_t< Component >, std::add_const_t< Other >... >, exclude_t< Exclude... > > view(exclude_t< Exclude... >={}) const
Returns a view for the given components.
Definition: registry.hpp:1229
void sort()
Sorts two pools of components in the same way.
Definition: registry.hpp:1467
void create(It first, It last)
Assigns each element in a range an identifier.
Definition: registry.hpp:604
void insert(It first, It last, const Component &value={})
Assigns each entity in a range the given component.
Definition: registry.hpp:777
void assign(It first, It last, const entity_type destroyed)
Assigns identifiers to an empty registry.
Definition: registry.hpp:635
decltype(auto) get([[maybe_unused]] const entity_type entity) const
Returns references to the given components for an entity.
Definition: registry.hpp:1021
decltype(auto) emplace_or_replace(const entity_type entity, Args &&...args)
Assigns or replaces the given component for an entity.
Definition: registry.hpp:813
version_type release(const entity_type entity)
Releases an identifier.
Definition: registry.hpp:652
bool all_of(const entity_type entity) const
Checks if an entity has all the given components.
Definition: registry.hpp:987
void sort(Compare compare, Sort algo=Sort{}, Args &&...args)
Sorts the elements of a given component.
Definition: registry.hpp:1435
entity_type released() const
Returns the head of the list of released entities.
Definition: registry.hpp:529
basic_registry(const size_type count)
Allocates enough memory upon construction to store count pools.
Definition: registry.hpp:347
decltype(auto) emplace(const entity_type entity, Args &&...args)
Assigns the given component to an entity.
Definition: registry.hpp:760
bool empty() const
Checks whether the registry is empty (no entities still in use).
Definition: registry.hpp:501
size_type remove(It first, It last)
Removes the given components from all the entities in a range.
Definition: registry.hpp:903
size_type size() const
Returns the number of entities created so far.
Definition: registry.hpp:462
basic_view< entity_type, get_t< Component, Other... >, exclude_t< Exclude... > > view(exclude_t< Exclude... >={})
Returns a view for the given components.
Definition: registry.hpp:1235
void release(It first, It last)
Releases all identifiers in a range.
Definition: registry.hpp:683
std::size_t size_type
Unsigned integer type.
Definition: registry.hpp:329
void destroy(It first, It last)
Destroys all entities in a range and releases their identifiers.
Definition: registry.hpp:738
auto storage() const
Returns an iterable object to use to visit a registry.
Definition: registry.hpp:403
size_type alive() const
Returns the number of entities still in use.
Definition: registry.hpp:470
version_type destroy(const entity_type entity)
Destroys an entity and releases its identifier.
Definition: registry.hpp:702
basic_group< entity_type, owned_t< std::add_const_t< Owned >... >, get_t< std::add_const_t< Get >... >, exclude_t< Exclude... > > group_if_exists(get_t< Get... >, exclude_t< Exclude... >={}) const
Returns a group for the given components.
Definition: registry.hpp:1346
Entity entity_type
Underlying entity identifier.
Definition: registry.hpp:325
typename entity_traits::version_type version_type
Underlying version type.
Definition: registry.hpp:327
auto storage(const id_type id) const
Finds the storage associated with a given name, if any.
Definition: registry.hpp:423
basic_registry()
Default constructor.
Definition: registry.hpp:336
void each(Func func) const
Iterates all the entities that are still in use.
Definition: registry.hpp:1122
void erase(It first, It last)
Erases the given components from all the entities in a range.
Definition: registry.hpp:948
entity_type create()
Creates a new entity or recycles a destroyed one.
Definition: registry.hpp:558
void reserve(const size_type cap)
Increases the capacity (number of entities) of the registry.
Definition: registry.hpp:484
auto try_get([[maybe_unused]] const entity_type entity) const
Returns pointers to the given components for an entity.
Definition: registry.hpp:1069
bool owned() const
Checks whether the given components belong to any group.
Definition: registry.hpp:1381
auto storage()
Returns an iterable object to use to visit a registry.
Definition: registry.hpp:398
void clear()
Clears a whole registry or the pools for the given components.
Definition: registry.hpp:1095
size_type remove(const entity_type entity)
Removes the given components from an entity.
Definition: registry.hpp:885
auto on_update()
Returns a sink object for the given component.
Definition: registry.hpp:1188
entity_type create(const entity_type hint)
Creates a new entity or recycles a destroyed one.
Definition: registry.hpp:571
const entity_type * data() const
Direct access to the list of entities of a registry.
Definition: registry.hpp:517
auto try_get([[maybe_unused]] const entity_type entity)
Returns pointers to the given components for an entity.
Definition: registry.hpp:1082
basic_registry(basic_registry &&other)
Move constructor.
Definition: registry.hpp:360
void erase(const entity_type entity)
Erases the given components from an entity.
Definition: registry.hpp:931
auto on_destroy()
Returns a sink object for the given component.
Definition: registry.hpp:1211
version_type destroy(const entity_type entity, const version_type version)
Destroys an entity and releases its identifier.
Definition: registry.hpp:718
auto on_construct()
Returns a sink object for the given component.
Definition: registry.hpp:1165
basic_group< entity_type, owned_t< Owned... >, get_t< Get... >, exclude_t< Exclude... > > group(get_t< Get... >, exclude_t< Exclude... >={})
Returns a group for the given components.
Definition: registry.hpp:1264
basic_group< entity_type, owned_t< Owned... >, get_t<>, exclude_t< Exclude... > > group(exclude_t< Exclude... >={})
Returns a group for the given components.
Definition: registry.hpp:1364
basic_group< entity_type, owned_t< std::add_const_t< Owned >... >, get_t<>, exclude_t< Exclude... > > group_if_exists(exclude_t< Exclude... >={}) const
Returns a group for the given components.
Definition: registry.hpp:1370
version_type release(const entity_type entity, const version_type version)
Releases an identifier.
Definition: registry.hpp:668
Basic sparse set implementation.
Definition: sparse_set.hpp:174
Basic storage implementation.
Definition: storage.hpp:234
View implementation.
Definition: fwd.hpp:20
Associative container for key-value pairs with unique keys.
Definition: dense_map.hpp:265
Entity traits.
Definition: entity.hpp:62
typename base_type::entity_type entity_type
Underlying entity type.
Definition: entity.hpp:69
static constexpr entity_type to_integral(const value_type value)
Converts an entity to its underlying type.
Definition: entity.hpp:82
static constexpr entity_type to_entity(const value_type value)
Returns the entity part once converted to the underlying type.
Definition: entity.hpp:91
static constexpr value_type construct(const entity_type entity, const version_type version)
Constructs an identifier from its parts.
Definition: entity.hpp:114
static constexpr value_type combine(const entity_type lhs, const entity_type rhs)
Combines two identifiers in a single one.
Definition: entity.hpp:128
typename base_type::version_type version_type
Underlying version type.
Definition: entity.hpp:71
static constexpr version_type to_version(const value_type value)
Returns the version part once converted to the underlying type.
Definition: entity.hpp:100
Mixin type used to add signal support to storage types.
EnTT default namespace.
Definition: dense_map.hpp:22
constexpr bool operator==(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.
entity
Default entity identifier.
Definition: fwd.hpp:47
std::uint32_t id_type
Alias declaration for type identifiers.
Definition: fwd.hpp:14
constexpr bool operator<=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.
constexpr exclude_t< Type... > exclude
Variable template for exclusion lists.
Definition: utility.hpp:20
constexpr bool operator>=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.
constexpr tombstone_t tombstone
Compile-time constant for tombstone entities.
Definition: entity.hpp:335
constexpr type_list< Type..., Other... > operator+(type_list< Type... >, type_list< Other... >)
Concatenates multiple type lists.
basic_any< Len, Align > forward_as_any(Type &&value)
Forwards its argument and avoids copies for lvalue references.
Definition: any.hpp:484
constexpr bool operator<(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.
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:402
constexpr bool operator>(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.
Common way to access various properties of components.
Definition: component.hpp:43
Alias for exclusion lists.
Definition: utility.hpp:13
Alias for lists of observed components.
Definition: utility.hpp:27
Identity function object (waiting for C++20).
Definition: utility.hpp:10
Utility class to create an iterable object from a pair of iterators.
Definition: iterator.hpp:63
Alias for lists of owned components.
Definition: utility.hpp:41
Function object to wrap std::sort in a class type.
Definition: algorithm.hpp:21
Type hash.
Definition: type_info.hpp:100