Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
manager.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <helios/assert.hpp>
6#include <helios/ecs/details/profile.hpp>
11
12#include <array>
13#include <cstddef>
14#include <ranges>
15#include <span>
16#include <string>
17#include <string_view>
18#include <utility>
19#include <vector>
20
21namespace helios::ecs {
22
23/// @brief Metadata for a registered message type.
25 MessageTypeIndex type_index; ///< Unique type index for the message
26 std::string_view name; ///< Human-readable name of the message
28 MessageClearPolicy::kAutomatic; ///< Message clearing policy
29 bool is_async = false; ///< Whether this message uses the async queue
30};
31
32/**
33 * @brief Central coordinator for message lifecycle with double buffering and
34 * consumed message removal.
35 * @details Implements message management with:
36 * - Double buffering: messages persist for one full update cycle (two frames)
37 * - Explicit registration: messages must be registered before use
38 * - Automatic clearing: messages are cleared after their lifecycle expires
39 * - Manual control: users can opt-out of auto-clearing
40 * - Consumed message removal: at Update time, consumed messages are physically
41 * removed from queues without additional heap allocation
42 *
43 * Message Lifecycle:
44 * - Frame N: Messages written to current queue
45 * - Frame N+1: Messages readable from previous queue (after swap)
46 * - Frame N+2: Messages cleared from previous queue (automatic policy)
47 *
48 * Consumed messages are removed at Update time based on merged per-system
49 * `ConsumedMessagesRegistry` instances. Each system writes to its own registry
50 * during parallel execution, and all registries are merged and applied here.
51 *
52 * @note Partially thread-safe.
53 */
55public:
56 using size_type = size_t;
57
58 MessageManager() = default;
60 MessageManager(MessageManager&&) noexcept = default;
61 ~MessageManager() = default;
62
63 MessageManager& operator=(const MessageManager&) = delete;
64 MessageManager& operator=(MessageManager&&) noexcept = default;
65
66 /**
67 * @brief Clears all messages, consumed flags, and registration data.
68 * @note Not thread-safe.
69 */
70 void Clear() noexcept;
71
72 /**
73 * @brief Clears all message data without removing registrations.
74 * @note Not thread-safe.
75 */
76 void ClearAllQueues() noexcept;
77
78 /**
79 * @brief Updates message lifecycle — applies consumed messages, swaps
80 * buffers, clears old messages.
81 * @details Performs the following steps:
82 * 1. Removes consumed messages from previous and current queues.
83 * 2. Clears automatic messages from previous queue.
84 * 3. Merges current queue into previous queue.
85 * 4. Prepares a fresh current queue for the next frame.
86 * @note Not thread-safe.
87 * @tparam Alloc Allocator type for the consumed registry
88 * @param consumed_registry Const References to per-system consumed
89 * registry. The caller is responsible for clearing it after this call.
90 */
91 template <typename Alloc>
92 void Update(const ConsumedMessagesRegistry<Alloc>& consumed_registry);
93
94 /**
95 * @brief Updates message lifecycle without any consumed message processing.
96 * @details Convenience overload when no systems have consumed any messages.
97 * @note Not thread-safe.
98 */
99 void Update();
100
101 /**
102 * @brief Applies consumed indices to both queues, removing consumed messages
103 * in-place.
104 * @details For each type with consumed entries, indices [0, prev_count)
105 * target previous messages and indices [prev_count, prev_count + curr_count)
106 * target current messages (offset by prev_count). No buffer swap occurs.
107 * @note Not thread-safe.
108 * @tparam Alloc Allocator type of the consumed registry
109 * @param merged_consumed The combined consumed registry from all systems
110 */
111 template <typename Alloc>
112 void ApplyConsumed(const ConsumedMessagesRegistry<Alloc>& merged_consumed);
113
114 /**
115 * @brief Registers multiple message types.
116 * @note Not thread-safe.
117 * @warning Triggers assertion if any message type is already registered.
118 * @tparam Messages Message types to register, satisfying `AnyMessageTrait`
119 */
120 template <AnyMessageTrait... Messages>
121 requires(sizeof...(Messages) > 0)
122 void Register();
123
124 /**
125 * @brief Writes a single regular message to the current queue.
126 * @note Not thread-safe.
127 * @warning Triggers assertion if the message type is not registered.
128 * @tparam T Message type satisfying `MessageTrait`
129 * @param message Message to write
130 */
131 template <MessageTrait T>
132 void Write(T&& message);
133
134 /**
135 * @brief Writes a single async message to the async queue.
136 * @note Thread-safe.
137 * @warning Triggers assertion if the message type is not registered.
138 * @tparam T Async message type satisfying `AsyncMessageTrait`
139 * @param message Message to write
140 */
141 template <AsyncMessageTrait T>
142 void WriteAsync(T&& message);
143
144 /**
145 * @brief Writes multiple regular messages to the current queue in bulk.
146 * @note Not thread-safe.
147 * @warning Triggers assertion if the message type is not registered.
148 * @tparam R Range whose value_type satisfies `MessageTrait`
149 * @param messages Range of messages to write
150 */
151 template <std::ranges::input_range R>
152 requires MessageTrait<std::ranges::range_value_t<R>>
153 void WriteBulk(R&& messages);
154
155 /**
156 * @brief Writes multiple async messages to the async queue in bulk.
157 * @note Thread-safe.
158 * @warning Triggers assertion if the message type is not registered.
159 * @tparam R Range whose value_type satisfies `AsyncMessageTrait`
160 * @param messages Range of async messages to write
161 */
162 template <std::ranges::input_range R>
163 requires AsyncMessageTrait<std::ranges::range_value_t<R>>
164 void WriteAsyncBulk(R&& messages);
165
166 /**
167 * @brief Manually clears regular messages of a specific type from both
168 * queues.
169 * @note Not thread-safe.
170 * @warning Triggers assertion if the message type is not registered.
171 * @tparam T Message type satisfying `MessageTrait`
172 */
173 template <MessageTrait T>
174 void ManualClear();
175
176 /**
177 * @brief Manually clears async messages of a specific type from the queue.
178 * @note Thread-safe.
179 * @warning Triggers assertion if the message type is not registered.
180 * @tparam T Async message type satisfying `AsyncMessageTrait`
181 */
182 template <AsyncMessageTrait T>
183 void ManualAsyncClear();
184
185 /**
186 * @brief Merges messages from a local `MessageQueue` into the current queue.
187 * @note Not thread-safe.
188 * @details Used to flush a per-system write buffer into the global message
189 * state. Typically called after a system finishes execution.
190 * @tparam OtherAllocator Allocator type used by the local `MessageQueue`
191 * @param local Local message queue to merge from (will be left in a valid but
192 * empty state)
193 */
194 template <typename OtherAllocator>
195 void MergeLocalMessages(const MessageQueue<OtherAllocator>& local) {
196 current_messages_.Merge(local);
197 }
198
199 /**
200 * @brief Merges messages from a local `MessageQueue` into the current queue.
201 * @note Not thread-safe.
202 * @details Rvalue overload that consumes the local queue.
203 * @tparam OtherAllocator Allocator type used by the local `MessageQueue`
204 * @param local Local message queue to merge from (will be left in a valid but
205 * empty state)
206 */
207 template <typename OtherAllocator>
209 current_messages_.Merge(std::move(local));
210 }
211
212 /**
213 * @brief Checks if an message type (regular or async) is registered.
214 * @note Thread safe for concurrent reads.
215 * @tparam T Message type satisfying `AnyMessageTrait`
216 * @return True if the message type is registered, false otherwise
217 */
218 template <AnyMessageTrait T>
219 [[nodiscard]] bool IsRegistered() const noexcept {
220 return registered_messages_.Contains(MessageTypeIndex::From<T>());
221 }
222
223 /**
224 * @brief Checks if any messages exist across regular and async queues.
225 * @note Thread safe for concurrent reads.
226 * @return True if at least one message exists, false otherwise
227 */
228 [[nodiscard]] bool HasMessages() const noexcept {
229 return current_messages_.HasMessages() ||
230 previous_messages_.HasMessages() || async_messages_.HasMessages();
231 }
232
233 /**
234 * @brief Checks if regular messages of a specific type exist in either queue.
235 * @note Thread safe for concurrent reads.
236 * @tparam T Message type satisfying `MessageTrait`
237 * @return True if messages exist, false otherwise
238 */
239 template <MessageTrait T>
240 [[nodiscard]] bool HasMessages() const noexcept;
241
242 /**
243 * @brief Checks if async messages of a specific type exist.
244 * @note Thread safe.
245 * @tparam T Async message type satisfying `AsyncMessageTrait`
246 * @return True if async messages exist, false otherwise
247 */
248 template <AsyncMessageTrait T>
249 [[nodiscard]] bool HasAsyncMessages() const noexcept;
250
251 /**
252 * @brief Gets a const span of messages of a specific type from the previous
253 * queue.
254 * @note Thread safe for concurrent reads.
255 * @tparam T Message type satisfying `MessageTrait`
256 * @return Span of const messages
257 */
258 template <MessageTrait T>
259 [[nodiscard]] auto PreviousMessages() const noexcept -> std::span<const T> {
260 return previous_messages_.Messages<T>();
261 }
262
263 /**
264 * @brief Gets a const span of messages of a specific type from the current
265 * queue.
266 * @note Thread safe for concurrent reads.
267 * @tparam T Message type satisfying `MessageTrait`
268 * @return Span of const messages
269 */
270 template <MessageTrait T>
271 [[nodiscard]] auto CurrentMessages() const noexcept -> std::span<const T> {
272 return current_messages_.Messages<T>();
273 }
274
275 /**
276 * @brief Gets metadata for a registered message type.
277 * @note Thread safe for concurrent reads.
278 * @tparam T Message type satisfying `AnyMessageTrait`
279 * @return Pointer to metadata, or nullptr if not registered
280 */
281 template <AnyMessageTrait T>
282 [[nodiscard]] const MessageMetadata* Metadata() const noexcept {
283 return registered_messages_.TryGet(MessageTypeIndex::From<T>());
284 }
285
286 /**
287 * @brief Gets the number of registered message types (regular + async).
288 * @note Thread safe for concurrent reads.
289 * @return Count of registered messages
290 */
291 [[nodiscard]] size_type RegisteredMessageCount() const noexcept {
292 return registered_messages_.Size();
293 }
294
295 /**
296 * @brief Gets const reference to current message queue.
297 * @note Thread-safe.
298 * @return Const reference to current queue
299 */
300 [[nodiscard]] const MessageQueue<>& CurrentQueue() const noexcept {
301 return current_messages_;
302 }
303
304 /**
305 * @brief Gets mutable reference to current message queue.
306 * @note Not thread-safe.
307 * @return Mutable reference to current queue
308 */
309 [[nodiscard]] MessageQueue<>& CurrentQueue() noexcept {
310 return current_messages_;
311 }
312
313 /**
314 * @brief Gets const reference to previous message queue.
315 * @note Thread-safe.
316 * @return Const reference to previous queue
317 */
318 [[nodiscard]] const MessageQueue<>& PreviousQueue() const noexcept {
319 return previous_messages_;
320 }
321
322 /**
323 * @brief Gets mutable reference to previous message queue.
324 * @note Not thread-safe.
325 * @return Mutable reference to previous queue
326 */
327 [[nodiscard]] MessageQueue<>& PreviousQueue() noexcept {
328 return previous_messages_;
329 }
330
331 /**
332 * @brief Gets the async message queue (for creating tokens, etc.).
333 * @note Thread-safe.
334 * @return Reference to the async message queue
335 */
336 [[nodiscard]] AsyncMessageQueue& AsyncQueue() noexcept {
337 return async_messages_;
338 }
339
340 /**
341 * @brief Gets the async message queue (const).
342 * @note Thread-safe.
343 * @return Const reference to the async message queue
344 */
345 [[nodiscard]] const AsyncMessageQueue& AsyncQueue() const noexcept {
346 return async_messages_;
347 }
348
349private:
350 using RegisteredMessages = container::MultiTypeMap<MessageMetadata>;
351
352 RegisteredMessages
353 registered_messages_; ///< Metadata for all registered message types
354
356 current_messages_; ///< Storage for messages written in the current frame
358 previous_messages_; ///< Storage for messages from the previous frame
359 AsyncMessageQueue async_messages_; ///< Storage for async messages
360 ///< (lock-free, not double-buffered)
361};
362
363inline void MessageManager::Clear() noexcept {
364 registered_messages_.ResetAll();
365 current_messages_.ResetAll();
366 previous_messages_.ResetAll();
367 async_messages_.Reset();
368}
369
370inline void MessageManager::ClearAllQueues() noexcept {
371 current_messages_.ClearAll();
372 previous_messages_.ClearAll();
373 async_messages_.Clear();
374}
375
376template <typename Alloc>
378 const ConsumedMessagesRegistry<Alloc>& consumed_registry) {
379 HELIOS_ECS_PROFILE_SCOPE_N("helios::ecs::MessageManager::Update");
380
381 // Remove consumed messages from both queues.
382 if (!consumed_registry.Empty()) {
383 ApplyConsumed(consumed_registry);
384 }
385
386 // Merge current queue into previous queue.
387 // Previous queue now holds surviving previous messages (manual policy,
388 // non-consumed) plus all current messages.
389 previous_messages_.Merge(std::move(current_messages_));
390
391 // Re-register types in current_messages_ so it's ready for new
392 // writes.
393 for (const auto& [type_index, metadata] : registered_messages_) {
394 if (!metadata.is_async) {
395 current_messages_.Register(type_index);
396 }
397 }
398}
399
401 HELIOS_ECS_PROFILE_SCOPE_N("helios::ecs::MessageManager::Update");
402
403 // No consumed messages to process; clear aged automatic messages from
404 // previous, then merge current into previous.
405 for (const auto& [type_index, metadata] : registered_messages_) {
406 if (!metadata.is_async &&
407 metadata.clear_policy == MessageClearPolicy::kAutomatic) {
408 previous_messages_.Clear(type_index);
409 }
410 }
411
412 previous_messages_.Merge(std::move(current_messages_));
413
414 for (const auto& [type_index, metadata] : registered_messages_) {
415 if (!metadata.is_async) {
416 current_messages_.Register(type_index);
417 }
418 }
419}
420
421template <typename Alloc>
423 const ConsumedMessagesRegistry<Alloc>& merged_consumed) {
424 HELIOS_ECS_PROFILE_SCOPE_N("helios::ecs::MessageManager::ApplyConsumed");
425 HELIOS_ECS_PROFILE_ZONE_VALUE(merged_consumed.TotalConsumedCount());
426
427 for (const auto& [type_index, consumed_indices] : merged_consumed.Data()) {
428 if (consumed_indices.empty()) {
429 continue;
430 }
431
432 // Skip types that aren't registered as regular messages.
433 const auto* metadata = registered_messages_.TryGet(type_index);
434 if (metadata == nullptr || metadata->is_async) {
435 continue;
436 }
437
438 const auto prev_count = previous_messages_.MessageCount(type_index);
439 const auto curr_count = current_messages_.MessageCount(type_index);
440
441 if (prev_count == 0 && curr_count == 0) {
442 continue;
443 }
444
445 // Split consumed indices into those targeting previous vs current
446 // messages. Global indices [0, prev_count) -> previous queue Global
447 // indices [prev_count, prev_count + curr_count) -> current queue (offset
448 // by prev_count)
449
450 // Find the partition point between previous and current indices.
451 const auto partition_it =
452 std::ranges::lower_bound(consumed_indices, prev_count);
453
454 // Apply to previous queue: indices are already direct indices into the
455 // previous buffer.
456 if (partition_it != consumed_indices.begin()) {
457 auto prev_indices = std::span<const size_type>(
458 consumed_indices.data(),
459 static_cast<size_type>(partition_it - consumed_indices.begin()));
460 previous_messages_.RemoveIndices(type_index, prev_indices);
461 }
462
463 // Apply to current queue: offset indices by subtracting prev_count.
464 if (partition_it != consumed_indices.end()) {
465 // Build offset indices in a small local buffer. Since we're in the
466 // single-threaded Update path and consumed counts are typically small,
467 // use a stack-friendly approach. We reuse the tail of consumed_indices
468 // by creating offset copies.
469 const auto curr_consumed_count = static_cast<size_type>(
470 consumed_indices.data() + consumed_indices.size() - &(*partition_it));
471
472 // To avoid heap allocation, we compute offset indices into a local
473 // small buffer. If the count is small enough, use the stack; otherwise
474 // fall back to a vector.
475 constexpr size_type kStackThreshold = 64;
476 if (curr_consumed_count <= kStackThreshold) {
477 std::array<size_type, kStackThreshold> local_buf = {};
478 for (size_type i = 0; i < curr_consumed_count; ++i) {
479 local_buf[i] =
480 *(partition_it + static_cast<ptrdiff_t>(i)) - prev_count;
481 }
482 current_messages_.RemoveIndices(
483 type_index,
484 std::span<const size_type>{local_buf.data(), curr_consumed_count});
485 } else {
486 // Many consumed messages - heap allocate.
487 std::vector<size_type> offset_indices;
488 offset_indices.reserve(curr_consumed_count);
489 for (auto it = partition_it; it != consumed_indices.end(); ++it) {
490 offset_indices.push_back(*it - prev_count);
491 }
492 current_messages_.RemoveIndices(type_index, offset_indices);
493 }
494 }
495 }
496}
497
498template <AnyMessageTrait... Ts>
499 requires(sizeof...(Ts) > 0)
501 constexpr auto type_indices =
502 std::to_array({MessageTypeIndex::From<Ts>()...});
503 constexpr auto names = std::to_array({MessageNameOf<Ts>()...});
504
505#ifdef HELIOS_ENABLE_ASSERTS
506 std::string already_registered;
507 for (size_t i = 0; i < sizeof...(Ts); ++i) {
508 if (!registered_messages_.Contains(type_indices[i])) {
509 continue;
510 }
511
512 if (!already_registered.empty()) {
513 already_registered.append(", ");
514 }
515 already_registered.append(names[i]);
516 }
517
518 HELIOS_ASSERT(already_registered.empty(),
519 "Message type(s) '{}' already registered!", already_registered);
520#endif
521
522 (
523 [this]<typename T>() {
524 registered_messages_.Emplace<T>(
526 .name = MessageNameOf<T>(),
527 .clear_policy = MessageClearPolicyOf<T>(),
528 .is_async = AsyncMessageTrait<T>});
529
530 if constexpr (AsyncMessageTrait<T>) {
531 async_messages_.Register<T>();
532 } else if constexpr (MessageTrait<T>) {
533 current_messages_.Register<T>();
534 previous_messages_.Register<T>();
535 }
536 }.template operator()<Ts>(),
537 ...);
538}
539
540template <MessageTrait T>
541inline void MessageManager::Write(T&& message) {
542 using DecayedT = std::remove_cvref_t<T>;
544 registered_messages_.Contains(MessageTypeIndex::From<DecayedT>()),
545 "Message type '{}' is not registered!", MessageNameOf<DecayedT>());
546 current_messages_.Enqueue(std::forward<T>(message));
547}
548
549template <AsyncMessageTrait T>
550inline void MessageManager::WriteAsync(T&& message) {
551 using DecayedT = std::remove_cvref_t<T>;
553 registered_messages_.Contains(MessageTypeIndex::From<DecayedT>()),
554 "Message type '{}' is not registered!", MessageNameOf<DecayedT>());
555 async_messages_.Enqueue<DecayedT>(std::forward<T>(message));
556}
557
558template <std::ranges::input_range R>
560inline void MessageManager::WriteBulk(R&& messages) {
561 using T = std::ranges::range_value_t<R>;
562 HELIOS_ASSERT(registered_messages_.Contains(MessageTypeIndex::From<T>()),
563 "Message type '{}' is not registered!", MessageNameOf<T>());
564 current_messages_.EnqueueBulk(std::forward<R>(messages));
565}
566
567template <std::ranges::input_range R>
569inline void MessageManager::WriteAsyncBulk(R&& messages) {
570 using T = std::ranges::range_value_t<R>;
571 HELIOS_ASSERT(registered_messages_.Contains(MessageTypeIndex::From<T>()),
572 "Message type '{}' is not registered!", MessageNameOf<T>());
573 async_messages_.EnqueueBulk(std::forward<R>(messages));
574}
575
576template <MessageTrait T>
578 constexpr auto type_index = MessageTypeIndex::From<T>();
579 HELIOS_ASSERT(registered_messages_.Contains(type_index),
580 "Message type '{}' is not registered!", MessageNameOf<T>());
581 current_messages_.Clear<T>();
582 previous_messages_.Clear<T>();
583}
584
585template <AsyncMessageTrait T>
587 constexpr auto type_index = MessageTypeIndex::From<T>();
588 HELIOS_ASSERT(registered_messages_.Contains(type_index),
589 "Message type '{}' is not registered!", MessageNameOf<T>());
590 async_messages_.Clear<T>();
591}
592
593template <MessageTrait T>
594inline bool MessageManager::HasMessages() const noexcept {
595 if (!registered_messages_.Contains(MessageTypeIndex::From<T>()))
596 [[unlikely]] {
597 return false;
598 }
599 return current_messages_.HasMessages<T>() ||
600 previous_messages_.HasMessages<T>();
601}
602
603template <AsyncMessageTrait T>
604inline bool MessageManager::HasAsyncMessages() const noexcept {
605 if (!registered_messages_.Contains(MessageTypeIndex::From<T>()))
606 [[unlikely]] {
607 return false;
608 }
609 return async_messages_.HasMessages<T>();
610}
611
612} // namespace helios::ecs
#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.
Async queue for managing multiple message types via lock-free concurrent queues.
void Reset() noexcept
Resets the queue by clearing all messages and unregistering all message types.
Per-system registry for tracking consumed message indices.
constexpr const ConsumedMap & Data() const noexcept
Provides direct access to the underlying consumed map.
constexpr bool Empty() const noexcept
Checks if any message type has consumed entries.
constexpr size_type TotalConsumedCount() const noexcept
Gets the total number of consumed messages across all types.
bool HasAsyncMessages() const noexcept
Checks if async messages of a specific type exist.
Definition manager.hpp:604
bool IsRegistered() const noexcept
Checks if an message type (regular or async) is registered.
Definition manager.hpp:219
const MessageMetadata * Metadata() const noexcept
Gets metadata for a registered message type.
Definition manager.hpp:282
void ManualAsyncClear()
Manually clears async messages of a specific type from the queue.
Definition manager.hpp:586
MessageQueue & PreviousQueue() noexcept
Gets mutable reference to previous message queue.
Definition manager.hpp:327
bool HasMessages() const noexcept
Checks if any messages exist across regular and async queues.
Definition manager.hpp:228
void MergeLocalMessages(MessageQueue< OtherAllocator > &&local)
Merges messages from a local MessageQueue into the current queue.
Definition manager.hpp:208
void ManualClear()
Manually clears regular messages of a specific type from both queues.
Definition manager.hpp:577
void WriteBulk(R &&messages)
Writes multiple regular messages to the current queue in bulk.
Definition manager.hpp:560
void ApplyConsumed(const ConsumedMessagesRegistry< Alloc > &merged_consumed)
Applies consumed indices to both queues, removing consumed messages in-place.
Definition manager.hpp:422
const AsyncMessageQueue & AsyncQueue() const noexcept
Gets the async message queue (const).
Definition manager.hpp:345
void WriteAsync(T &&message)
Writes a single async message to the async queue.
Definition manager.hpp:550
MessageManager(const MessageManager &)=delete
void Update()
Updates message lifecycle without any consumed message processing.
Definition manager.hpp:400
void Write(T &&message)
Writes a single regular message to the current queue.
Definition manager.hpp:541
void WriteAsyncBulk(R &&messages)
Writes multiple async messages to the async queue in bulk.
Definition manager.hpp:569
size_type RegisteredMessageCount() const noexcept
Gets the number of registered message types (regular + async).
Definition manager.hpp:291
MessageQueue & CurrentQueue() noexcept
Gets mutable reference to current message queue.
Definition manager.hpp:309
void MergeLocalMessages(const MessageQueue< OtherAllocator > &local)
Merges messages from a local MessageQueue into the current queue.
Definition manager.hpp:195
MessageManager(MessageManager &&) noexcept=default
const MessageQueue & CurrentQueue() const noexcept
Gets const reference to current message queue.
Definition manager.hpp:300
void Clear() noexcept
Clears all messages, consumed flags, and registration data.
Definition manager.hpp:363
void Update(const ConsumedMessagesRegistry< Alloc > &consumed_registry)
Updates message lifecycle — applies consumed messages, swaps buffers, clears old messages.
Definition manager.hpp:377
void ClearAllQueues() noexcept
Clears all message data without removing registrations.
Definition manager.hpp:370
AsyncMessageQueue & AsyncQueue() noexcept
Gets the async message queue (for creating tokens, etc.).
Definition manager.hpp:336
auto CurrentMessages() const noexcept -> std::span< const T >
Gets a const span of messages of a specific type from the current queue.
Definition manager.hpp:271
const MessageQueue & PreviousQueue() const noexcept
Gets const reference to previous message queue.
Definition manager.hpp:318
auto PreviousMessages() const noexcept -> std::span< const T >
Gets a const span of messages of a specific type from the previous queue.
Definition manager.hpp:259
void Register()
Registers multiple message types.
Definition manager.hpp:500
Queue for managing multiple message types using type-erased contiguous storage.
Definition queue.hpp:26
static constexpr TypeIndex From() noexcept
Constructs a TypeIndex from a type T.
Concept for any valid message type.
Definition message.hpp:55
Concept for valid async message types.
Definition message.hpp:44
Concept for valid message types.
Definition message.hpp:29
consteval MessageClearPolicy MessageClearPolicyOf() noexcept
Gets the clear policy of an message.
Definition message.hpp:126
MessageClearPolicy
Policy for message clearing behavior.
Definition message.hpp:17
@ kAutomatic
Messages are automatically cleared after double buffer cycle.
Definition message.hpp:18
utils::TypeIndex MessageTypeIndex
Type index for messages.
Definition message.hpp:14
constexpr std::string_view MessageNameOf() noexcept
Gets the name of an message.
Definition message.hpp:97
STL namespace.
Metadata for a registered message type.
Definition manager.hpp:24
std::string_view name
Human-readable name of the message.
Definition manager.hpp:26
MessageTypeIndex type_index
Unique type index for the message.
Definition manager.hpp:25
bool is_async
Whether this message uses the async queue.
Definition manager.hpp:29
MessageClearPolicy clear_policy
Message clearing policy.
Definition manager.hpp:27