Skip to main content

TypeIndexedDoubleBuffer.ixx File

Double-buffered, type-indexed message system for decoupled inter-system communication. More...

Included Headers

#include <memory> #include <span> #include <vector> #include <helios.core.buffer.ReadBuffer> #include <helios.core.buffer.WriteBuffer> #include <helios.core.buffer.ReadWriteDoubleBuffer> #include <helios.core.buffer.DoubleBuffer> #include <helios.core.data.TypeIndexer>

Namespaces Index

namespacehelios
namespacecore

Core utilities shared across the helios engine. More...

namespacebuffer

Double-buffering infrastructure for thread-safe message passing. More...

Classes Index

classTypeIndexedDoubleBuffer<Indexer>

Central hub for publishing and consuming typed messages. More...

classWriteSink

Lightweight handle for pushing messages to a TypeIndexedDoubleBuffer. More...

classReadSource

Lightweight handle for reading messages from a TypeIndexedDoubleBuffer. More...

Description

Double-buffered, type-indexed message system for decoupled inter-system communication.

File Listing

The file content with the documentation metadata removed is:

1/**
2 * @file TypeIndexedDoubleBuffer.ixx
3 * @brief Double-buffered, type-indexed message system for decoupled inter-system communication.
4 */
5module;
6
7#include <memory>
8#include <span>
9#include <vector>
10
11export module helios.core.buffer.TypeIndexedDoubleBuffer;
12
13import helios.core.data.TypeIndexer;
14
15import helios.core.buffer.DoubleBuffer;
16import helios.core.buffer.WriteBuffer;
17import helios.core.buffer.ReadBuffer;
18import helios.core.buffer.ReadWriteDoubleBuffer;
19
20
21export namespace helios::core::buffer {
22
23
24
25 template<typename Indexer>
26 /**
27 * @class TypeIndexedDoubleBuffer
28 * @brief Central hub for publishing and consuming typed messages.
29 *
30 * @details TypeIndexedDoubleBuffer provides a double-buffered, type-indexed message system
31 * for decoupled inter-system communication. Systems push messages during their update phase,
32 * then at frame boundaries `swapBuffers()` is called to make those messages available for reading.
33 *
34 * Each message type T gets its own dedicated ReadWriteDoubleBuffer, indexed via TypeIndexer
35 * for O(1) access. This design enables efficient, allocation-friendly inter-system communication.
36 *
37 * Example usage:
38 * ```cpp
39 * // Define a message
40 * struct CollisionMessage {
41 * EntityId a;
42 * EntityId b;
43 * vec3f contact;
44 * };
45 *
46 * // In collision system
47 * messageBuffer.push<CollisionMessage>(entityA, entityB, contactPoint);
48 *
49 * // At frame boundary
50 * messageBuffer.swapBuffers();
51 *
52 * // In damage system (next phase or frame)
53 * for (const auto& msg : messageBuffer.read<CollisionMessage>()) {
54 * applyDamage(msg.a, msg.b);
55 * }
56 * ```
57 *
58 * @tparam Indexer The TypeIndexer used for mapping message types to buffer indices.
59 */
61
62 /**
63 * @brief Type-erased storage for message buffers, indexed by type.
64 */
65 std::vector<std::unique_ptr<DoubleBuffer>> buffers_;
66
67 /**
68 * @brief Returns the buffer index for message type T.
69 *
70 * @tparam T The message type.
71 *
72 * @return The index assigned to type T by the Indexer.
73 */
74 template<typename T>
75 static size_t index() {
76 return Indexer::template typeIndex<T>();
77 }
78
79 public:
80
81 /**
82 * @brief Default destructor.
83 */
85
86 /**
87 * @brief Default constructor.
88 */
90
91 /**
92 * @brief Pushes a message of type T to the write buffer.
93 *
94 * @tparam T The message type.
95 * @tparam Args Constructor argument types for T.
96 *
97 * @param args Arguments forwarded to T's constructor.
98 */
99 template<typename T, typename... Args>
100 void push(Args&&... args) {
101 getOrCreateBuffer<T>().push(std::forward<Args>(args)...);
102 }
103
104 /**
105 * @brief Returns a read-only view of all messages of type T.
106 *
107 * @details Returns messages that were written before the last `swapBuffers()` call.
108 * If no messages of type T exist, returns an empty span.
109 *
110 * @tparam T The message type to read.
111 *
112 * @return A span over all messages of type T in the read buffer.
113 */
114 template<typename T>
115 std::span<const T> read() const noexcept {
116
117 const size_t idx = index<T>();
118
119 if (buffers_.size() <= idx || !buffers_[idx]) {
120 return {};
121 }
122
123 return static_cast<ReadWriteDoubleBuffer<T>*>(buffers_[idx].get())->read();
124 }
125
126 /**
127 * @brief Pre-allocates capacity for messages of type T.
128 *
129 * @details Call during initialization to avoid allocations during gameplay.
130 *
131 * @tparam T The message type.
132 *
133 * @param size The number of messages to reserve capacity for.
134 */
135 template<typename T>
136 void reserve(size_t size) {
137 getOrCreateBuffer<T>().reserve(size);
138 }
139
140 /**
141 * @brief Gets or creates the buffer for message type T.
142 *
143 * @tparam T The message type.
144 *
145 * @return Reference to the ReadWriteDoubleBuffer for type T.
146 */
147 template<typename T>
149 const size_t idx = index<T>();
150
151 if (buffers_.size() <= idx) {
152 buffers_.resize(idx + 1);
153 }
154
155 if (!buffers_[idx]) {
156 buffers_[idx] = std::make_unique<ReadWriteDoubleBuffer<T>>();
157 }
158
159 return *static_cast<ReadWriteDoubleBuffer<T>*>(buffers_[idx].get());
160 }
161
162 /**
163 * @brief Swaps read and write buffers for all message types.
164 *
165 * @details Call once per frame at a consistent point (e.g., end of update phase).
166 * After this call, messages pushed in the current frame become readable.
167 */
168 void swapBuffers() {
169 for (auto& buffer : buffers_) {
170 if (buffer) {
171 buffer->swap();
172 }
173 }
174 }
175
176 /**
177 * @brief Clears all read buffers.
178 *
179 * @details Typically not needed as `swapBuffers()` clears read buffers automatically.
180 */
182 for (auto& buffer : buffers_) {
183 if (buffer) {
184 buffer->clearReadBuffer();
185 }
186 }
187 }
188
189 /**
190 * @brief Clears all write buffers without swapping.
191 *
192 * @details Use to discard messages that were pushed but should not be processed.
193 */
195 for (auto& buffer : buffers_) {
196 if (buffer) {
197 buffer->clearWriteBuffer();
198 }
199
200 }
201 }
202
203 /**
204 * @brief Clears both read and write buffers for all message types.
205 *
206 * @details Completely resets the buffer state, discarding all messages.
207 */
208 void clearAll() {
211 }
212
213 /**
214 * @class WriteSink
215 * @brief Lightweight handle for pushing messages to a TypeIndexedDoubleBuffer.
216 *
217 * @details WriteSink provides a focused interface for message producers that only
218 * need write access. It holds a non-owning pointer to a TypeIndexedDoubleBuffer
219 * and exposes only the push() operation.
220 *
221 * This class is useful for dependency injection where systems should be able to
222 * publish messages without having access to buffer management operations like
223 * swapBuffers() or clearReadBuffers().
224 */
225 class WriteSink {
226
227 /**
228 * @brief Non-owning pointer to the parent buffer.
229 */
230 TypeIndexedDoubleBuffer* db_ = nullptr;
231
232 public:
233
234 /**
235 * @brief Constructs a WriteSink from a TypeIndexedDoubleBuffer reference.
236 *
237 * @param db The buffer to write to. Must remain valid for the lifetime of this sink.
238 */
239 explicit WriteSink(TypeIndexedDoubleBuffer& db) noexcept : db_(&db) {}
240
241 /**
242 * @brief Constructs and pushes a message of type E to the buffer.
243 *
244 * @tparam E The message type.
245 * @tparam Args Constructor argument types for E.
246 *
247 * @param args Arguments forwarded to E's constructor.
248 */
249 template<typename E, typename... Args>
250 void push(Args&&... args) noexcept {
251 db_->template push <E>(std::forward<Args>(args)...);
252 }
253 };
254
255 /**
256 * @class ReadSource
257 * @brief Lightweight handle for reading messages from a TypeIndexedDoubleBuffer.
258 *
259 * @details ReadSource provides a focused interface for message consumers that only
260 * need read access. It holds a non-owning pointer to a TypeIndexedDoubleBuffer
261 * and exposes only the read() operation.
262 *
263 * This class is useful for dependency injection where systems should be able to
264 * consume messages without having access to buffer management operations.
265 */
266 class ReadSource {
267
268 /**
269 * @brief Non-owning pointer to the parent buffer.
270 */
271 TypeIndexedDoubleBuffer* db_ = nullptr;
272
273 public:
274
275 /**
276 * @brief Constructs a ReadSource from a TypeIndexedDoubleBuffer reference.
277 *
278 * @param db The buffer to read from. Must remain valid for the lifetime of this source.
279 */
280 explicit ReadSource(TypeIndexedDoubleBuffer& db) noexcept : db_(&db) {}
281
282 /**
283 * @brief Returns a read-only view of messages of type E.
284 *
285 * @tparam E The message type to read.
286 *
287 * @return A span over all messages of type E in the read buffer.
288 */
289 template<typename E>
290 std::span<const E> read() const noexcept {
291 return db_->template read<E>();
292 }
293 };
294
295 /**
296 * @brief Creates a WriteSink handle for this buffer.
297 *
298 * @return A WriteSink that can push messages to this buffer.
299 */
300 [[nodiscard]] WriteSink writeSink() noexcept {
301 return WriteSink(*this);
302 }
303
304 /**
305 * @brief Creates a ReadSource handle for this buffer.
306 *
307 * @return A ReadSource that can read messages from this buffer.
308 */
309 [[nodiscard]] ReadSource readSource() noexcept {
310 return ReadSource(*this);
311 }
312
313 };
314
315
316}
317

Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.15.0.