EnTT 3.11.0
registry.hpp
1#ifndef ENTT_ENTITY_REGISTRY_HPP
2#define ENTT_ENTITY_REGISTRY_HPP
3
4#include <algorithm>
5#include <cstddef>
6#include <functional>
7#include <iterator>
8#include <memory>
9#include <tuple>
10#include <type_traits>
11#include <utility>
12#include <vector>
13#include "../config/config.h"
14#include "../container/dense_map.hpp"
15#include "../core/algorithm.hpp"
16#include "../core/any.hpp"
17#include "../core/compressed_pair.hpp"
18#include "../core/fwd.hpp"
19#include "../core/iterator.hpp"
20#include "../core/memory.hpp"
21#include "../core/type_info.hpp"
22#include "../core/type_traits.hpp"
23#include "../core/utility.hpp"
24#include "component.hpp"
25#include "entity.hpp"
26#include "fwd.hpp"
27#include "group.hpp"
28#include "sparse_set.hpp"
29#include "storage.hpp"
30#include "view.hpp"
31
32namespace entt {
33
39namespace internal {
40
41template<typename It>
42class registry_storage_iterator final {
43 template<typename Other>
44 friend class registry_storage_iterator;
45
46 using mapped_type = std::remove_reference_t<decltype(std::declval<It>()->second)>;
47
48public:
49 using value_type = std::pair<id_type, constness_as_t<typename mapped_type::element_type, mapped_type> &>;
50 using pointer = input_iterator_pointer<value_type>;
51 using reference = value_type;
52 using difference_type = std::ptrdiff_t;
53 using iterator_category = std::input_iterator_tag;
54
55 constexpr registry_storage_iterator() noexcept
56 : it{} {}
57
58 constexpr registry_storage_iterator(It iter) noexcept
59 : it{iter} {}
60
61 template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
62 constexpr registry_storage_iterator(const registry_storage_iterator<Other> &other) noexcept
63 : registry_storage_iterator{other.it} {}
64
65 constexpr registry_storage_iterator &operator++() noexcept {
66 return ++it, *this;
67 }
68
69 constexpr registry_storage_iterator operator++(int) noexcept {
70 registry_storage_iterator orig = *this;
71 return ++(*this), orig;
72 }
73
74 constexpr registry_storage_iterator &operator--() noexcept {
75 return --it, *this;
76 }
77
78 constexpr registry_storage_iterator operator--(int) noexcept {
79 registry_storage_iterator orig = *this;
80 return operator--(), orig;
81 }
82
83 constexpr registry_storage_iterator &operator+=(const difference_type value) noexcept {
84 it += value;
85 return *this;
86 }
87
88 constexpr registry_storage_iterator operator+(const difference_type value) const noexcept {
89 registry_storage_iterator copy = *this;
90 return (copy += value);
91 }
92
93 constexpr registry_storage_iterator &operator-=(const difference_type value) noexcept {
94 return (*this += -value);
95 }
96
97 constexpr registry_storage_iterator operator-(const difference_type value) const noexcept {
98 return (*this + -value);
99 }
100
101 [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
102 return {it[value].first, *it[value].second};
103 }
104
105 [[nodiscard]] constexpr reference operator*() const noexcept {
106 return {it->first, *it->second};
107 }
108
109 [[nodiscard]] constexpr pointer operator->() const noexcept {
110 return operator*();
111 }
112
113 template<typename ILhs, typename IRhs>
114 friend constexpr std::ptrdiff_t operator-(const registry_storage_iterator<ILhs> &, const registry_storage_iterator<IRhs> &) noexcept;
115
116 template<typename ILhs, typename IRhs>
117 friend constexpr bool operator==(const registry_storage_iterator<ILhs> &, const registry_storage_iterator<IRhs> &) noexcept;
118
119 template<typename ILhs, typename IRhs>
120 friend constexpr bool operator<(const registry_storage_iterator<ILhs> &, const registry_storage_iterator<IRhs> &) noexcept;
121
122private:
123 It it;
124};
125
126template<typename ILhs, typename IRhs>
127[[nodiscard]] constexpr std::ptrdiff_t operator-(const registry_storage_iterator<ILhs> &lhs, const registry_storage_iterator<IRhs> &rhs) noexcept {
128 return lhs.it - rhs.it;
129}
130
131template<typename ILhs, typename IRhs>
132[[nodiscard]] constexpr bool operator==(const registry_storage_iterator<ILhs> &lhs, const registry_storage_iterator<IRhs> &rhs) noexcept {
133 return lhs.it == rhs.it;
134}
135
136template<typename ILhs, typename IRhs>
137[[nodiscard]] constexpr bool operator!=(const registry_storage_iterator<ILhs> &lhs, const registry_storage_iterator<IRhs> &rhs) noexcept {
138 return !(lhs == rhs);
139}
140
141template<typename ILhs, typename IRhs>
142[[nodiscard]] constexpr bool operator<(const registry_storage_iterator<ILhs> &lhs, const registry_storage_iterator<IRhs> &rhs) noexcept {
143 return lhs.it < rhs.it;
144}
145
146template<typename ILhs, typename IRhs>
147[[nodiscard]] constexpr bool operator>(const registry_storage_iterator<ILhs> &lhs, const registry_storage_iterator<IRhs> &rhs) noexcept {
148 return rhs < lhs;
149}
150
151template<typename ILhs, typename IRhs>
152[[nodiscard]] constexpr bool operator<=(const registry_storage_iterator<ILhs> &lhs, const registry_storage_iterator<IRhs> &rhs) noexcept {
153 return !(lhs > rhs);
154}
155
156template<typename ILhs, typename IRhs>
157[[nodiscard]] constexpr bool operator>=(const registry_storage_iterator<ILhs> &lhs, const registry_storage_iterator<IRhs> &rhs) noexcept {
158 return !(lhs < rhs);
159}
160
161class registry_context {
162 using key_type = id_type;
163 using mapped_type = basic_any<0u>;
164 using container_type = dense_map<key_type, mapped_type, identity>;
165
166public:
167 template<typename Type, typename... Args>
168 [[deprecated("Use ::emplace_as instead")]] Type &emplace_hint(const id_type id, Args &&...args) {
169 return emplace_as<Type>(id, std::forward<Args>(args)...);
170 }
171
172 template<typename Type, typename... Args>
173 Type &emplace_as(const id_type id, Args &&...args) {
174 return any_cast<Type &>(ctx.try_emplace(id, std::in_place_type<Type>, std::forward<Args>(args)...).first->second);
175 }
176
177 template<typename Type, typename... Args>
178 Type &emplace(Args &&...args) {
179 return emplace_as<Type>(type_id<Type>().hash(), std::forward<Args>(args)...);
180 }
181
182 template<typename Type>
183 Type &insert_or_assign(const id_type id, Type &&value) {
184 return any_cast<std::remove_cv_t<std::remove_reference_t<Type>> &>(ctx.insert_or_assign(id, std::forward<Type>(value)).first->second);
185 }
186
187 template<typename Type>
188 Type &insert_or_assign(Type &&value) {
189 return insert_or_assign(type_id<Type>().hash(), std::forward<Type>(value));
190 }
191
192 template<typename Type>
193 bool erase(const id_type id = type_id<Type>().hash()) {
194 const auto it = ctx.find(id);
195 return it != ctx.end() && it->second.type() == type_id<Type>() ? (ctx.erase(it), true) : false;
196 }
197
198 template<typename Type>
199 [[deprecated("Use ::get instead")]] [[nodiscard]] const Type &at(const id_type id = type_id<Type>().hash()) const {
200 return get<Type>(id);
201 }
202
203 template<typename Type>
204 [[deprecated("Use ::get instead")]] [[nodiscard]] Type &at(const id_type id = type_id<Type>().hash()) {
205 return get<Type>(id);
206 }
207
208 template<typename Type>
209 [[nodiscard]] const Type &get(const id_type id = type_id<Type>().hash()) const {
210 return any_cast<const Type &>(ctx.at(id));
211 }
212
213 template<typename Type>
214 [[nodiscard]] Type &get(const id_type id = type_id<Type>().hash()) {
215 return any_cast<Type &>(ctx.at(id));
216 }
217
218 template<typename Type>
219 [[nodiscard]] const Type *find(const id_type id = type_id<Type>().hash()) const {
220 const auto it = ctx.find(id);
221 return it != ctx.cend() ? any_cast<const Type>(&it->second) : nullptr;
222 }
223
224 template<typename Type>
225 [[nodiscard]] Type *find(const id_type id = type_id<Type>().hash()) {
226 const auto it = ctx.find(id);
227 return it != ctx.end() ? any_cast<Type>(&it->second) : nullptr;
228 }
229
230 template<typename Type>
231 [[nodiscard]] bool contains(const id_type id = type_id<Type>().hash()) const {
232 const auto it = ctx.find(id);
233 return it != ctx.cend() && it->second.type() == type_id<Type>();
234 }
235
236private:
237 container_type ctx;
238};
239
240} // namespace internal
241
252template<typename Entity, typename Allocator>
254 using alloc_traits = typename std::allocator_traits<Allocator>;
255 static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
258
259 template<typename Type>
261
262 template<typename...>
263 struct group_handler;
264
265 template<typename... Exclude, typename... Get, typename... Owned>
266 struct group_handler<exclude_t<Exclude...>, get_t<Get...>, Owned...> {
267 // nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here
268 static_assert(!std::disjunction_v<std::bool_constant<component_traits<Owned>::in_place_delete>...>, "Groups do not support in-place delete");
269 using value_type = std::conditional_t<sizeof...(Owned) == 0, basic_common_type, std::size_t>;
270 value_type current{};
271
272 template<typename... Args>
273 group_handler(Args &&...args)
274 : current{std::forward<Args>(args)...} {}
275
276 template<typename Type>
277 void maybe_valid_if(basic_registry &owner, const Entity entt) {
278 [[maybe_unused]] const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...);
279
280 const auto is_valid = ((std::is_same_v<Type, Owned> || std::get<storage_for_type<Owned> &>(cpools).contains(entt)) && ...)
281 && ((std::is_same_v<Type, Get> || owner.assure<Get>().contains(entt)) && ...)
282 && ((std::is_same_v<Type, Exclude> || !owner.assure<Exclude>().contains(entt)) && ...);
283
284 if constexpr(sizeof...(Owned) == 0) {
285 if(is_valid && !current.contains(entt)) {
286 current.emplace(entt);
287 }
288 } else {
289 if(is_valid && !(std::get<0>(cpools).index(entt) < current)) {
290 const auto pos = current++;
291 (std::get<storage_for_type<Owned> &>(cpools).swap_elements(std::get<storage_for_type<Owned> &>(cpools).data()[pos], entt), ...);
292 }
293 }
294 }
295
296 void discard_if([[maybe_unused]] basic_registry &owner, const Entity entt) {
297 if constexpr(sizeof...(Owned) == 0) {
298 current.remove(entt);
299 } else {
300 if(const auto cpools = std::forward_as_tuple(owner.assure<Owned>()...); std::get<0>(cpools).contains(entt) && (std::get<0>(cpools).index(entt) < current)) {
301 const auto pos = --current;
302 (std::get<storage_for_type<Owned> &>(cpools).swap_elements(std::get<storage_for_type<Owned> &>(cpools).data()[pos], entt), ...);
303 }
304 }
305 }
306 };
307
308 struct group_data {
309 std::size_t size;
310 std::shared_ptr<void> group;
311 bool (*owned)(const id_type) noexcept;
312 bool (*get)(const id_type) noexcept;
313 bool (*exclude)(const id_type) noexcept;
314 };
315
316 template<typename Type>
317 [[nodiscard]] auto &assure(const id_type id = type_hash<Type>::value()) {
318 static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
319 auto &cpool = pools[id];
320
321 if(!cpool) {
322 cpool = std::allocate_shared<storage_for_type<std::remove_const_t<Type>>>(get_allocator(), get_allocator());
323 cpool->bind(forward_as_any(*this));
324 }
325
326 ENTT_ASSERT(cpool->type() == type_id<Type>(), "Unexpected type");
327 return static_cast<storage_for_type<Type> &>(*cpool);
328 }
329
330 template<typename Type>
331 [[nodiscard]] const auto &assure(const id_type id = type_hash<Type>::value()) const {
332 static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
333
334 if(const auto it = pools.find(id); it != pools.cend()) {
335 ENTT_ASSERT(it->second->type() == type_id<Type>(), "Unexpected type");
336 return static_cast<const storage_for_type<Type> &>(*it->second);
337 }
338
339 static storage_for_type<Type> placeholder{};
340 return placeholder;
341 }
342
343 auto generate_identifier(const std::size_t pos) noexcept {
344 ENTT_ASSERT(pos < entity_traits::to_entity(null), "No entities available");
345 return entity_traits::combine(static_cast<typename entity_traits::entity_type>(pos), {});
346 }
347
348 auto recycle_identifier() noexcept {
349 ENTT_ASSERT(free_list != null, "No entities available");
350 const auto curr = entity_traits::to_entity(free_list);
352 return (epool[curr] = entity_traits::combine(curr, entity_traits::to_integral(epool[curr])));
353 }
354
355 auto release_entity(const Entity entt, const typename entity_traits::version_type version) {
356 const typename entity_traits::version_type vers = version + (version == entity_traits::to_version(tombstone));
359 return vers;
360 }
361
362 void rebind() {
363 for(auto &&curr: pools) {
364 curr.second->bind(forward_as_any(*this));
365 }
366 }
367
368public:
370 using allocator_type = Allocator;
372 using entity_type = Entity;
376 using size_type = std::size_t;
380 using context = internal::registry_context;
381
385
390 explicit basic_registry(const allocator_type &allocator)
391 : basic_registry{0u, allocator} {}
392
398 basic_registry(const size_type count, const allocator_type &allocator = allocator_type{})
399 : vars{},
400 free_list{tombstone},
401 epool{allocator},
402 pools{allocator},
403 groups{allocator} {
404 pools.reserve(count);
405 }
406
412 : vars{std::move(other.vars)},
413 free_list{std::move(other.free_list)},
414 epool{std::move(other.epool)},
415 pools{std::move(other.pools)},
416 groups{std::move(other.groups)} {
417 rebind();
418 }
419
426 vars = std::move(other.vars);
427 free_list = std::move(other.free_list);
428 epool = std::move(other.epool);
429 pools = std::move(other.pools);
430 groups = std::move(other.groups);
431
432 rebind();
433
434 return *this;
435 }
436
441 void swap(basic_registry &other) {
442 using std::swap;
443 swap(vars, other.vars);
444 swap(free_list, other.free_list);
445 swap(epool, other.epool);
446 swap(pools, other.pools);
447 swap(groups, other.groups);
448
449 rebind();
450 other.rebind();
451 }
452
457 [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
458 return epool.get_allocator();
459 }
460
469 [[nodiscard]] auto storage() noexcept {
470 return iterable_adaptor{internal::registry_storage_iterator{pools.begin()}, internal::registry_storage_iterator{pools.end()}};
471 }
472
474 [[nodiscard]] auto storage() const noexcept {
475 return iterable_adaptor{internal::registry_storage_iterator{pools.cbegin()}, internal::registry_storage_iterator{pools.cend()}};
476 }
477
483 [[nodiscard]] base_type *storage(const id_type id) {
484 return const_cast<base_type *>(std::as_const(*this).storage(id));
485 }
486
492 [[nodiscard]] const base_type *storage(const id_type id) const {
493 const auto it = pools.find(id);
494 return it == pools.cend() ? nullptr : it->second.get();
495 }
496
503 template<typename Type>
504 decltype(auto) storage(const id_type id = type_hash<Type>::value()) {
505 return assure<Type>(id);
506 }
507
519 template<typename Type>
520 decltype(auto) storage(const id_type id = type_hash<Type>::value()) const {
521 return assure<Type>(id);
522 }
523
528 [[nodiscard]] size_type size() const noexcept {
529 return epool.size();
530 }
531
536 [[nodiscard]] size_type alive() const {
537 auto sz = epool.size();
538
539 for(auto curr = free_list; curr != null; --sz) {
540 curr = epool[entity_traits::to_entity(curr)];
541 }
542
543 return sz;
544 }
545
550 void reserve(const size_type cap) {
551 epool.reserve(cap);
552 }
553
559 [[nodiscard]] size_type capacity() const noexcept {
560 return epool.capacity();
561 }
562
567 [[nodiscard]] bool empty() const {
568 return !alive();
569 }
570
583 [[nodiscard]] const entity_type *data() const noexcept {
584 return epool.data();
585 }
586
595 [[nodiscard]] entity_type released() const noexcept {
596 return free_list;
597 }
598
604 [[nodiscard]] bool valid(const entity_type entt) const {
605 const auto pos = size_type(entity_traits::to_entity(entt));
606 return (pos < epool.size() && epool[pos] == entt);
607 }
608
615 [[nodiscard]] version_type current(const entity_type entt) const {
616 const auto pos = size_type(entity_traits::to_entity(entt));
617 return entity_traits::to_version(pos < epool.size() ? epool[pos] : tombstone);
618 }
619
624 [[nodiscard]] entity_type create() {
625 return (free_list == null) ? epool.emplace_back(generate_identifier(epool.size())) : recycle_identifier();
626 }
627
637 [[nodiscard]] entity_type create(const entity_type hint) {
638 const auto length = epool.size();
639
640 if(hint == null || hint == tombstone) {
641 return create();
642 } else if(const auto req = entity_traits::to_entity(hint); !(req < length)) {
643 epool.resize(size_type(req) + 1u, null);
644
645 for(auto pos = length; pos < req; ++pos) {
646 release_entity(generate_identifier(pos), {});
647 }
648
649 return (epool[req] = hint);
650 } else if(const auto curr = entity_traits::to_entity(epool[req]); req == curr) {
651 return create();
652 } else {
653 auto *it = &free_list;
654 for(; entity_traits::to_entity(*it) != req; it = &epool[entity_traits::to_entity(*it)]) {}
656 return (epool[req] = hint);
657 }
658 }
659
669 template<typename It>
670 void create(It first, It last) {
671 for(; free_list != null && first != last; ++first) {
672 *first = recycle_identifier();
673 }
674
675 const auto length = epool.size();
676 epool.resize(length + std::distance(first, last), null);
677
678 for(auto pos = length; first != last; ++first, ++pos) {
679 *first = epool[pos] = generate_identifier(pos);
680 }
681 }
682
700 template<typename It>
701 void assign(It first, It last, const entity_type destroyed) {
702 ENTT_ASSERT(!alive(), "Entities still alive");
703 epool.assign(first, last);
704 free_list = destroyed;
705 }
706
719 return release(entt, static_cast<version_type>(entity_traits::to_version(entt) + 1u));
720 }
721
735 ENTT_ASSERT(valid(entt), "Invalid identifier");
736 ENTT_ASSERT(std::all_of(pools.cbegin(), pools.cend(), [entt](auto &&curr) { return (curr.second->current(entt) == entity_traits::to_version(tombstone)); }), "Non-orphan entity");
737 return release_entity(entt, version);
738 }
739
749 template<typename It>
750 void release(It first, It last) {
751 for(; first != last; ++first) {
752 release(*first);
753 }
754 }
755
770 return destroy(entt, static_cast<version_type>(entity_traits::to_version(entt) + 1u));
771 }
772
786 for(size_type pos = pools.size(); pos; --pos) {
787 pools.begin()[pos - 1u].second->remove(entt);
788 }
789
790 return release(entt, version);
791 }
792
802 template<typename It>
803 void destroy(It first, It last) {
804 for(; first != last; ++first) {
805 destroy(*first);
806 }
807 }
808
824 template<typename Type, typename... Args>
825 decltype(auto) emplace(const entity_type entt, Args &&...args) {
826 return assure<Type>().emplace(entt, std::forward<Args>(args)...);
827 }
828
840 template<typename Type, typename It>
841 void insert(It first, It last, const Type &value = {}) {
842 assure<Type>().insert(first, last, value);
843 }
844
857 template<typename Type, typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, Type>>>
858 void insert(EIt first, EIt last, CIt from) {
859 assure<Type>().insert(first, last, from);
860 }
861
874 template<typename Type, typename... Args>
875 decltype(auto) emplace_or_replace(const entity_type entt, Args &&...args) {
876 if(auto &cpool = assure<Type>(); cpool.contains(entt)) {
877 return cpool.patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward<Args>(args)...}), ...); });
878 } else {
879 return cpool.emplace(entt, std::forward<Args>(args)...);
880 }
881 }
882
907 template<typename Type, typename... Func>
908 decltype(auto) patch(const entity_type entt, Func &&...func) {
909 return assure<Type>().patch(entt, std::forward<Func>(func)...);
910 }
911
927 template<typename Type, typename... Args>
928 decltype(auto) replace(const entity_type entt, Args &&...args) {
929 return patch<Type>(entt, [&args...](auto &...curr) { ((curr = Type{std::forward<Args>(args)...}), ...); });
930 }
931
940 template<typename Type, typename... Other>
942 return (assure<Type>().remove(entt) + ... + assure<Other>().remove(entt));
943 }
944
957 template<typename Type, typename... Other, typename It>
958 size_type remove(It first, It last) {
959 if constexpr(sizeof...(Other) == 0u) {
960 return assure<Type>().remove(std::move(first), std::move(last));
961 } else {
962 size_type count{};
963
964 for(auto cpools = std::forward_as_tuple(assure<Type>(), assure<Other>()...); first != last; ++first) {
965 count += std::apply([entt = *first](auto &...curr) { return (curr.remove(entt) + ... + 0u); }, cpools);
966 }
967
968 return count;
969 }
970 }
971
983 template<typename Type, typename... Other>
984 void erase(const entity_type entt) {
985 (assure<Type>().erase(entt), (assure<Other>().erase(entt), ...));
986 }
987
999 template<typename Type, typename... Other, typename It>
1000 void erase(It first, It last) {
1001 if constexpr(sizeof...(Other) == 0u) {
1002 assure<Type>().erase(std::move(first), std::move(last));
1003 } else {
1004 for(auto cpools = std::forward_as_tuple(assure<Type>(), assure<Other>()...); first != last; ++first) {
1005 std::apply([entt = *first](auto &...curr) { (curr.erase(entt), ...); }, cpools);
1006 }
1007 }
1008 }
1009
1015 template<typename... Type>
1016 void compact() {
1017 if constexpr(sizeof...(Type) == 0) {
1018 for(auto &&curr: pools) {
1019 curr.second->compact();
1020 }
1021 } else {
1022 (assure<Type>().compact(), ...);
1023 }
1024 }
1025
1032 template<typename... Type>
1033 [[nodiscard]] bool all_of(const entity_type entt) const {
1034 return (assure<std::remove_const_t<Type>>().contains(entt) && ...);
1035 }
1036
1044 template<typename... Type>
1045 [[nodiscard]] bool any_of(const entity_type entt) const {
1046 return (assure<std::remove_const_t<Type>>().contains(entt) || ...);
1047 }
1048
1060 template<typename... Type>
1061 [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const {
1062 if constexpr(sizeof...(Type) == 1u) {
1063 return (assure<std::remove_const_t<Type>>().get(entt), ...);
1064 } else {
1065 return std::forward_as_tuple(get<Type>(entt)...);
1066 }
1067 }
1068
1070 template<typename... Type>
1071 [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) {
1072 if constexpr(sizeof...(Type) == 1u) {
1073 return (const_cast<Type &>(std::as_const(*this).template get<Type>(entt)), ...);
1074 } else {
1075 return std::forward_as_tuple(get<Type>(entt)...);
1076 }
1077 }
1078
1094 template<typename Type, typename... Args>
1095 [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entt, Args &&...args) {
1096 if(auto &cpool = assure<Type>(); cpool.contains(entt)) {
1097 return cpool.get(entt);
1098 } else {
1099 return cpool.emplace(entt, std::forward<Args>(args)...);
1100 }
1101 }
1102
1113 template<typename... Type>
1114 [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) const {
1115 if constexpr(sizeof...(Type) == 1) {
1116 const auto &cpool = assure<std::remove_const_t<Type>...>();
1117 return cpool.contains(entt) ? std::addressof(cpool.get(entt)) : nullptr;
1118 } else {
1119 return std::make_tuple(try_get<Type>(entt)...);
1120 }
1121 }
1122
1124 template<typename... Type>
1125 [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) {
1126 if constexpr(sizeof...(Type) == 1) {
1127 return (const_cast<Type *>(std::as_const(*this).template try_get<Type>(entt)), ...);
1128 } else {
1129 return std::make_tuple(try_get<Type>(entt)...);
1130 }
1131 }
1132
1137 template<typename... Type>
1138 void clear() {
1139 if constexpr(sizeof...(Type) == 0) {
1140 for(auto &&curr: pools) {
1141 curr.second->clear();
1142 }
1143
1144 each([this](const auto entity) { this->release(entity); });
1145 } else {
1146 (assure<Type>().clear(), ...);
1147 }
1148 }
1149
1164 template<typename Func>
1165 void each(Func func) const {
1166 if(free_list == null) {
1167 for(auto pos = epool.size(); pos; --pos) {
1168 func(epool[pos - 1]);
1169 }
1170 } else {
1171 for(auto pos = epool.size(); pos; --pos) {
1172 if(const auto entity = epool[pos - 1]; entity_traits::to_entity(entity) == (pos - 1)) {
1173 func(entity);
1174 }
1175 }
1176 }
1177 }
1178
1184 [[nodiscard]] bool orphan(const entity_type entt) const {
1185 return std::none_of(pools.cbegin(), pools.cend(), [entt](auto &&curr) { return curr.second->contains(entt); });
1186 }
1187
1206 template<typename Type>
1207 [[nodiscard]] auto on_construct() {
1208 return assure<Type>().on_construct();
1209 }
1210
1229 template<typename Type>
1230 [[nodiscard]] auto on_update() {
1231 return assure<Type>().on_update();
1232 }
1233
1252 template<typename Type>
1253 [[nodiscard]] auto on_destroy() {
1254 return assure<Type>().on_destroy();
1255 }
1256
1270 template<typename Type, typename... Other, typename... Exclude>
1271 [[nodiscard]] basic_view<get_t<storage_for_type<const Type>, storage_for_type<const Other>...>, exclude_t<storage_for_type<const Exclude>...>>
1273 return {assure<std::remove_const_t<Type>>(), assure<std::remove_const_t<Other>>()..., assure<std::remove_const_t<Exclude>>()...};
1274 }
1275
1277 template<typename Type, typename... Other, typename... Exclude>
1278 [[nodiscard]] basic_view<get_t<storage_for_type<Type>, storage_for_type<Other>...>, exclude_t<storage_for_type<Exclude>...>>
1280 return {assure<std::remove_const_t<Type>>(), assure<std::remove_const_t<Other>>()..., assure<std::remove_const_t<Exclude>>()...};
1281 }
1282
1307 template<typename... Owned, typename... Get, typename... Exclude>
1308 [[nodiscard]] basic_group<owned_t<storage_for_type<Owned>...>, get_t<storage_for_type<Get>...>, exclude_t<storage_for_type<Exclude>...>>
1309 group(get_t<Get...> = {}, exclude_t<Exclude...> = {}) {
1310 static_assert(sizeof...(Owned) + sizeof...(Get) > 0, "Exclusion-only groups are not supported");
1311 static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1, "Single component groups are not allowed");
1312
1313 using handler_type = group_handler<exclude_t<std::remove_const_t<Exclude>...>, get_t<std::remove_const_t<Get>...>, std::remove_const_t<Owned>...>;
1314
1315 const auto cpools = std::forward_as_tuple(assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()...);
1316 constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude);
1317 handler_type *handler = nullptr;
1318
1319 auto it = std::find_if(groups.cbegin(), groups.cend(), [size](const auto &gdata) {
1320 return gdata.size == size
1321 && (gdata.owned(type_hash<std::remove_const_t<Owned>>::value()) && ...)
1322 && (gdata.get(type_hash<std::remove_const_t<Get>>::value()) && ...)
1323 && (gdata.exclude(type_hash<std::remove_const_t<Exclude>>::value()) && ...);
1324 });
1325
1326 if(it != groups.cend()) {
1327 handler = static_cast<handler_type *>(it->group.get());
1328 } else {
1329 group_data candidate = {
1330 size,
1331 std::apply([this](auto &&...args) { return std::allocate_shared<handler_type>(get_allocator(), std::forward<decltype(args)>(args)...); }, entt::uses_allocator_construction_args<typename handler_type::value_type>(get_allocator())),
1332 []([[maybe_unused]] const id_type ctype) noexcept { return ((ctype == type_hash<std::remove_const_t<Owned>>::value()) || ...); },
1333 []([[maybe_unused]] const id_type ctype) noexcept { return ((ctype == type_hash<std::remove_const_t<Get>>::value()) || ...); },
1334 []([[maybe_unused]] const id_type ctype) noexcept { return ((ctype == type_hash<std::remove_const_t<Exclude>>::value()) || ...); },
1335 };
1336
1337 handler = static_cast<handler_type *>(candidate.group.get());
1338
1339 const void *maybe_valid_if = nullptr;
1340 const void *discard_if = nullptr;
1341
1342 if constexpr(sizeof...(Owned) == 0) {
1343 groups.push_back(std::move(candidate));
1344 } else {
1345 [[maybe_unused]] auto has_conflict = [size](const auto &gdata) {
1346 const auto overlapping = (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value()));
1347 const auto sz = overlapping + (0u + ... + gdata.get(type_hash<std::remove_const_t<Get>>::value())) + (0u + ... + gdata.exclude(type_hash<std::remove_const_t<Exclude>>::value()));
1348 return !overlapping || ((sz == size) || (sz == gdata.size));
1349 };
1350
1351 ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), std::move(has_conflict)), "Conflicting groups");
1352
1353 const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [size](const auto &gdata) {
1354 return !(0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value())) || (size > gdata.size);
1355 });
1356
1357 const auto prev = std::find_if(std::make_reverse_iterator(next), groups.crend(), [](const auto &gdata) {
1358 return (0u + ... + gdata.owned(type_hash<std::remove_const_t<Owned>>::value()));
1359 });
1360
1361 maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get());
1362 discard_if = (prev == groups.crend() ? discard_if : prev->group.get());
1363 groups.insert(next, std::move(candidate));
1364 }
1365
1366 (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), ...);
1367 (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), ...);
1368 (on_destroy<std::remove_const_t<Exclude>>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if<std::remove_const_t<Exclude>>>(*handler), ...);
1369
1370 (on_destroy<std::remove_const_t<Owned>>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...);
1371 (on_destroy<std::remove_const_t<Get>>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...);
1372 (on_construct<std::remove_const_t<Exclude>>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...);
1373
1374 if constexpr(sizeof...(Owned) == 0) {
1375 for(const auto entity: view<Owned..., Get...>(exclude<Exclude...>)) {
1376 handler->current.emplace(entity);
1377 }
1378 } else {
1379 // we cannot iterate backwards because we want to leave behind valid entities in case of owned types
1380 for(auto *first = std::get<0>(cpools).data(), *last = first + std::get<0>(cpools).size(); first != last; ++first) {
1381 handler->template maybe_valid_if<type_list_element_t<0, type_list<std::remove_const_t<Owned>...>>>(*this, *first);
1382 }
1383 }
1384 }
1385
1386 return {handler->current, std::get<storage_for_type<std::remove_const_t<Owned>> &>(cpools)..., std::get<storage_for_type<std::remove_const_t<Get>> &>(cpools)...};
1387 }
1388
1390 template<typename... Owned, typename... Get, typename... Exclude>
1391 [[nodiscard]] basic_group<owned_t<storage_for_type<const Owned>...>, get_t<storage_for_type<const Get>...>, exclude_t<storage_for_type<const Exclude>...>>
1392 group_if_exists(get_t<Get...> = {}, exclude_t<Exclude...> = {}) const {
1393 auto it = std::find_if(groups.cbegin(), groups.cend(), [](const auto &gdata) {
1394 return gdata.size == (sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude))
1395 && (gdata.owned(type_hash<std::remove_const_t<Owned>>::value()) && ...)
1396 && (gdata.get(type_hash<std::remove_const_t<Get>>::value()) && ...)
1397 && (gdata.exclude(type_hash<std::remove_const_t<Exclude>>::value()) && ...);
1398 });
1399
1400 if(it == groups.cend()) {
1401 return {};
1402 } else {
1403 using handler_type = group_handler<exclude_t<std::remove_const_t<Exclude>...>, get_t<std::remove_const_t<Get>...>, std::remove_const_t<Owned>...>;
1404 return {static_cast<handler_type *>(it->group.get())->current, assure<std::remove_const_t<Owned>>()..., assure<std::remove_const_t<Get>>()...};
1405 }
1406 }
1407
1414 template<typename... Type>
1415 [[nodiscard]] bool owned() const {
1416 return std::any_of(groups.cbegin(), groups.cend(), [](auto &&gdata) { return (gdata.owned(type_hash<std::remove_const_t<Type>>::value()) || ...); });
1417 }
1418
1426 template<typename... Owned, typename... Get, typename... Exclude>
1428 constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude);
1429 auto pred = [size](const auto &gdata) { return (0u + ... + gdata.owned(type_hash<typename Owned::value_type>::value())) && (size < gdata.size); };
1430 return std::find_if(groups.cbegin(), groups.cend(), std::move(pred)) == groups.cend();
1431 }
1432
1468 template<typename Type, typename Compare, typename Sort = std_sort, typename... Args>
1469 void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
1470 ENTT_ASSERT(!owned<Type>(), "Cannot sort owned storage");
1471 auto &cpool = assure<Type>();
1472
1473 if constexpr(std::is_invocable_v<Compare, decltype(cpool.get({})), decltype(cpool.get({}))>) {
1474 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))); };
1475 cpool.sort(std::move(comp), std::move(algo), std::forward<Args>(args)...);
1476 } else {
1477 cpool.sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
1478 }
1479 }
1480
1500 template<typename To, typename From>
1501 void sort() {
1502 ENTT_ASSERT(!owned<To>(), "Cannot sort owned storage");
1503 assure<To>().respect(assure<From>());
1504 }
1505
1510 context &ctx() noexcept {
1511 return vars;
1512 }
1513
1515 const context &ctx() const noexcept {
1516 return vars;
1517 }
1518
1519private:
1520 context vars;
1521 entity_type free_list;
1522 std::vector<entity_type, allocator_type> epool;
1523 // std::shared_ptr because of its type erased allocator which is useful here
1524 dense_map<id_type, std::shared_ptr<base_type>, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, std::shared_ptr<base_type>>>> pools;
1525 std::vector<group_data, typename alloc_traits::template rebind_alloc<group_data>> groups;
1526};
1527
1528} // namespace entt
1529
1530#endif
Group.
Definition: fwd.hpp:39
Fast and reliable entity-component system.
Definition: registry.hpp:253
decltype(auto) storage(const id_type id=type_hash< Type >::value())
Returns the storage for a given component type.
Definition: registry.hpp:504
context & ctx() noexcept
Returns the context object, that is, a general purpose container.
Definition: registry.hpp:1510
bool any_of(const entity_type entt) const
Check if an entity is part of at least one given storage.
Definition: registry.hpp:1045
bool owned() const
Checks whether the given components belong to any group.
Definition: registry.hpp:1415
auto on_destroy()
Returns a sink object for the given component.
Definition: registry.hpp:1253
decltype(auto) get_or_emplace(const entity_type entt, Args &&...args)
Returns a reference to the given component for an entity.
Definition: registry.hpp:1095
bool orphan(const entity_type entt) const
Checks if an entity has components assigned.
Definition: registry.hpp:1184
decltype(auto) replace(const entity_type entt, Args &&...args)
Replaces the given component for an entity.
Definition: registry.hpp:928
basic_registry(const size_type count, const allocator_type &allocator=allocator_type{})
Allocates enough memory upon construction to store count pools.
Definition: registry.hpp:398
typename entity_traits::version_type version_type
Underlying version type.
Definition: registry.hpp:374
auto try_get(const entity_type entt) const
Returns pointers to the given components for an entity.
Definition: registry.hpp:1114
decltype(auto) emplace_or_replace(const entity_type entt, Args &&...args)
Assigns or replaces the given component for an entity.
Definition: registry.hpp:875
auto on_construct()
Returns a sink object for the given component.
Definition: registry.hpp:1207
bool valid(const entity_type entt) const
Checks if an identifier refers to a valid entity.
Definition: registry.hpp:604
entity_type released() const noexcept
Returns the head of the list of released entities.
Definition: registry.hpp:595
bool all_of(const entity_type entt) const
Check if an entity is part of all the given storage.
Definition: registry.hpp:1033
void each(Func func) const
Iterates all the entities that are still in use.
Definition: registry.hpp:1165
version_type destroy(const entity_type entt)
Destroys an entity and releases its identifier.
Definition: registry.hpp:769
entity_type create()
Creates a new entity or recycles a destroyed one.
Definition: registry.hpp:624
const context & ctx() const noexcept
Returns the context object, that is, a general purpose container.
Definition: registry.hpp:1515
void sort()
Sorts two pools of components in the same way.
Definition: registry.hpp:1501
version_type destroy(const entity_type entt, const version_type version)
Destroys an entity and releases its identifier.
Definition: registry.hpp:785
size_type size() const noexcept
Returns the number of entities created so far.
Definition: registry.hpp:528
internal::registry_context context
Context type.
Definition: registry.hpp:380
auto on_update()
Returns a sink object for the given component.
Definition: registry.hpp:1230
void create(It first, It last)
Assigns each element in a range an identifier.
Definition: registry.hpp:670
size_type remove(It first, It last)
Removes the given components from all the entities in a range.
Definition: registry.hpp:958
basic_group< owned_t< storage_for_type< Owned >... >, get_t< storage_for_type< Get >... >, exclude_t< storage_for_type< Exclude >... > > group(get_t< Get... >={}, exclude_t< Exclude... >={})
Returns a group for the given components.
Definition: registry.hpp:1309
entity_type create(const entity_type hint)
Creates a new entity or recycles a destroyed one.
Definition: registry.hpp:637
void clear()
Clears a whole registry or the pools for the given components.
Definition: registry.hpp:1138
size_type alive() const
Returns the number of entities still in use.
Definition: registry.hpp:536
basic_view< get_t< storage_for_type< const Type >, storage_for_type< const Other >... >, exclude_t< storage_for_type< const Exclude >... > > view(exclude_t< Exclude... >={}) const
Returns a view for the given components.
Definition: registry.hpp:1272
auto storage() noexcept
Returns an iterable object to use to visit a registry.
Definition: registry.hpp:469
decltype(auto) patch(const entity_type entt, Func &&...func)
Patches the given component for an entity.
Definition: registry.hpp:908
size_type remove(const entity_type entt)
Removes the given components from an entity.
Definition: registry.hpp:941
auto storage() const noexcept
Returns an iterable object to use to visit a registry.
Definition: registry.hpp:474
basic_registry & operator=(basic_registry &&other) noexcept
Move assignment operator.
Definition: registry.hpp:425
auto try_get(const entity_type entt)
Returns pointers to the given components for an entity.
Definition: registry.hpp:1125
bool empty() const
Checks whether the registry is empty (no entities still in use).
Definition: registry.hpp:567
void destroy(It first, It last)
Destroys all entities in a range and releases their identifiers.
Definition: registry.hpp:803
decltype(auto) get(const entity_type entt)
Returns references to the given components for an entity.
Definition: registry.hpp:1071
void swap(basic_registry &other)
Exchanges the contents with those of a given registry.
Definition: registry.hpp:441
version_type release(const entity_type entt, const version_type version)
Releases an identifier.
Definition: registry.hpp:734
void erase(It first, It last)
Erases the given components from all the entities in a range.
Definition: registry.hpp:1000
basic_view< get_t< storage_for_type< Type >, storage_for_type< Other >... >, exclude_t< storage_for_type< Exclude >... > > view(exclude_t< Exclude... >={})
Returns a view for the given components.
Definition: registry.hpp:1279
Allocator allocator_type
Allocator type.
Definition: registry.hpp:370
void sort(Compare compare, Sort algo=Sort{}, Args &&...args)
Sorts the elements of a given component.
Definition: registry.hpp:1469
constexpr allocator_type get_allocator() const noexcept
Returns the associated allocator.
Definition: registry.hpp:457
decltype(auto) emplace(const entity_type entt, Args &&...args)
Assigns the given component to an entity.
Definition: registry.hpp:825
basic_registry(const allocator_type &allocator)
Constructs an empty registry with a given allocator.
Definition: registry.hpp:390
size_type capacity() const noexcept
Returns the number of entities that a registry has currently allocated space for.
Definition: registry.hpp:559
decltype(auto) get(const entity_type entt) const
Returns references to the given components for an entity.
Definition: registry.hpp:1061
std::size_t size_type
Unsigned integer type.
Definition: registry.hpp:376
basic_registry(basic_registry &&other) noexcept
Move constructor.
Definition: registry.hpp:411
void compact()
Removes all tombstones from a registry or only the pools for the given components.
Definition: registry.hpp:1016
void reserve(const size_type cap)
Increases the capacity (number of entities) of the registry.
Definition: registry.hpp:550
bool sortable(const basic_group< owned_t< Owned... >, get_t< Get... >, exclude_t< Exclude... > > &) noexcept
Checks whether a group can be sorted.
Definition: registry.hpp:1427
decltype(auto) storage(const id_type id=type_hash< Type >::value()) const
Returns the storage for a given component type.
Definition: registry.hpp:520
version_type current(const entity_type entt) const
Returns the actual version for an identifier.
Definition: registry.hpp:615
Entity entity_type
Underlying entity identifier.
Definition: registry.hpp:372
void insert(It first, It last, const Type &value={})
Assigns each entity in a range the given component.
Definition: registry.hpp:841
void insert(EIt first, EIt last, CIt from)
Assigns each entity in a range the given components.
Definition: registry.hpp:858
const base_type * storage(const id_type id) const
Finds the storage associated with a given name, if any.
Definition: registry.hpp:492
const entity_type * data() const noexcept
Direct access to the list of entities of a registry.
Definition: registry.hpp:583
version_type release(const entity_type entt)
Releases an identifier.
Definition: registry.hpp:718
base_type * storage(const id_type id)
Finds the storage associated with a given name, if any.
Definition: registry.hpp:483
void assign(It first, It last, const entity_type destroyed)
Assigns identifiers to an empty registry.
Definition: registry.hpp:701
void release(It first, It last)
Releases all identifiers in a range.
Definition: registry.hpp:750
basic_registry()
Default constructor.
Definition: registry.hpp:383
basic_group< owned_t< storage_for_type< const Owned >... >, get_t< storage_for_type< const Get >... >, exclude_t< storage_for_type< const Exclude >... > > group_if_exists(get_t< Get... >={}, exclude_t< Exclude... >={}) const
Returns a group for the given components.
Definition: registry.hpp:1392
void erase(const entity_type entt)
Erases the given components from an entity.
Definition: registry.hpp:984
Basic sparse set implementation.
Definition: sparse_set.hpp:174
View implementation.
Definition: fwd.hpp:33
Associative container for key-value pairs with unique keys.
Definition: dense_map.hpp:264
Entity traits.
Definition: entity.hpp:62
static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept
Combines two identifiers in a single one.
Definition: entity.hpp:128
typename base_type::entity_type entity_type
Underlying entity type.
Definition: entity.hpp:69
static constexpr entity_type to_entity(const value_type value) noexcept
Returns the entity part once converted to the underlying type.
Definition: entity.hpp:91
static constexpr version_type to_version(const value_type value) noexcept
Returns the version part once converted to the underlying type.
Definition: entity.hpp:100
static constexpr entity_type to_integral(const value_type value) noexcept
Converts an entity to its underlying type.
Definition: entity.hpp:82
static constexpr value_type construct(const entity_type entity, const version_type version) noexcept
Constructs an identifier from its parts.
Definition: entity.hpp:114
typename base_type::version_type version_type
Underlying version type.
Definition: entity.hpp:71
EnTT default namespace.
Definition: dense_map.hpp:21
entity
Default entity identifier.
Definition: fwd.hpp:12
std::uint32_t id_type
Alias declaration for type identifiers.
Definition: fwd.hpp:13
constexpr null_t null
Compile-time constant for null entities.
Definition: entity.hpp:326
constexpr exclude_t< Type... > exclude
Variable template for exclusion lists.
Definition: fwd.hpp:71
constexpr tombstone_t tombstone
Compile-time constant for tombstone entities.
Definition: entity.hpp:335
constexpr get_t< Type... > get
Variable template for lists of observed components.
Definition: fwd.hpp:85
constexpr bool operator<=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr bool operator<(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr type_list< Type..., Other... > operator+(type_list< Type... >, type_list< Other... >)
Concatenates multiple type lists.
constexpr bool operator!=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr bool operator>=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr bool operator>(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
basic_any< Len, Align > forward_as_any(Type &&value)
Forwards its argument and avoids copies for lvalue references.
Definition: any.hpp:504
constexpr bool operator==(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
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:141
Function object to wrap std::sort in a class type.
Definition: algorithm.hpp:21
Type hash.
Definition: type_info.hpp:100
A class to use to push around lists of types, nothing more.