Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
multi_type_map.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <helios/assert.hpp>
6
7#include <algorithm>
8#include <concepts>
9#include <cstddef>
10#include <memory>
11#include <memory_resource>
12#include <type_traits>
13#include <utility>
14
15#ifdef HELIOS_STL_FLAT_MAP_AVAILABLE
16#include <flat_map>
17#else
18#include <boost/container/flat_map.hpp>
19#endif
20
21namespace helios::container {
22
23/**
24 * @brief Generic type-indexed map that stores one `Storage` instance per
25 * registered type key.
26 * @details Uses a flat_map keyed by `TypeIndex` to store `Storage` instances.
27 * Each type gets its own storage entry identified by its compile-time type
28 * index. Type identification uses `helios::utils::TypeIndex`.
29 *
30 * This class models a *map structure* — use `Ensure<T>()` / `Get<T>()` to
31 * obtain the underlying `Storage` and interact with individual entries
32 * directly.
33 *
34 * If `Storage` supports `ChangeType<T>()`, it is called automatically on
35 * `Ensure<T>()`. If `Storage` supports `Merge(Storage&&)` or
36 * `merge(Storage&&)`, it is called during `Merge()` for overlapping type
37 * entries.
38 *
39 * @tparam Storage The value type stored per type key. Must be
40 * default-constructible.
41 * @tparam Allocator The allocator type for the underlying flat_map (default:
42 * `std::allocator<std::byte>`)
43 */
44template <typename Storage, typename Allocator = std::allocator<std::byte>>
46public:
48
49 using size_type = size_t;
50 using allocator_type = Allocator;
51
52private:
53#ifdef HELIOS_STL_FLAT_MAP_AVAILABLE
54 using KeyContainer =
55 std::vector<TypeIndex, typename std::allocator_traits<allocator_type>::
56 template rebind_alloc<TypeIndex>>;
57 using MappedContainer =
58 std::vector<Storage, typename std::allocator_traits<
59 allocator_type>::template rebind_alloc<Storage>>;
60
61 using MapType = std::flat_map<TypeIndex, Storage, std::less<TypeIndex>,
62 KeyContainer, MappedContainer>;
63#else
64 using MapValueType = std::pair<TypeIndex, Storage>;
65 using MapAllocator = typename std::allocator_traits<
66 allocator_type>::template rebind_alloc<MapValueType>;
67
68 using MapType =
69 boost::container::flat_map<TypeIndex, Storage, std::less<TypeIndex>,
70 MapAllocator>;
71#endif
72
73public:
74 constexpr MultiTypeMap() = default;
75
76#ifdef HELIOS_STL_FLAT_MAP_AVAILABLE
77 /**
78 * @brief Constructs with a custom allocator.
79 * @param alloc Allocator instance to use
80 */
81 explicit constexpr MultiTypeMap(const allocator_type& alloc)
82 : storage_(alloc), allocator_(alloc) {}
83#else
84 /**
85 * @brief Constructs with a custom allocator.
86 * @param alloc Allocator instance to use
87 */
88 explicit constexpr MultiTypeMap(const allocator_type& alloc)
89 : storage_(MapAllocator(alloc)), allocator_(alloc) {}
90#endif
91
92 /**
93 * @brief Constructs with a PMR memory resource.
94 * @details Enabled only when `allocator_type` is constructible from
95 * `std::pmr::memory_resource*`.
96 * @param resource Memory resource used to construct allocator
97 */
98 explicit constexpr MultiTypeMap(std::pmr::memory_resource* resource) noexcept(
99 std::is_nothrow_constructible_v<allocator_type,
100 std::pmr::memory_resource*>)
101 requires std::constructible_from<allocator_type, std::pmr::memory_resource*>
102 : MultiTypeMap(allocator_type{resource}) {}
103
104 MultiTypeMap(std::nullptr_t) = delete;
105
106 constexpr MultiTypeMap(const MultiTypeMap& other) = default;
107 constexpr MultiTypeMap(MultiTypeMap&& other) noexcept = default;
108 constexpr ~MultiTypeMap() = default;
109
110 constexpr MultiTypeMap& operator=(const MultiTypeMap& other) = default;
111 constexpr MultiTypeMap& operator=(MultiTypeMap&& other) noexcept = default;
112
113 /**
114 * @brief Clears the Storage for type `T` (calls `Storage::Clear()` or
115 * `Storage::clear()` if available).
116 * @tparam T The type key to clear
117 */
118 template <typename T>
119 constexpr void Clear() noexcept {
121 }
122
123 /**
124 * @brief Clears the Storage for the given type index.
125 * @param index The type index to clear
126 */
127 constexpr void Clear(TypeIndex index) noexcept;
128
129 /// @brief Clears all per-type storages (calls `Clear()`/`clear()` on each
130 /// entry if available).
131 constexpr void ClearAll() noexcept;
132
133 /**
134 * @brief Resets (removes) the Storage entry for type `T`.
135 * @tparam T The type key to reset
136 */
137 template <typename T>
138 constexpr void Reset() noexcept {
139 storage_.erase(TypeIndexOf<T>());
140 }
141
142 /**
143 * @brief Resets (removes) the Storage entry for the given type index.
144 * @param index The type index to reset
145 */
146 constexpr void Reset(TypeIndex index) noexcept { storage_.erase(index); }
147
148 /// @brief Removes all Storage entries from the map.
149 constexpr void ResetAll() noexcept { storage_.clear(); }
150
151 /**
152 * @brief Creates or replaces Storage for type `T` with the given value.
153 * @details The type key is derived from `T`
154 * @tparam T The type key — `TypeIndexOf<T>` is used as the map key
155 * @tparam Args The argument types for constructing Storage
156 * @param args The arguments to construct the Storage value
157 * (perfect-forwarded)
158 * @return Pair of iterator to the entry and bool — `true` if a new entry was
159 * inserted
160 */
161 template <typename T, typename... Args>
162 requires std::constructible_from<Storage, Args...>
163 constexpr auto Emplace(Args&&... args) {
164 return storage_.emplace(TypeIndexOf<T>(), std::forward<Args>(args)...);
165 }
166
167 /**
168 * @brief Creates Storage for type `T` with the given value, only if no entry
169 * for `T` already exists.
170 * @details The type key is derived from `T`
171 * Unlike `Emplace`, this never overwrites an existing entry.
172 * @tparam T The type key — `TypeIndexOf<T>` is used as the map key
173 * @tparam Args The argument types for constructing Storage
174 * @param args The arguments to construct the Storage value
175 * (perfect-forwarded)
176 * @return Pair of iterator to the entry and bool — `true` if a new entry was
177 * inserted
178 */
179 template <typename T, typename... Args>
180 requires std::constructible_from<Storage, Args...>
181 constexpr auto TryEmplace(Args&&... args) {
182 return storage_.try_emplace(TypeIndexOf<T>(), std::forward<Args>(args)...);
183 }
184
185 /**
186 * @brief Removes Storage for type `T` entirely.
187 * @tparam T The type key to remove
188 * @return true if storage was removed
189 */
190 template <typename T>
191 constexpr bool Remove() noexcept {
192 return Remove(TypeIndexOf<T>());
193 }
194
195 /**
196 * @brief Removes Storage for the given type index entirely.
197 * @param index The type index to remove
198 * @return true if storage was removed
199 */
200 constexpr bool Remove(TypeIndex index) noexcept {
201 return storage_.erase(index) > 0;
202 }
203
204 /**
205 * @brief Ensures a Storage entry exists for type `T` and returns a reference
206 * to it.
207 * @details If no entry exists for `T`, a new default-constructed Storage is
208 * inserted. If `Storage` supports `ChangeType<T>()`, it is called on the
209 * newly created entry.
210 * @tparam T The type key to ensure storage for
211 * @return Reference to the Storage for type `T`
212 */
213 template <typename T>
214 constexpr Storage& Ensure();
215
216 /**
217 * @brief Ensures a Storage entry exists for the given type index and returns
218 * a reference to it.
219 * @details If no entry exists, a new default-constructed Storage is inserted.
220 * @param index The type index to ensure storage for
221 * @return Reference to the Storage for the given type index
222 */
223 constexpr Storage& Ensure(TypeIndex index);
224
225 /**
226 * @brief Gets Storage for type `T`.
227 * @warning Triggers assertion if storage for type `T` doesn't exist.
228 * @tparam T The type key to get storage for
229 * @return Reference to the Storage for type `T`
230 */
231 template <typename T>
232 [[nodiscard]] constexpr Storage& Get() noexcept;
233
234 /**
235 * @brief Gets Storage for type `T` (const).
236 * @warning Triggers assertion if storage for type `T` doesn't exist.
237 * @tparam T The type key to get storage for
238 * @return Const reference to the Storage for type `T`
239 */
240 template <typename T>
241 [[nodiscard]] constexpr const Storage& Get() const noexcept;
242
243 /**
244 * @brief Gets Storage for the given type index.
245 * @warning Triggers assertion if storage for the given type index doesn't
246 * exist.
247 * @param index The type index to get storage for
248 * @return Reference to the Storage for the given type index
249 */
250 [[nodiscard]] constexpr Storage& Get(TypeIndex index) noexcept;
251
252 /**
253 * @brief Gets Storage for the given type index (const).
254 * @warning Triggers assertion if storage for the given type index doesn't
255 * exist.
256 * @param index The type index to get storage for
257 * @return Const reference to the Storage for the given type index
258 */
259 [[nodiscard]] constexpr const Storage& Get(TypeIndex index) const noexcept;
260
261 /**
262 * @brief Tries to get Storage for type `T`.
263 * @tparam T The type key to get storage for
264 * @return Pointer to Storage if exists, `nullptr` otherwise
265 */
266 template <typename T>
267 [[nodiscard]] constexpr Storage* TryGet() noexcept {
268 return TryGet(TypeIndexOf<T>());
269 }
270
271 /**
272 * @brief Tries to get Storage for type `T` (const).
273 * @tparam T The type key to get storage for
274 * @return Const pointer to Storage if exists, `nullptr` otherwise
275 */
276 template <typename T>
277 [[nodiscard]] constexpr const Storage* TryGet() const noexcept {
278 return TryGet(TypeIndexOf<T>());
279 }
280
281 /**
282 * @brief Tries to get Storage for the given type index.
283 * @param index The type index to get storage for
284 * @return Pointer to Storage if exists, `nullptr` otherwise
285 */
286 [[nodiscard]] constexpr Storage* TryGet(TypeIndex index) noexcept;
287
288 /**
289 * @brief Tries to get Storage for the given type index (const).
290 * @param index The type index to get storage for
291 * @return Const pointer to Storage if exists, `nullptr` otherwise
292 */
293 [[nodiscard]] constexpr const Storage* TryGet(TypeIndex index) const noexcept;
294
295 /**
296 * @brief Merges all entries from another MultiTypeMap into this one.
297 * @details For each entry in `other`:
298 * - If this map already contains the same type key, calls `Storage::Merge` or
299 * `Storage::merge` on the existing entry if such a method is available.
300 * - Otherwise, the entry from `other` is inserted into this map. For
301 * differing Storage types, a new default Storage is created and
302 * `Merge`/`merge` is attempted on it.
303 *
304 * After merging, `other` is fully cleared (`TypeCount() == 0`).
305 *
306 * @tparam OtherStorage Storage type of the other map (may differ from
307 * Storage)
308 * @tparam OtherAllocator The allocator template of the other map
309 * @param other The map to merge from
310 */
311 template <typename OtherStorage, typename OtherAllocator>
313
314 template <typename OtherStorage, typename OtherAllocator>
316
317 /**
318 * @brief Swaps contents with another MultiTypeMap.
319 * @param other Map to swap with
320 */
321 constexpr void Swap(MultiTypeMap& other) noexcept(
322 std::is_nothrow_swappable_v<MapType> &&
323 std::is_nothrow_swappable_v<allocator_type>) {
324 std::swap(storage_, other.storage_);
325 std::swap(allocator_, other.allocator_);
326 }
327
328 friend constexpr void swap(MultiTypeMap& lhs, MultiTypeMap& rhs) noexcept(
329 std::is_nothrow_swappable_v<MapType> &&
330 std::is_nothrow_swappable_v<allocator_type>) {
331 lhs.Swap(rhs);
332 }
333
334 /**
335 * @brief Checks if a Storage entry exists for type `T`.
336 * @tparam T The type key to check
337 * @return true if an entry exists
338 */
339 template <typename T>
340 [[nodiscard]] constexpr bool Contains() const noexcept {
341 return Contains(TypeIndexOf<T>());
342 }
343
344 /**
345 * @brief Checks if a Storage entry exists for the given type index.
346 * @param index The type index to check
347 * @return true if an entry exists
348 */
349 [[nodiscard]] constexpr bool Contains(TypeIndex index) const noexcept {
350 return storage_.contains(index);
351 }
352
353 /**
354 * @brief Checks if the Storage for type `T` is empty (no elements / no
355 * value).
356 * @details Returns true if the entry doesn't exist, or if Storage supports
357 * `Empty()`/`empty()` and it returns true.
358 * @tparam T The type key to check
359 * @return true if storage is empty or doesn't exist
360 */
361 template <typename T>
362 [[nodiscard]] constexpr bool Empty() const noexcept {
363 return Empty(TypeIndexOf<T>());
364 }
365
366 /**
367 * @brief Checks if the Storage for the given type index is empty.
368 * @param index The type index to check
369 * @return true if storage is empty or doesn't exist
370 */
371 [[nodiscard]] constexpr bool Empty(TypeIndex index) const noexcept;
372
373 /**
374 * @brief Returns true if all per-type storages are empty or if the map has no
375 * entries.
376 * @return true if all storages are empty
377 */
378 [[nodiscard]] constexpr bool EmptyAll() const noexcept;
379
380 /**
381 * @brief Gets the compile-time type index for `T`.
382 * @tparam T The type to get index for
383 * @return Type index
384 */
385 template <typename T>
386 [[nodiscard]] static constexpr TypeIndex TypeIndexOf() noexcept {
388 }
389
390 /**
391 * @brief Returns the total number of elements across all entries.
392 * @details Only meaningful if Storage supports `Size()` or `size()`.
393 * Returns number of map entries otherwise.
394 * @return Total element count (or entry count if Storage has no size method)
395 */
396 [[nodiscard]] constexpr size_type Size() const noexcept;
397
398 /**
399 * @brief Returns the number of elements stored for type `T`.
400 * @details Returns `Storage::Size()` or `Storage::size()` if available, else
401 * 0 if entry exists.
402 * @tparam T The type key to query
403 * @return Element count, 0 if storage doesn't exist
404 */
405 template <typename T>
406 [[nodiscard]] constexpr size_type Size() const noexcept {
407 return Size(TypeIndexOf<T>());
408 }
409
410 /**
411 * @brief Returns the number of elements stored for the given type index.
412 * @param index The type index to query
413 * @return Element count, 0 if storage doesn't exist
414 */
415 [[nodiscard]] constexpr size_type Size(TypeIndex index) const noexcept;
416
417 /**
418 * @brief Returns the number of registered type entries.
419 * @return Number of registered type entries
420 */
421 [[nodiscard]] constexpr size_type TypeCount() const noexcept {
422 return storage_.size();
423 }
424
425 /**
426 * @brief Returns a view of the underlying map (non-const).
427 * @return Reference to the underlying flat_map
428 */
429 [[nodiscard]] constexpr MapType& Data() noexcept { return storage_; }
430
431 /**
432 * @brief Returns a view of the underlying map (const).
433 * @return Const reference to the underlying flat_map
434 */
435 [[nodiscard]] constexpr const MapType& Data() const noexcept {
436 return storage_;
437 }
438
439 /**
440 * @brief Gets the allocator.
441 * @return Copy of the allocator
442 */
443 [[nodiscard]] constexpr allocator_type GetAllocator() const noexcept {
444 return allocator_;
445 }
446
447 /**
448 * @brief Returns an iterator to the beginning of the map entries.
449 * @return Iterator to the beginning of the map entries
450 */
451 [[nodiscard]] constexpr auto begin() noexcept { return storage_.begin(); }
452
453 /**
454 * @brief Returns a const iterator to the beginning of the map entries.
455 * @return Const iterator to the beginning of the map entries
456 */
457 [[nodiscard]] constexpr auto begin() const noexcept {
458 return storage_.begin();
459 }
460
461 /**
462 * @brief Returns a const iterator to the beginning of the map entries.
463 * @return Const iterator to the beginning of the map entries
464 */
465 [[nodiscard]] constexpr auto cbegin() const noexcept {
466 return storage_.cbegin();
467 }
468
469 /**
470 * @brief Returns an iterator to the end of the map entries.
471 * @return Iterator to the end of the map entries
472 */
473 [[nodiscard]] constexpr auto end() noexcept { return storage_.end(); }
474
475 /**
476 * @brief Returns a const iterator to the end of the map entries.
477 * @return Const iterator to the end of the map entries
478 */
479 [[nodiscard]] constexpr auto end() const noexcept { return storage_.end(); }
480
481 /**
482 * @brief Returns a const iterator to the end of the map entries.
483 * @return Const iterator to the end of the map entries
484 */
485 [[nodiscard]] constexpr auto cend() const noexcept { return storage_.cend(); }
486
487private:
488 template <typename OtherStorage, typename OtherA>
489 friend class MultiTypeMap;
490
491 /// @brief Creates new Storage initialized with allocator if constructible
492 /// from it, else default.
493 [[nodiscard]] constexpr Storage MakeStorage() const;
494
495 MapType storage_;
496 [[no_unique_address]] allocator_type allocator_{};
497};
498
499template <typename Storage, typename Allocator>
501 TypeIndex index) noexcept {
502 if (auto* ptr = TryGet(index)) [[likely]] {
503 if constexpr (requires { ptr->Clear(); }) {
504 ptr->Clear();
505 } else if constexpr (requires { ptr->clear(); }) {
506 ptr->clear();
507 }
508 }
509}
510
511template <typename Storage, typename Allocator>
513 for (auto&& [_, storage] : storage_) {
514 if constexpr (requires { storage.Clear(); }) {
515 storage.Clear();
516 } else if constexpr (requires { storage.clear(); }) {
517 storage.clear();
518 }
519 }
520}
521
522template <typename Storage, typename Allocator>
523template <typename T>
525 constexpr auto type_index = TypeIndexOf<T>();
526 const auto it = storage_.find(type_index);
527 if (it == storage_.end()) {
528 const auto [inserted_iter, success] =
529 storage_.emplace(type_index, MakeStorage());
530 HELIOS_ASSERT(success, "Failed to create storage for type '{}'!",
532 return inserted_iter->second;
533 }
534 return it->second;
535}
536
537template <typename Storage, typename Allocator>
539 const auto it = storage_.find(index);
540 if (it == storage_.end()) {
541 const auto [inserted_iter, success] =
542 storage_.emplace(index, MakeStorage());
543 HELIOS_ASSERT(success, "Failed to create storage for type index '{}'!",
544 index.Hash());
545 return inserted_iter->second;
546 }
547 return it->second;
548}
549
550template <typename Storage, typename Allocator>
551template <typename T>
552constexpr Storage& MultiTypeMap<Storage, Allocator>::Get() noexcept {
553 constexpr auto type_index = TypeIndexOf<T>();
554 const auto it = storage_.find(type_index);
555 HELIOS_ASSERT(it != storage_.end(), "Storage for type '{}' does not exist!",
557 return it->second;
558}
559
560template <typename Storage, typename Allocator>
561template <typename T>
563 const noexcept {
564 constexpr auto type_index = TypeIndexOf<T>();
565 const auto it = storage_.find(type_index);
566 HELIOS_ASSERT(it != storage_.end(), "Storage for type '{}' does not exist!",
568 return it->second;
569}
570
571template <typename Storage, typename Allocator>
573 TypeIndex index) noexcept {
574 const auto it = storage_.find(index);
575 HELIOS_ASSERT(it != storage_.end(),
576 "Storage for type index '{}' does not exist!", index.Hash());
577 return it->second;
578}
579
580template <typename Storage, typename Allocator>
582 TypeIndex index) const noexcept {
583 const auto it = storage_.find(index);
584 HELIOS_ASSERT(it != storage_.end(),
585 "Storage for type index '{}' does not exist!", index.Hash());
586 return it->second;
587}
588
589template <typename Storage, typename Allocator>
591 TypeIndex index) noexcept {
592 const auto it = storage_.find(index);
593 return it != storage_.end() ? &it->second : nullptr;
594}
595
596template <typename Storage, typename Allocator>
598 TypeIndex index) const noexcept {
599 const auto it = storage_.find(index);
600 return it != storage_.end() ? &it->second : nullptr;
601}
602
603template <typename Storage, typename Allocator>
604template <typename OtherStorage, typename OtherAllocator>
607 for (const auto& [index, other_storage] : other.storage_) {
608 if (const auto it = storage_.find(index); it != storage_.end()) {
609 // Existing key: prefer const overloads, then fall back to copy+move.
610 if constexpr (requires { it->second.Merge(other_storage); }) {
611 it->second.Merge(other_storage);
612 } else if constexpr (requires { it->second.merge(other_storage); }) {
613 it->second.merge(other_storage);
614 } else if constexpr (std::copy_constructible<OtherStorage> &&
615 requires(OtherStorage copy) {
616 it->second.Merge(std::move(copy));
617 }) {
618 auto copy = other_storage;
619 it->second.Merge(std::move(copy));
620 } else if constexpr (std::copy_constructible<OtherStorage> &&
621 requires(OtherStorage copy) {
622 it->second.merge(std::move(copy));
623 }) {
624 auto copy = other_storage;
625 it->second.merge(std::move(copy));
626 }
627 // else: no merge method available — existing entry is left unchanged.
628 } else {
629 // New key: insert storage.
630 if constexpr (std::same_as<Storage, OtherStorage> &&
631 std::copy_constructible<Storage>) {
632 // Same storage type — copy directly.
633 storage_.emplace(index, other_storage);
634 } else {
635 // Different storage type — create a new default Storage and attempt to
636 // merge into it.
637 auto new_storage = MakeStorage();
638 if constexpr (requires { new_storage.Merge(other_storage); }) {
639 new_storage.Merge(other_storage);
640 } else if constexpr (requires { new_storage.merge(other_storage); }) {
641 new_storage.merge(other_storage);
642 } else if constexpr (std::copy_constructible<OtherStorage> &&
643 requires(OtherStorage copy) {
644 new_storage.Merge(std::move(copy));
645 }) {
646 auto copy = other_storage;
647 new_storage.Merge(std::move(copy));
648 } else if constexpr (std::copy_constructible<OtherStorage> &&
649 requires(OtherStorage copy) {
650 new_storage.merge(std::move(copy));
651 }) {
652 auto copy = other_storage;
653 new_storage.merge(std::move(copy));
654 } else if constexpr (std::constructible_from<Storage,
655 const OtherStorage&>) {
656 new_storage = Storage(other_storage);
657 }
658 storage_.emplace(index, std::move(new_storage));
659 }
660 }
661 }
662}
663
664template <typename Storage, typename Allocator>
665template <typename OtherStorage, typename OtherAllocator>
668 for (auto&& [index, other_storage] : other.storage_) {
669 if (const auto it = storage_.find(index); it != storage_.end()) {
670 // Existing key: call Merge or merge on Storage if available.
671 if constexpr (requires { it->second.Merge(std::move(other_storage)); }) {
672 it->second.Merge(std::move(other_storage));
673 } else if constexpr (requires {
674 it->second.merge(std::move(other_storage));
675 }) {
676 it->second.merge(std::move(other_storage));
677 }
678 // else: no merge method available — existing entry is left unchanged.
679 } else {
680 // New key: insert storage.
681 if constexpr (std::same_as<Storage, OtherStorage>) {
682 // Same storage type — move directly.
683 storage_.emplace(index, std::move(other_storage));
684 } else {
685 // Different storage type — create a new default Storage and attempt to
686 // merge into it.
687 auto new_storage = MakeStorage();
688 if constexpr (requires {
689 new_storage.Merge(std::move(other_storage));
690 }) {
691 new_storage.Merge(std::move(other_storage));
692 } else if constexpr (requires {
693 new_storage.merge(std::move(other_storage));
694 }) {
695 new_storage.merge(std::move(other_storage));
696 } else if constexpr (std::constructible_from<Storage, OtherStorage&&>) {
697 new_storage = Storage(std::move(other_storage));
698 }
699 storage_.emplace(index, std::move(new_storage));
700 }
701 }
702 }
703 other.storage_.clear();
704}
705
706template <typename Storage, typename Allocator>
708 TypeIndex index) const noexcept {
709 const auto* ptr = TryGet(index);
710 if (ptr == nullptr) {
711 return true;
712 }
713 if constexpr (requires { ptr->Empty(); }) {
714 return ptr->Empty();
715 } else if constexpr (requires { ptr->empty(); }) {
716 return ptr->empty();
717 } else {
718 return false;
719 }
720}
721
722template <typename Storage, typename Allocator>
723constexpr bool MultiTypeMap<Storage, Allocator>::EmptyAll() const noexcept {
724 return std::ranges::all_of(storage_, [](const auto& entry) {
725 if constexpr (requires { entry.second.Empty(); }) {
726 return entry.second.Empty();
727 } else if constexpr (requires { entry.second.empty(); }) {
728 return entry.second.empty();
729 } else {
730 return true;
731 }
732 });
733}
734
735template <typename Storage, typename Allocator>
736constexpr auto MultiTypeMap<Storage, Allocator>::Size() const noexcept
737 -> size_type {
738 size_type total = 0;
739 for (const auto& [_, storage] : storage_) {
740 if constexpr (requires { storage.Size(); }) {
741 total += storage.Size();
742 } else if constexpr (requires { storage.size(); }) {
743 total += storage.size();
744 } else {
745 ++total;
746 }
747 }
748 return total;
749}
750
751template <typename Storage, typename Allocator>
753 TypeIndex index) const noexcept -> size_type {
754 const auto* ptr = TryGet(index);
755 if (ptr == nullptr) {
756 return 0;
757 }
758 if constexpr (requires { ptr->Size(); }) {
759 return ptr->Size();
760 } else if constexpr (requires { ptr->size(); }) {
761 return ptr->size();
762 } else {
763 return 0;
764 }
765}
766
767template <typename Storage, typename Allocator>
768constexpr auto MultiTypeMap<Storage, Allocator>::MakeStorage() const
769 -> Storage {
770 if constexpr (std::constructible_from<Storage, allocator_type>) {
771 return Storage(allocator_);
772 } else {
773 return Storage{};
774 }
775}
776
777template <typename Storage>
780
781} // namespace helios::container
#define HELIOS_ASSERT(condition,...)
Assertion macro that aborts execution in debug builds.
Definition assert.hpp:259
Generic type-indexed map that stores one Storage instance per registered type key.
constexpr const Storage * TryGet() const noexcept
Tries to get Storage for type T (const).
constexpr void Merge(const MultiTypeMap< OtherStorage, OtherAllocator > &other)
Merges all entries from another MultiTypeMap into this one.
constexpr MultiTypeMap(MultiTypeMap &&other) noexcept=default
constexpr Storage * TryGet(TypeIndex index) noexcept
Tries to get Storage for the given type index.
constexpr bool Contains(TypeIndex index) const noexcept
Checks if a Storage entry exists for the given type index.
friend constexpr void swap(MultiTypeMap &lhs, MultiTypeMap &rhs) noexcept(std::is_nothrow_swappable_v< MapType > &&std::is_nothrow_swappable_v< allocator_type >)
constexpr auto cbegin() const noexcept
Returns a const iterator to the beginning of the map entries.
constexpr MultiTypeMap & operator=(const MultiTypeMap &other)=default
constexpr auto begin() noexcept
Returns an iterator to the beginning of the map entries.
constexpr bool Remove(TypeIndex index) noexcept
Removes Storage for the given type index entirely.
constexpr Storage & Ensure(TypeIndex index)
Ensures a Storage entry exists for the given type index and returns a reference to it.
constexpr const Storage * TryGet(TypeIndex index) const noexcept
Tries to get Storage for the given type index (const).
constexpr void Merge(MultiTypeMap< OtherStorage, OtherAllocator > &&other)
constexpr auto TryEmplace(Args &&... args)
Creates Storage for type T with the given value, only if no entry for T already exists.
constexpr void ResetAll() noexcept
Removes all Storage entries from the map.
constexpr ~MultiTypeMap()=default
constexpr Storage & Get() noexcept
Gets Storage for type T.
constexpr const MapType & Data() const noexcept
Returns a view of the underlying map (const).
constexpr auto end() const noexcept
Returns a const iterator to the end of the map entries.
constexpr auto Emplace(Args &&... args)
Creates or replaces Storage for type T with the given value.
constexpr bool Empty() const noexcept
Checks if the Storage for type T is empty (no elements / no value).
constexpr auto end() noexcept
Returns an iterator to the end of the map entries.
constexpr bool Empty(TypeIndex index) const noexcept
Checks if the Storage for the given type index is empty.
constexpr auto cend() const noexcept
Returns a const iterator to the end of the map entries.
constexpr bool Remove() noexcept
Removes Storage for type T entirely.
constexpr auto begin() const noexcept
Returns a const iterator to the beginning of the map entries.
constexpr void Clear() noexcept
Clears the Storage for type T (calls Storage::Clear() or Storage::clear() if available).
constexpr void Clear(TypeIndex index) noexcept
Clears the Storage for the given type index.
constexpr MultiTypeMap(std::pmr::memory_resource *resource) noexcept(std::is_nothrow_constructible_v< allocator_type, std::pmr::memory_resource * >)
Constructs with a PMR memory resource.
MultiTypeMap(std::nullptr_t)=delete
constexpr size_type Size(TypeIndex index) const noexcept
Returns the number of elements stored for the given type index.
constexpr MultiTypeMap & operator=(MultiTypeMap &&other) noexcept=default
constexpr void Swap(MultiTypeMap &other) noexcept(std::is_nothrow_swappable_v< MapType > &&std::is_nothrow_swappable_v< allocator_type >)
Swaps contents with another MultiTypeMap.
constexpr MultiTypeMap(const MultiTypeMap &other)=default
constexpr size_type Size() const noexcept
Returns the total number of elements across all entries.
constexpr allocator_type GetAllocator() const noexcept
Gets the allocator.
constexpr Storage & Ensure()
Ensures a Storage entry exists for type T and returns a reference to it.
constexpr size_type TypeCount() const noexcept
Returns the number of registered type entries.
constexpr void Reset(TypeIndex index) noexcept
Resets (removes) the Storage entry for the given type index.
constexpr bool EmptyAll() const noexcept
Returns true if all per-type storages are empty or if the map has no entries.
constexpr void ClearAll() noexcept
Clears all per-type storages (calls Clear()/clear() on each entry if available).
constexpr MultiTypeMap(const allocator_type &alloc)
Constructs with a custom allocator.
constexpr MultiTypeMap()=default
constexpr bool Contains() const noexcept
Checks if a Storage entry exists for type T.
constexpr MapType & Data() noexcept
Returns a view of the underlying map (non-const).
static constexpr TypeIndex From() noexcept
Constructs a TypeIndex from a type T.
constexpr size_t Hash() const noexcept
Retrieves the hash value associated with this TypeIndex.
MultiTypeMap< Storage, std::pmr::polymorphic_allocator< std::byte > > PmrMultiTypeMap
constexpr std::string_view TypeNameOf() noexcept
Retrieves the unqualified type name of T.