Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
archetype_id.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <helios/assert.hpp>
6
7#include <algorithm>
8#include <cstddef>
9#include <functional>
10#include <initializer_list>
11#include <ranges>
12#include <span>
13#include <vector>
14
15namespace helios::ecs {
16
17/**
18 * @brief Unique identifier for an archetype, represented as a sorted set of
19 * component type indices.
20 * @details An archetype is defined by the exact set of component types it
21 * contains. The type indices are kept sorted to ensure consistent identity
22 * regardless of insertion order. Only archetype-storage components are tracked
23 * here; sparse-set components are managed separately.
24 */
26public:
27 using size_type = size_t;
28
29 /// @brief Constructs an empty archetype id (no components).
30 constexpr ArchetypeId() = default;
31
32 /**
33 * @brief Constructs an archetype id from a span of component type indices.
34 * @details Indices are copied and sorted internally. Duplicates are removed.
35 * @param types Span of component type indices
36 */
37 explicit constexpr ArchetypeId(std::vector<ComponentTypeIndex> types);
38
39 /**
40 * @brief Constructs an archetype id from an initializer list of component
41 * type indices.
42 * @details Indices are copied and sorted internally. Duplicates are removed.
43 * @param types Initializer list of component type indices
44 */
45 constexpr ArchetypeId(std::initializer_list<ComponentTypeIndex> types);
46 constexpr ArchetypeId(const ArchetypeId&) = default;
47 constexpr ArchetypeId(ArchetypeId&&) noexcept = default;
48 constexpr ~ArchetypeId() = default;
49
50 constexpr ArchetypeId& operator=(const ArchetypeId&) = default;
51 constexpr ArchetypeId& operator=(ArchetypeId&&) noexcept = default;
52
53 /**
54 * @brief Creates an archetype id from a set of component types.
55 * @tparam Ts Component types
56 * @return Archetype id containing the type indices of all provided types
57 */
58 template <ComponentTrait... Ts>
59 requires utils::UniqueTypes<Ts...>
60 [[nodiscard]] static constexpr ArchetypeId From();
61
62 /**
63 * @brief Returns a new archetype id with the given component type added.
64 * @details If the type is already present, returns a copy of this id.
65 * @param type Component type index to add
66 * @return New archetype id with the type included
67 */
68 [[nodiscard]] constexpr ArchetypeId With(ComponentTypeIndex type) const;
69
70 /**
71 * @brief Returns a new archetype id with the given component type removed.
72 * @details If the type is not present, returns a copy of this id.
73 * @param type Component type index to remove
74 * @return New archetype id without the type
75 */
76 [[nodiscard]] constexpr ArchetypeId Without(ComponentTypeIndex type) const;
77
78 /**
79 * @brief Returns a new archetype id with the given component type added.
80 * @tparam T Component type to add
81 * @return New archetype id with the type included
82 */
83 template <ComponentTrait T>
84 [[nodiscard]] constexpr ArchetypeId With() const {
86 }
87
88 /**
89 * @brief Returns a new archetype id with the given component type removed.
90 * @tparam T Component type to remove
91 * @return New archetype id without the type
92 */
93 template <ComponentTrait T>
94 [[nodiscard]] constexpr ArchetypeId Without() const {
96 }
97
98 constexpr bool operator==(const ArchetypeId& other) const noexcept {
99 return types_ == other.types_;
100 }
101 constexpr bool operator!=(const ArchetypeId& other) const noexcept {
102 return !(*this == other);
103 }
104 constexpr bool operator<(const ArchetypeId& other) const noexcept {
105 return std::ranges::lexicographical_compare(types_, other.types_);
106 }
107
108 /**
109 * @brief Checks if this archetype id contains the given component type.
110 * @param type Component type index to check
111 * @return True if the type is present
112 */
113 [[nodiscard]] constexpr bool Contains(
114 ComponentTypeIndex type) const noexcept {
115 return std::ranges::binary_search(types_, type);
116 }
117
118 /**
119 * @brief Checks if this archetype id contains the given component type.
120 * @tparam T Component type to check
121 * @return True if the type is present
122 */
123 template <ComponentTrait T>
124 [[nodiscard]] constexpr bool Contains() const noexcept {
126 }
127
128 /**
129 * @brief Checks if this archetype id contains all the given component types.
130 * @tparam R Range of `ComponentTypeIndex` to check
131 * @param types Range of component type indices to check
132 * @return True if all types are present
133 */
134 template <std::ranges::range R>
135 requires std::same_as<std::ranges::range_value_t<R>, ComponentTypeIndex>
136 [[nodiscard]] constexpr bool ContainsAll(const R& types) const noexcept {
137 return std::ranges::all_of(
138 types, [this](ComponentTypeIndex type) { return Contains(type); });
139 }
140
141 /**
142 * @brief Checks if this archetype id contains any of the given component
143 * types.
144 * @tparam R Range of `ComponentTypeIndex` to check
145 * @param types Range of component type indices to check
146 * @return True if any type is present
147 */
148 template <std::ranges::range R>
149 requires std::same_as<std::ranges::range_value_t<R>, ComponentTypeIndex>
150 [[nodiscard]] constexpr bool ContainsAny(const R& types) const noexcept {
151 return std::ranges::any_of(
152 types, [this](ComponentTypeIndex type) { return Contains(type); });
153 }
154
155 /**
156 * @brief Checks if this archetype id contains none of the given component
157 * types.
158 * @tparam R Range of `ComponentTypeIndex` to check
159 * @param types Range of component type indices to check
160 * @return True if no type is present
161 */
162 template <std::ranges::range R>
163 requires std::same_as<std::ranges::range_value_t<R>, ComponentTypeIndex>
164 [[nodiscard]] constexpr bool ContainsNone(const R& types) const noexcept {
165 return std::ranges::none_of(
166 types, [this](ComponentTypeIndex type) { return Contains(type); });
167 }
168
169 /**
170 * @brief Gets the sorted component type indices.
171 * @return Span of component type indices in this archetype
172 */
173 [[nodiscard]] constexpr auto Types() const noexcept
174 -> std::span<const ComponentTypeIndex> {
175 return types_;
176 }
177
178 /**
179 * @brief Gets the precomputed hash of this archetype id.
180 * @return Hash value for this archetype id
181 */
182 [[nodiscard]] constexpr size_t Hash() const noexcept { return hash_; }
183
184 /**
185 * @brief Checks if this archetype id has no component types.
186 * @return True if this archetype id is empty (no component types)
187 */
188 [[nodiscard]] constexpr bool Empty() const noexcept { return types_.empty(); }
189
190 /**
191 * @brief Gets the number of component types in this archetype id.
192 * @return Count of component types in this archetype id
193 */
194 [[nodiscard]] constexpr size_type Size() const noexcept {
195 return types_.size();
196 }
197
198private:
199 constexpr void SortAndDeduplicate();
200 constexpr void ComputeHash() noexcept;
201
202 std::vector<ComponentTypeIndex> types_;
203 size_t hash_ = 0;
204};
205
207 : types_(std::move(types)) {
208 SortAndDeduplicate();
209 ComputeHash();
210}
211
213 std::initializer_list<ComponentTypeIndex> types)
214 : types_(types) {
215 SortAndDeduplicate();
216 ComputeHash();
217}
218
219template <ComponentTrait... Ts>
220 requires utils::UniqueTypes<Ts...>
222 if constexpr (sizeof...(Ts) == 0) {
223 return {};
224 } else {
226 }
227}
228
230 if (Contains(type)) {
231 return *this;
232 }
233
234 ArchetypeId result;
235 result.types_.reserve(types_.size() + 1);
236 result.types_ = types_;
237 auto it = std::ranges::lower_bound(result.types_, type);
238 result.types_.insert(it, type);
239 result.ComputeHash();
240 return result;
241}
242
244 if (!Contains(type)) {
245 return *this;
246 }
247
248 ArchetypeId result;
249 result.types_.reserve(types_.size() - 1);
250 for (const auto& ty : types_) {
251 if (ty != type) {
252 result.types_.push_back(ty);
253 }
254 }
255 result.ComputeHash();
256 return result;
257}
258
259constexpr void ArchetypeId::SortAndDeduplicate() {
260 std::ranges::sort(types_);
261 auto [first, last] = std::ranges::unique(types_);
262 types_.erase(first, last);
263}
264
265constexpr void ArchetypeId::ComputeHash() noexcept {
266 size_t seed = 0;
267 for (const auto type_index : types_) {
268 // Use a good hash combiner (boost-style).
269 seed ^=
270 type_index.Hash() + 0x9e3779b97f4a7c15ULL + (seed << 6U) + (seed >> 2U);
271 }
272 hash_ = seed;
273}
274
275} // namespace helios::ecs
276
277namespace std {
278
279template <>
280struct hash<helios::ecs::ArchetypeId> {
281 constexpr size_t operator()(
282 const helios::ecs::ArchetypeId& id) const noexcept {
283 return id.Hash();
284 }
285};
286
287} // namespace std
Unique identifier for an archetype, represented as a sorted set of component type indices.
constexpr auto Types() const noexcept -> std::span< const ComponentTypeIndex >
Gets the sorted component type indices.
constexpr bool ContainsAny(const R &types) const noexcept
Checks if this archetype id contains any of the given component types.
constexpr bool operator==(const ArchetypeId &other) const noexcept
constexpr bool operator<(const ArchetypeId &other) const noexcept
constexpr ArchetypeId(const ArchetypeId &)=default
constexpr bool Contains() const noexcept
Checks if this archetype id contains the given component type.
constexpr bool Empty() const noexcept
Checks if this archetype id has no component types.
constexpr bool operator!=(const ArchetypeId &other) const noexcept
constexpr size_type Size() const noexcept
Gets the number of component types in this archetype id.
constexpr ArchetypeId With() const
Returns a new archetype id with the given component type added.
constexpr bool Contains(ComponentTypeIndex type) const noexcept
Checks if this archetype id contains the given component type.
static constexpr ArchetypeId From()
Creates an archetype id from a set of component types.
constexpr bool ContainsAll(const R &types) const noexcept
Checks if this archetype id contains all the given component types.
constexpr size_t Hash() const noexcept
Gets the precomputed hash of this archetype id.
constexpr ArchetypeId With(ComponentTypeIndex type) const
Returns a new archetype id with the given component type added.
constexpr bool ContainsNone(const R &types) const noexcept
Checks if this archetype id contains none of the given component types.
constexpr ArchetypeId Without(ComponentTypeIndex type) const
Returns a new archetype id with the given component type removed.
constexpr ArchetypeId()=default
Constructs an empty archetype id (no components).
constexpr ArchetypeId Without() const
Returns a new archetype id with the given component type removed.
constexpr ArchetypeId(ArchetypeId &&) noexcept=default
static constexpr TypeIndex From() noexcept
Constructs a TypeIndex from a type T.
Concept to check if a type can be used as a component.
Definition component.hpp:31
Concept that checks if all types in a pack are unique (after removing cv/ref qualifiers).
utils::TypeIndex ComponentTypeIndex
Type index for components.
Definition component.hpp:14
STL namespace.
constexpr size_t operator()(const helios::ecs::ArchetypeId &id) const noexcept