Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
system_set.hpp
Go to the documentation of this file.
1#pragma once
2
6
7#include <algorithm>
8#include <compare>
9#include <concepts>
10#include <cstddef>
11#include <ranges>
12#include <string_view>
13#include <type_traits>
14#include <vector>
15
16namespace helios::ecs {
17
18/// @brief Type index for system sets.
20
21/// @brief Type id for system sets.
23
24/**
25 * @brief Concept for system sets.
26 * @details A system set must be an object and be empty.
27 */
28template <typename T>
29concept SystemSetTrait = std::is_object_v<std::remove_cvref_t<T>> &&
30 std::is_empty_v<std::remove_cvref_t<T>>;
31
32/**
33 * @brief Concept for system sets that provide a name.
34 * @details A system set with name trait must satisfy `SystemSetTrait`
35 * and provide:
36 * - `static constexpr std::string_view kName` variable
37 */
38template <typename T>
40 { std::remove_cvref_t<T>::kName } -> std::convertible_to<std::string_view>;
41};
42
43/**
44 * @brief System set name for debugging and serialization.
45 * @tparam T System set type
46 * @return System set name
47 */
48template <SystemSetTrait T>
49[[nodiscard]] constexpr std::string_view SystemSetNameOf() noexcept {
50 if constexpr (SystemSetWithNameTrait<T>) {
51 return T::kName;
52 } else {
54 }
55}
56
57/**
58 * @brief System set name for debugging and serialization.
59 * @tparam T System set type
60 * @param instance System set instance
61 * @return System set name
62 */
63template <SystemSetTrait T>
64[[nodiscard]] constexpr std::string_view SystemSetNameOf(
65 const T& /*instance*/) noexcept {
67}
68
69/// @brief Identifier for a system set.
71 size_t id = 0;
72
73 /**
74 * @brief Creates system set id from a system set type index.
75 * @param index System set type index
76 * @return System set id
77 */
78 [[nodiscard]] static constexpr SystemSetId From(
79 SystemSetTypeIndex index) noexcept {
80 return SystemSetId{.id = index.Hash()};
81 }
82
83 /**
84 * @brief Creates system set id from a system set type id.
85 * @param id System set type id
86 * @return System set id
87 */
88 [[nodiscard]] static constexpr SystemSetId From(SystemSetTypeId id) noexcept {
89 return From(id.Index());
90 }
91
92 /**
93 * @brief Creates system set id from a system set type.
94 * @tparam T System set type
95 * @return System set id
96 */
97 template <SystemSetTrait T>
98 [[nodiscard]] static constexpr SystemSetId From() noexcept {
100 }
101
102 /**
103 * @brief Creates system set id from a system set type.
104 * @tparam T System set type
105 * @param instance System set instance
106 * @return System set id
107 */
108 template <SystemSetTrait T>
109 [[nodiscard]] static constexpr SystemSetId From(
110 const T& /*instance*/) noexcept {
112 }
113
114 /**
115 * @brief Creates system set id from a range of system ids.
116 * @tparam R Range type containing SystemId elements
117 * @param ids Range of system ids
118 * @return System set id
119 */
120 template <std::ranges::input_range R>
121 requires std::same_as<std::ranges::range_value_t<R>, SystemId>
122 [[nodiscard]] static constexpr SystemSetId From(const R& ids) noexcept;
123
124 [[nodiscard]] constexpr std::strong_ordering operator<=>(
125 const SystemSetId&) const noexcept = default;
126};
127
128template <std::ranges::input_range R>
129 requires std::same_as<std::ranges::range_value_t<R>, SystemId>
130constexpr auto SystemSetId::From(const R& ids) noexcept -> SystemSetId {
131 const auto hasher = [](size_t lhs, size_t rhs) {
132 return lhs ^ (rhs + 0x9e3779b9 + (lhs << 6) + (rhs >> 2));
133 };
134
135 size_t hash = 0;
136 for (const auto& system_id : ids) {
137 hash = hasher(hash, system_id.id);
138 }
139 return SystemSetId{.id = hash};
140}
141
142/**
143 * @brief A group of systems that share ordering, run conditions, and other
144 * properties.
145 * @details When a property is applied to a set (e.g., `Before`, `After`,
146 * `RunIf`), it is inherited by all systems that belong to the set.
147 */
149public:
150 /**
151 * @brief Constructs a `SystemSet` with the given ID.
152 * @param id The ID of the system set
153 */
154 explicit constexpr SystemSet(SystemSetId id) : id_(id) {}
155 SystemSet(const SystemSet&) = delete;
156 constexpr SystemSet(SystemSet&&) = default;
157 constexpr ~SystemSet() = default;
158
159 SystemSet& operator=(const SystemSet&) = delete;
160 constexpr SystemSet& operator=(SystemSet&&) = default;
161
162 /**
163 * @brief Adds an ordering constraint: all members must run before the given
164 * system.
165 * @param target System that members must precede
166 * @return Reference to this set for chaining
167 */
168 constexpr auto Before(this auto&& self, SystemId target)
169 -> decltype(std::forward<decltype(self)>(self));
170
171 /**
172 * @brief Adds an ordering constraint: all members must run before all
173 * members of the given set.
174 * @param target Set that members must precede
175 * @return Reference to this set for chaining
176 */
177 constexpr auto Before(this auto&& self, SystemSetId target)
178 -> decltype(std::forward<decltype(self)>(self));
179
180 /**
181 * @brief Adds an ordering constraint: all members must run after the given
182 * system.
183 * @param target System that members must follow
184 * @return Reference to this set for chaining
185 */
186 constexpr auto After(this auto&& self, SystemId target)
187 -> decltype(std::forward<decltype(self)>(self));
188
189 /**
190 * @brief Adds an ordering constraint: all members must run after all
191 * members of the given set.
192 * @param target Set that members must follow
193 * @return Reference to this set for chaining
194 */
195 constexpr auto After(this auto&& self, SystemSetId target)
196 -> decltype(std::forward<decltype(self)>(self));
197
198 /**
199 * @brief Adds a run condition to the set.
200 * @details All systems in this set will only run if all conditions return
201 * true.
202 * @param condition Run condition predicate with access policy
203 * @param policy Access policy for the run condition
204 * @param options Options for run condition local data construction
205 * @return Reference to this set for chaining
206 */
207 constexpr auto RunIf(this auto&& self, RunConditionStorage condition,
208 AccessPolicy policy = {},
209 SystemLocalDataOptions options = {})
210 -> decltype(std::forward<decltype(self)>(self));
211
212 /**
213 * @brief Adds a run condition functor to the set.
214 * @details Lambdas are rejected; use `RunIf(std::string, T&&)`.
215 * @tparam T Run condition type satisfying `FunctorSystemTrait`
216 * @param condition Functor with declared access policy
217 * @return Reference to this set for chaining
218 */
219 template <FunctorSystemTrait T>
220 constexpr auto RunIf(this auto&& self, T&& condition)
221 -> decltype(std::forward<decltype(self)>(self));
222
223 /**
224 * @brief Adds a run condition to the set with an explicit name.
225 * @details Required for lambdas.
226 * @tparam T Run condition type satisfying `SystemTrait`
227 * @param name Human-readable name
228 * @param condition Functor with declared access policy
229 * @return Reference to this set for chaining
230 */
231 template <SystemTrait T>
232 constexpr auto RunIf(this auto&& self, std::string name, T&& condition)
233 -> decltype(std::forward<decltype(self)>(self));
234
235 /**
236 * @brief Marks this set as a sequence: systems run in insertion order.
237 * @return Reference to this set for chaining
238 */
239 constexpr auto Sequence(this auto&& self)
240 -> decltype(std::forward<decltype(self)>(self));
241
242 /**
243 * @brief Checks if this set is a sequence set.
244 * @return True if this set is a sequence set, false otherwise
245 */
246 [[nodiscard]] constexpr bool IsSequence() const noexcept {
247 return is_sequence_;
248 }
249
250 /**
251 * @brief Gets the system set id.
252 * @return System set id
253 */
254 [[nodiscard]] constexpr SystemSetId Id() const noexcept { return id_; }
255
256 /**
257 * @brief Takes ownership of all run conditions from this set.
258 * @return Vector of run conditions that were previously owned by this set
259 */
260 [[nodiscard]] constexpr auto TakeConditions()
261 -> std::vector<RunConditionStorage> {
262 return std::exchange(conditions_, {});
263 }
264
265 /**
266 * @brief Gets the system ordering targets for "before" constraints.
267 * @return Vector of system ids that are targets of "before" constraints
268 * from this set
269 */
270 [[nodiscard]] constexpr auto BeforeTargets() const noexcept
271 -> const std::vector<SystemId>& {
272 return before_targets_;
273 }
274
275 /**
276 * @brief Gets the system ordering targets for "after" constraints.
277 * @return Vector of system ids that are targets of "after" constraints from
278 * this set
279 */
280 [[nodiscard]] constexpr auto AfterTargets() const noexcept
281 -> const std::vector<SystemId>& {
282 return after_targets_;
283 }
284
285 /**
286 * @brief Gets the set ordering targets for "before" constraints.
287 * @return Vector of system set ids that are targets of "before" constraints
288 * from this set
289 */
290 [[nodiscard]] constexpr auto BeforeSetTargets() const noexcept
291 -> const std::vector<SystemSetId>& {
292 return before_set_targets_;
293 }
294
295 /**
296 * @brief Gets the set ordering targets for "after" constraints.
297 * @return Vector of system set ids that are targets of "after" constraints
298 * from this set
299 */
300 [[nodiscard]] constexpr auto AfterSetTargets() const noexcept
301 -> const std::vector<SystemSetId>& {
302 return after_set_targets_;
303 }
304
305 /**
306 * @brief Gets the run conditions owned by this set.
307 * @return Vector of run conditions owned by this set
308 */
309 [[nodiscard]] constexpr auto Conditions() const noexcept
310 -> const std::vector<RunConditionStorage>& {
311 return conditions_;
312 }
313
314private:
315 SystemSetId id_;
316 std::vector<SystemId> before_targets_;
317 std::vector<SystemId> after_targets_;
318 std::vector<SystemSetId> before_set_targets_;
319 std::vector<SystemSetId> after_set_targets_;
320 std::vector<RunConditionStorage> conditions_;
321 bool is_sequence_ = false;
322};
323
324constexpr auto SystemSet::Before(this auto&& self, SystemId target)
325 -> decltype(std::forward<decltype(self)>(self)) {
326 if (std::ranges::find(self.before_targets_, target) ==
327 self.before_targets_.end()) {
328 self.before_targets_.push_back(target);
329 }
330 return std::forward<decltype(self)>(self);
331}
332
333constexpr auto SystemSet::Before(this auto&& self, SystemSetId target)
334 -> decltype(std::forward<decltype(self)>(self)) {
335 if (std::ranges::find(self.before_set_targets_, target) ==
336 self.before_set_targets_.end()) {
337 self.before_set_targets_.push_back(target);
338 }
339 return std::forward<decltype(self)>(self);
340}
341
342constexpr auto SystemSet::After(this auto&& self, SystemId target)
343 -> decltype(std::forward<decltype(self)>(self)) {
344 if (std::ranges::find(self.after_targets_, target) ==
345 self.after_targets_.end()) {
346 self.after_targets_.push_back(target);
347 }
348 return std::forward<decltype(self)>(self);
349}
350
351constexpr auto SystemSet::After(this auto&& self, SystemSetId target)
352 -> decltype(std::forward<decltype(self)>(self)) {
353 if (std::ranges::find(self.after_set_targets_, target) ==
354 self.after_set_targets_.end()) {
355 self.after_set_targets_.push_back(target);
356 }
357 return std::forward<decltype(self)>(self);
358}
359
360constexpr auto SystemSet::RunIf(this auto&& self, RunConditionStorage condition,
361 AccessPolicy /*policy*/,
362 SystemLocalDataOptions /*options*/)
363 -> decltype(std::forward<decltype(self)>(self)) {
364 self.conditions_.push_back(std::move(condition));
365 return std::forward<decltype(self)>(self);
366}
367
368template <FunctorSystemTrait T>
369constexpr auto SystemSet::RunIf(this auto&& self, T&& condition)
370 -> decltype(std::forward<decltype(self)>(self)) {
371 self.conditions_.push_back(
372 RunConditionStorage::FromParam(std::forward<T>(condition)));
373 return std::forward<decltype(self)>(self);
374}
375
376template <SystemTrait T>
377constexpr auto SystemSet::RunIf(this auto&& self, std::string name,
378 T&& condition)
379 -> decltype(std::forward<decltype(self)>(self)) {
380 self.conditions_.push_back(RunConditionStorage::FromParamNamed(
381 std::move(name), std::forward<T>(condition)));
382 return std::forward<decltype(self)>(self);
383}
384
385constexpr auto SystemSet::Sequence(this auto&& self)
386 -> decltype(std::forward<decltype(self)>(self)) {
387 self.is_sequence_ = true;
388 return std::forward<decltype(self)>(self);
389}
390
391} // namespace helios::ecs
Stores data access requirements for a system at compile time.
constexpr SystemSet & operator=(SystemSet &&)=default
constexpr ~SystemSet()=default
constexpr auto BeforeSetTargets() const noexcept -> const std::vector< SystemSetId > &
Gets the set ordering targets for "before" constraints.
constexpr auto Conditions() const noexcept -> const std::vector< RunConditionStorage > &
Gets the run conditions owned by this set.
constexpr auto Before(this auto &&self, SystemId target) -> decltype(std::forward< decltype(self)>(self))
Adds an ordering constraint: all members must run before the given system.
constexpr auto AfterSetTargets() const noexcept -> const std::vector< SystemSetId > &
Gets the set ordering targets for "after" constraints.
constexpr auto AfterTargets() const noexcept -> const std::vector< SystemId > &
Gets the system ordering targets for "after" constraints.
constexpr SystemSetId Id() const noexcept
Gets the system set id.
SystemSet(const SystemSet &)=delete
constexpr auto Sequence(this auto &&self) -> decltype(std::forward< decltype(self)>(self))
Marks this set as a sequence: systems run in insertion order.
constexpr bool IsSequence() const noexcept
Checks if this set is a sequence set.
SystemSet & operator=(const SystemSet &)=delete
constexpr auto BeforeTargets() const noexcept -> const std::vector< SystemId > &
Gets the system ordering targets for "before" constraints.
constexpr SystemSet(SystemSet &&)=default
constexpr auto TakeConditions() -> std::vector< RunConditionStorage >
Takes ownership of all run conditions from this set.
constexpr SystemSet(SystemSetId id)
Constructs a SystemSet with the given ID.
constexpr auto RunIf(this auto &&self, RunConditionStorage condition, AccessPolicy policy={}, SystemLocalDataOptions options={}) -> decltype(std::forward< decltype(self)>(self))
Adds a run condition to the set.
constexpr auto After(this auto &&self, SystemId target) -> decltype(std::forward< decltype(self)>(self))
Adds an ordering constraint: all members must run after the given system.
static constexpr TypeIndex From() noexcept
Constructs a TypeIndex from a type T.
Concept for system sets.
Concept for system sets that provide a name.
utils::TypeIndex SystemSetTypeIndex
Type index for system sets.
constexpr std::string_view SystemSetNameOf() noexcept
System set name for debugging and serialization.
utils::TypeId SystemSetTypeId
Type id for system sets.
constexpr std::string_view QualifiedTypeNameOf() noexcept
Retrieves the fully qualified type name of T.
STL namespace.
Storage for a run condition — predicate + access policy + local data.
static RunConditionStorage FromParam(T &&instance, SystemLocalDataOptions options={})
Factory from a param-style run condition functor.
static RunConditionStorage FromParamNamed(std::string name, T &&instance, SystemLocalDataOptions options={})
Factory from a param-style run condition with an explicit name.
Id for systems.
Definition system.hpp:146
Options for system local data.
Identifier for a system set.
constexpr std::strong_ordering operator<=>(const SystemSetId &) const noexcept=default
static constexpr SystemSetId From(const R &ids) noexcept
Creates system set id from a range of system ids.
static constexpr SystemSetId From(const T &) noexcept
Creates system set id from a system set type.
static constexpr SystemSetId From(SystemSetTypeIndex index) noexcept
Creates system set id from a system set type index.
static constexpr SystemSetId From(SystemSetTypeId id) noexcept
Creates system set id from a system set type id.
static constexpr SystemSetId From() noexcept
Creates system set id from a system set type.