wireforge 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. wireforge/__init__.py +3 -0
  2. wireforge/_data/cmake/WireForgeConfig.cmake +29 -0
  3. wireforge/_data/cmake/WireForgeConfigVersion.cmake +15 -0
  4. wireforge/_data/cmake/WireForgeGenerate.cmake +64 -0
  5. wireforge/_data/runtime/LICENSE +21 -0
  6. wireforge/_data/runtime/include/wireforge/array_access.hpp +35 -0
  7. wireforge/_data/runtime/include/wireforge/bit_utils.hpp +226 -0
  8. wireforge/_data/runtime/include/wireforge/byte_io.hpp +387 -0
  9. wireforge/_data/runtime/include/wireforge/checksum.hpp +201 -0
  10. wireforge/_data/runtime/include/wireforge/codec_support.hpp +412 -0
  11. wireforge/_data/runtime/include/wireforge/config.hpp +41 -0
  12. wireforge/_data/runtime/include/wireforge/delegate.hpp +101 -0
  13. wireforge/_data/runtime/include/wireforge/dispatcher.hpp +339 -0
  14. wireforge/_data/runtime/include/wireforge/expr_support.hpp +38 -0
  15. wireforge/_data/runtime/include/wireforge/frame_decoder.hpp +262 -0
  16. wireforge/_data/runtime/include/wireforge/frame_layout.hpp +20 -0
  17. wireforge/_data/runtime/include/wireforge/link.hpp +223 -0
  18. wireforge/_data/runtime/include/wireforge/lock.hpp +146 -0
  19. wireforge/_data/runtime/include/wireforge/ring_buffer.hpp +226 -0
  20. wireforge/_data/runtime/include/wireforge/span.hpp +116 -0
  21. wireforge/_data/runtime/include/wireforge/static_vector.hpp +264 -0
  22. wireforge/_data/runtime/include/wireforge/status.hpp +75 -0
  23. wireforge/_data/runtime/include/wireforge/transport.hpp +95 -0
  24. wireforge/_data/runtime/include/wireforge/validation.hpp +153 -0
  25. wireforge/_data/runtime/include/wireforge/version.hpp +22 -0
  26. wireforge/_data/runtime/requirements.yaml +438 -0
  27. wireforge/_data/schema/deviations.schema.json +47 -0
  28. wireforge/_data/schema/fragment.schema.json +79 -0
  29. wireforge/_data/schema/llr_ids.schema.json +25 -0
  30. wireforge/_data/schema/project.schema.json +1240 -0
  31. wireforge/_data/schema/runtime_requirements.schema.json +34 -0
  32. wireforge/_data/ui/assets/index-6Pomu6U4.js +44 -0
  33. wireforge/_data/ui/assets/index-GVdDyUAG.css +1 -0
  34. wireforge/_data/ui/index.html +13 -0
  35. wireforge/bundle.py +177 -0
  36. wireforge/ci_check.py +155 -0
  37. wireforge/cli.py +617 -0
  38. wireforge/codec.py +897 -0
  39. wireforge/compat.py +536 -0
  40. wireforge/coverage_points.py +64 -0
  41. wireforge/cpp.py +273 -0
  42. wireforge/cpp_codec.py +440 -0
  43. wireforge/cpp_interface.py +874 -0
  44. wireforge/cpp_tests.py +1993 -0
  45. wireforge/differential.py +586 -0
  46. wireforge/docs_markdown.py +72 -0
  47. wireforge/expr.py +598 -0
  48. wireforge/findings.py +187 -0
  49. wireforge/fmt.py +351 -0
  50. wireforge/generate.py +644 -0
  51. wireforge/golden.py +253 -0
  52. wireforge/hlr_import.py +112 -0
  53. wireforge/impact.py +249 -0
  54. wireforge/importers.py +402 -0
  55. wireforge/issues.py +27 -0
  56. wireforge/layout.py +681 -0
  57. wireforge/lifecycle.py +1266 -0
  58. wireforge/lifecycle_results.py +205 -0
  59. wireforge/lifecycle_rules.py +78 -0
  60. wireforge/lifecycle_runtime.py +375 -0
  61. wireforge/llr.py +693 -0
  62. wireforge/llr_ids.py +104 -0
  63. wireforge/loader.py +422 -0
  64. wireforge/mcp_server.py +357 -0
  65. wireforge/model.py +322 -0
  66. wireforge/plain.py +15 -0
  67. wireforge/py.typed +0 -0
  68. wireforge/reqif.py +289 -0
  69. wireforge/resources.py +43 -0
  70. wireforge/results.py +377 -0
  71. wireforge/runtime_reqs.py +147 -0
  72. wireforge/schema.py +151 -0
  73. wireforge/server/__init__.py +1 -0
  74. wireforge/server/app.py +619 -0
  75. wireforge/server/oidc.py +99 -0
  76. wireforge/server/storage.py +146 -0
  77. wireforge/service.py +1354 -0
  78. wireforge/templates/command/project.yaml +38 -0
  79. wireforge/templates/raw/project.yaml +28 -0
  80. wireforge/templates/telemetry/project.yaml +51 -0
  81. wireforge/test_cases.py +475 -0
  82. wireforge/test_project.py +478 -0
  83. wireforge/trace.py +442 -0
  84. wireforge/validate.py +1241 -0
  85. wireforge/vcs.py +44 -0
  86. wireforge/vectors.py +222 -0
  87. wireforge/versioning.py +137 -0
  88. wireforge/wireshark.py +792 -0
  89. wireforge/wireshark_runtime.lua +725 -0
  90. wireforge-0.1.0.dist-info/METADATA +91 -0
  91. wireforge-0.1.0.dist-info/RECORD +96 -0
  92. wireforge-0.1.0.dist-info/WHEEL +5 -0
  93. wireforge-0.1.0.dist-info/entry_points.txt +2 -0
  94. wireforge-0.1.0.dist-info/licenses/LICENSE +98 -0
  95. wireforge-0.1.0.dist-info/licenses/runtime/LICENSE +21 -0
  96. wireforge-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,412 @@
1
+ /**
2
+ * @file codec_support.hpp
3
+ * @brief Encoder and Decoder used by generated serialize/deserialize functions.
4
+ *
5
+ * Both keep the first error ("sticky" status): after a failure every further operation does
6
+ * nothing, so generated code is a straight sequence of calls followed by `status()`.
7
+ */
8
+ #ifndef WIREFORGE_CODEC_SUPPORT_HPP
9
+ #define WIREFORGE_CODEC_SUPPORT_HPP
10
+
11
+ #include "wireforge/bit_utils.hpp"
12
+ #include "wireforge/byte_io.hpp"
13
+ #include "wireforge/checksum.hpp"
14
+ #include "wireforge/span.hpp"
15
+ #include "wireforge/static_vector.hpp"
16
+ #include "wireforge/status.hpp"
17
+
18
+ #include <cstddef>
19
+ #include <cstdint>
20
+ #include <limits>
21
+ #include <type_traits>
22
+
23
+ namespace wireforge {
24
+
25
+ /// @brief Encoding settings of one message on one interface.
26
+ struct Encoding {
27
+ Endian endian = Endian::Big; ///< Byte order of multi-byte values.
28
+ BitOrder bit_order = BitOrder::MsbFirst; ///< Placement of fields in bit groups.
29
+ bool check_encode = true; ///< Check min/max and enum values when encoding.
30
+ bool check_decode = true; ///< Check min/max and enum values when decoding.
31
+ bool allow_trailing_bytes = false; ///< Accept payload bytes after the last field.
32
+ };
33
+
34
+ /// @cond INTERNAL
35
+ namespace detail {
36
+
37
+ template <typename T> struct WireInt {
38
+ using type = T;
39
+ };
40
+ template <> struct WireInt<bool> {
41
+ using type = std::uint8_t;
42
+ };
43
+ template <> struct WireInt<char> {
44
+ using type = std::uint8_t;
45
+ };
46
+
47
+ // Integer type that carries the wire value of T (enums: their underlying type).
48
+ template <typename T>
49
+ using wire_int_t =
50
+ typename WireInt<typename std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>,
51
+ std::enable_if<true, T>>::type>::type;
52
+
53
+ // Converts without a cast when the types already match (keeps -Wuseless-cast quiet).
54
+ template <typename To, typename From> [[nodiscard]] constexpr To convert(From value) noexcept {
55
+ if constexpr (std::is_same_v<To, From>) {
56
+ return value;
57
+ } else {
58
+ return static_cast<To>(value);
59
+ }
60
+ }
61
+
62
+ } // namespace detail
63
+ /// @endcond
64
+
65
+ /**
66
+ * @brief Sequential encoder with a sticky status.
67
+ */
68
+ class Encoder {
69
+ public:
70
+ /// @brief Encodes into @p buffer with @p encoding.
71
+ Encoder(Span<std::uint8_t> buffer, const Encoding& encoding) noexcept
72
+ : writer_(buffer), encoding_(encoding) {}
73
+
74
+ /// @brief Settings in use.
75
+ [[nodiscard]] const Encoding& encoding() const noexcept { return encoding_; }
76
+ /// @brief The first error, or Status::Ok.
77
+ [[nodiscard]] Status status() const noexcept { return status_; }
78
+ /// @brief Bytes written so far.
79
+ [[nodiscard]] std::size_t position() const noexcept { return writer_.position(); }
80
+
81
+ /// @brief Records @p status unless an earlier error is already recorded.
82
+ void check(Status status) noexcept {
83
+ if (status_ == Status::Ok) {
84
+ status_ = status;
85
+ }
86
+ }
87
+
88
+ /// @brief Records @p status when encode-side value checks are enabled.
89
+ void check_value(Status status) noexcept {
90
+ if (encoding_.check_encode) {
91
+ check(status);
92
+ }
93
+ }
94
+
95
+ /**
96
+ * @brief Writes @p value as wire type T (integers, bool, char, enums, float, double).
97
+ */
98
+ template <typename T> void write(T value) noexcept {
99
+ if (status_ != Status::Ok) {
100
+ return;
101
+ }
102
+ if constexpr (std::is_same_v<T, float>) {
103
+ check(writer_.write_f32(value, encoding_.endian));
104
+ } else if constexpr (std::is_same_v<T, double>) {
105
+ check(writer_.write_f64(value, encoding_.endian));
106
+ } else {
107
+ using Wire = detail::wire_int_t<T>;
108
+ const auto wire = detail::convert<Wire>(value);
109
+ if constexpr (std::is_signed_v<Wire>) {
110
+ check(writer_.write_signed(wire, sizeof(Wire), encoding_.endian));
111
+ } else {
112
+ check(writer_.write_unsigned(wire, sizeof(Wire), encoding_.endian));
113
+ }
114
+ }
115
+ }
116
+
117
+ /**
118
+ * @brief Writes an element count as wire type W (bool counts must be 0 or 1).
119
+ */
120
+ template <typename W> void write_count(std::size_t count) noexcept {
121
+ const std::uint64_t limit = std::is_same_v<W, bool>
122
+ ? 1U
123
+ : static_cast<std::uint64_t>(std::numeric_limits<W>::max());
124
+ if (count > limit) {
125
+ check(Status::OutOfRange);
126
+ return;
127
+ }
128
+ if constexpr (std::is_same_v<W, bool>) {
129
+ write(count == 1U);
130
+ } else {
131
+ write(detail::convert<W>(count));
132
+ }
133
+ }
134
+
135
+ /// @brief Writes the first @p count characters of @p text and pads with zeros to @p width.
136
+ template <std::size_t N>
137
+ void write_text(const FixedString<N>& text, std::size_t width) noexcept {
138
+ for (std::size_t index = 0U; index < width; ++index) {
139
+ write(index < text.size() ? text[index] : '\0');
140
+ }
141
+ }
142
+
143
+ /**
144
+ * @brief Adds a field to a bit group (bool, enum, signed or unsigned integer).
145
+ */
146
+ template <typename T> void put(BitPacker& group, T value, unsigned bits) noexcept {
147
+ using Wire = detail::wire_int_t<T>;
148
+ const auto wire = detail::convert<Wire>(value);
149
+ if constexpr (std::is_signed_v<Wire>) {
150
+ check(group.put_signed(wire, bits));
151
+ } else {
152
+ check(group.put(wire, bits));
153
+ }
154
+ }
155
+
156
+ /// @brief Writes a complete bit group.
157
+ void write_bits(const BitPacker& group) noexcept {
158
+ if (status_ == Status::Ok) {
159
+ check(writer_.write_bits(group, encoding_.endian));
160
+ }
161
+ }
162
+
163
+ /**
164
+ * @brief Copies @p bytes onto the wire unchanged (no endian or bit-order transform).
165
+ *
166
+ * For a user type's own encode(): the type owns its wire representation entirely. Sets
167
+ * Status::BufferTooSmall (checked by status()) when they do not fit.
168
+ */
169
+ void write_bytes(Span<const std::uint8_t> bytes) noexcept {
170
+ if (status_ == Status::Ok) {
171
+ check(writer_.write_bytes(bytes));
172
+ }
173
+ }
174
+
175
+ /**
176
+ * @brief Writes the checksum C of the bytes written in [@p begin, @p end).
177
+ */
178
+ template <typename C> void write_checksum(std::size_t begin, std::size_t end) noexcept {
179
+ if (status_ != Status::Ok) {
180
+ return;
181
+ }
182
+ const Span<const std::uint8_t> covered = writer_.written().subspan(begin, end - begin);
183
+ check(writer_.write_unsigned(compute_checksum<C>(covered), C::kSize, encoding_.endian));
184
+ }
185
+
186
+ private:
187
+ ByteWriter writer_;
188
+ Encoding encoding_;
189
+ Status status_ = Status::Ok;
190
+ };
191
+
192
+ /**
193
+ * @brief Sequential decoder with a sticky status.
194
+ */
195
+ class Decoder {
196
+ public:
197
+ /// @brief Decodes @p input with @p encoding.
198
+ Decoder(Span<const std::uint8_t> input, const Encoding& encoding) noexcept
199
+ : input_(input), reader_(input), encoding_(encoding) {}
200
+
201
+ /// @brief Settings in use.
202
+ [[nodiscard]] const Encoding& encoding() const noexcept { return encoding_; }
203
+ /// @brief The first error, or Status::Ok.
204
+ [[nodiscard]] Status status() const noexcept { return status_; }
205
+ /// @brief Bytes consumed so far.
206
+ [[nodiscard]] std::size_t position() const noexcept { return reader_.position(); }
207
+ /// @brief Bytes not consumed yet.
208
+ [[nodiscard]] std::size_t remaining() const noexcept { return reader_.remaining(); }
209
+
210
+ /// @brief Records @p status unless an earlier error is already recorded.
211
+ void check(Status status) noexcept {
212
+ if (status_ == Status::Ok) {
213
+ status_ = status;
214
+ }
215
+ }
216
+
217
+ /// @brief Records @p status when decode-side value checks are enabled.
218
+ void check_value(Status status) noexcept {
219
+ if (encoding_.check_decode) {
220
+ check(status);
221
+ }
222
+ }
223
+
224
+ /**
225
+ * @brief Reads a value of wire type T into @p out (a bool must be 0 or 1).
226
+ */
227
+ template <typename T> void read(T& out) noexcept {
228
+ if (status_ != Status::Ok) {
229
+ return;
230
+ }
231
+ if constexpr (std::is_same_v<T, float>) {
232
+ check(reader_.read_f32(out, encoding_.endian));
233
+ } else if constexpr (std::is_same_v<T, double>) {
234
+ check(reader_.read_f64(out, encoding_.endian));
235
+ } else {
236
+ using Wire = detail::wire_int_t<T>;
237
+ Wire wire{};
238
+ read_int(wire);
239
+ assign(out, wire);
240
+ }
241
+ }
242
+
243
+ /// @brief Reads an element count stored as wire type W.
244
+ template <typename W> [[nodiscard]] std::size_t read_count() noexcept {
245
+ W value{};
246
+ read(value);
247
+ return detail::convert<std::size_t>(value);
248
+ }
249
+
250
+ /// @brief Reads a value of wire type T and fails with ConstMismatch unless it is @p expected.
251
+ template <typename T> void expect(T expected) noexcept {
252
+ T value{};
253
+ read(value);
254
+ if (status_ == Status::Ok && !(value == expected)) {
255
+ check(Status::ConstMismatch);
256
+ }
257
+ }
258
+
259
+ /// @brief Skips @p count bytes.
260
+ void skip(std::size_t count) noexcept {
261
+ if (status_ == Status::Ok) {
262
+ check(reader_.skip(count));
263
+ }
264
+ }
265
+
266
+ /**
267
+ * @brief Reads @p out.size() bytes unchanged (no endian or bit-order transform).
268
+ *
269
+ * For a user type's own decode(): the type owns its wire representation entirely. Sets
270
+ * Status::Truncated (checked by status()) when fewer bytes remain.
271
+ */
272
+ void read_bytes(Span<std::uint8_t> out) noexcept {
273
+ if (status_ == Status::Ok) {
274
+ check(reader_.read_bytes(out));
275
+ }
276
+ }
277
+
278
+ /**
279
+ * @brief Reads @p width characters; a fixed-width text drops trailing zeros.
280
+ */
281
+ template <std::size_t N>
282
+ void read_text(FixedString<N>& text, std::size_t width, bool fixed) noexcept {
283
+ if (width > N) {
284
+ check(Status::CountExceedsMax);
285
+ }
286
+ if (status_ != Status::Ok) {
287
+ return;
288
+ }
289
+ static_cast<void>(text.resize(width));
290
+ std::size_t length = 0U;
291
+ for (std::size_t index = 0U; index < width; ++index) {
292
+ read(text[index]);
293
+ length = text[index] != '\0' || !fixed ? index + 1U : length;
294
+ }
295
+ static_cast<void>(text.resize(length));
296
+ }
297
+
298
+ /// @brief Sets the size of @p container to @p count (CountExceedsMax outside [low, capacity]).
299
+ template <typename Container>
300
+ void resize(Container& container, std::size_t count, std::size_t low) noexcept {
301
+ if (count < low || count > Container::capacity()) {
302
+ check(Status::CountExceedsMax);
303
+ }
304
+ if (status_ == Status::Ok) {
305
+ static_cast<void>(container.resize(count));
306
+ }
307
+ }
308
+
309
+ /**
310
+ * @brief Number of elements of @p element_size bytes in the rest of the input.
311
+ * @return 0 with InvalidLength when the rest is not a multiple of the element size.
312
+ */
313
+ [[nodiscard]] std::size_t fill_count(std::size_t element_size) noexcept {
314
+ if (element_size == 0U || remaining() % element_size != 0U) {
315
+ check(Status::InvalidLength);
316
+ return 0U;
317
+ }
318
+ return remaining() / element_size;
319
+ }
320
+
321
+ /// @brief Reads a bit group of @p total_bits bits.
322
+ [[nodiscard]] BitUnpacker read_bits(unsigned total_bits) noexcept {
323
+ BitUnpacker group(0U, total_bits, encoding_.bit_order);
324
+ if (status_ == Status::Ok) {
325
+ check(reader_.read_bits(group, total_bits, encoding_.bit_order, encoding_.endian));
326
+ }
327
+ return group;
328
+ }
329
+
330
+ /// @brief Extracts the next field of a bit group into @p out.
331
+ template <typename T> void get(BitUnpacker& group, T& out, unsigned bits) noexcept {
332
+ using Wire = detail::wire_int_t<T>;
333
+ if constexpr (std::is_signed_v<Wire>) {
334
+ std::int64_t value = 0;
335
+ check(group.get_signed(value, bits));
336
+ assign(out, detail::convert<Wire>(value));
337
+ } else {
338
+ std::uint64_t value = 0U;
339
+ check(group.get(value, bits));
340
+ assign(out, detail::convert<Wire>(value));
341
+ }
342
+ }
343
+
344
+ /// @brief Extracts a constant field of a bit group (ConstMismatch when it differs).
345
+ template <typename T> void expect_bits(BitUnpacker& group, T expected, unsigned bits) noexcept {
346
+ T value{};
347
+ get(group, value, bits);
348
+ if (status_ == Status::Ok && !(value == expected)) {
349
+ check(Status::ConstMismatch);
350
+ }
351
+ }
352
+
353
+ /// @brief Skips a reserved field of a bit group.
354
+ void skip_bits(BitUnpacker& group, unsigned bits) noexcept {
355
+ std::uint64_t ignored = 0U;
356
+ check(group.get(ignored, bits));
357
+ }
358
+
359
+ /**
360
+ * @brief Reads a checksum C and compares it with the checksum of the input [@p begin, @p end).
361
+ */
362
+ template <typename C> void verify_checksum(std::size_t begin, std::size_t end) noexcept {
363
+ std::uint64_t received = 0U;
364
+ if (status_ == Status::Ok) {
365
+ check(reader_.read_unsigned(received, C::kSize, encoding_.endian));
366
+ }
367
+ if (status_ == Status::Ok &&
368
+ compute_checksum<C>(input_.subspan(begin, end - begin)) != received) {
369
+ check(Status::ChecksumMismatch);
370
+ }
371
+ }
372
+
373
+ /// @brief Fails with TrailingBytes when input is left and trailing bytes are not allowed.
374
+ void finish() noexcept {
375
+ if (remaining() > 0U && !encoding_.allow_trailing_bytes) {
376
+ check(Status::TrailingBytes);
377
+ }
378
+ }
379
+
380
+ private:
381
+ template <typename Wire> void read_int(Wire& wire) noexcept {
382
+ if constexpr (std::is_signed_v<Wire>) {
383
+ std::int64_t value = 0;
384
+ check(reader_.read_signed(value, sizeof(Wire), encoding_.endian));
385
+ wire = detail::convert<Wire>(value);
386
+ } else {
387
+ std::uint64_t value = 0U;
388
+ check(reader_.read_unsigned(value, sizeof(Wire), encoding_.endian));
389
+ wire = detail::convert<Wire>(value);
390
+ }
391
+ }
392
+
393
+ template <typename T, typename Wire> void assign(T& out, Wire wire) noexcept {
394
+ if constexpr (std::is_same_v<T, bool>) {
395
+ if (wire > 1U) {
396
+ check(Status::OutOfRange);
397
+ }
398
+ out = wire == 1U;
399
+ } else {
400
+ out = detail::convert<T>(wire);
401
+ }
402
+ }
403
+
404
+ Span<const std::uint8_t> input_;
405
+ ByteReader reader_;
406
+ Encoding encoding_;
407
+ Status status_ = Status::Ok;
408
+ };
409
+
410
+ } // namespace wireforge
411
+
412
+ #endif // WIREFORGE_CODEC_SUPPORT_HPP
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @file config.hpp
3
+ * @brief Build-time configuration of the WireForge runtime.
4
+ *
5
+ * Every option is a macro that can be set on the compiler command line or in a user header named by
6
+ * `WIREFORGE_CONFIG_HEADER`, which is included first.
7
+ *
8
+ * | Macro | Effect |
9
+ * |---------------------------|----------------------------------------------------------------|
10
+ * | `WIREFORGE_CONFIG_HEADER` | Header (with quotes or angle brackets) included before anything |
11
+ * | `WIREFORGE_NO_STD_MUTEX` | Do not include `<mutex>`; wireforge::StdMutexLock is unavailable |
12
+ * | `WIREFORGE_DEFAULT_LOCK` | Lock policy type used as wireforge::DefaultLock |
13
+ * | `WIREFORGE_CRC_TABLELESS` | Predefined CRC aliases use the bitwise mode instead of a table |
14
+ */
15
+ #ifndef WIREFORGE_CONFIG_HPP
16
+ #define WIREFORGE_CONFIG_HPP
17
+
18
+ #if defined(WIREFORGE_CONFIG_HEADER)
19
+ #include WIREFORGE_CONFIG_HEADER
20
+ #endif
21
+
22
+ /// @brief Compile-time view of the configuration macros.
23
+ namespace wireforge::config {
24
+
25
+ #if defined(WIREFORGE_NO_STD_MUTEX)
26
+ inline constexpr bool kHasStdMutex = false;
27
+ #else
28
+ /// @brief true unless `WIREFORGE_NO_STD_MUTEX` is defined.
29
+ inline constexpr bool kHasStdMutex = true;
30
+ #endif
31
+
32
+ #if defined(WIREFORGE_CRC_TABLELESS)
33
+ inline constexpr bool kCrcUseTable = false;
34
+ #else
35
+ /// @brief Default table mode of wireforge::Crc; false when `WIREFORGE_CRC_TABLELESS` is defined.
36
+ inline constexpr bool kCrcUseTable = true;
37
+ #endif
38
+
39
+ } // namespace wireforge::config
40
+
41
+ #endif // WIREFORGE_CONFIG_HPP
@@ -0,0 +1,101 @@
1
+ /**
2
+ * @file delegate.hpp
3
+ * @brief Small non-owning callable wrapper that never allocates.
4
+ */
5
+ #ifndef WIREFORGE_DELEGATE_HPP
6
+ #define WIREFORGE_DELEGATE_HPP
7
+
8
+ #include <type_traits>
9
+ #include <utility>
10
+
11
+ namespace wireforge {
12
+
13
+ /// @brief Declared only; see the specialization for function types.
14
+ template <typename Signature> class Delegate;
15
+
16
+ /**
17
+ * @brief Non-owning reference to a free function, a member function bound to an object, or a
18
+ * callable object.
19
+ *
20
+ * The delegate stores two pointers and never allocates. Objects bound with bind() or
21
+ * from_callable() are referenced, not copied: they must outlive the delegate.
22
+ * @code
23
+ * Delegate<int(int)> d1 = Delegate<int(int)>::from_function(&twice);
24
+ * Delegate<int(int)> d2 = Delegate<int(int)>::bind<&Counter::add>(counter);
25
+ * auto lambda = [](int value) { return value + 1; };
26
+ * Delegate<int(int)> d3 = Delegate<int(int)>::from_callable(lambda);
27
+ * @endcode
28
+ * @tparam R Return type.
29
+ * @tparam Args Parameter types.
30
+ */
31
+ template <typename R, typename... Args> class Delegate<R(Args...)> {
32
+ public:
33
+ /// @brief Free function type accepted by from_function().
34
+ using Function = R (*)(Args...);
35
+
36
+ /// @brief Creates an empty delegate; calling it is not allowed.
37
+ constexpr Delegate() noexcept = default;
38
+
39
+ /// @brief Refers to the free function @p function.
40
+ [[nodiscard]] static constexpr Delegate from_function(Function function) noexcept {
41
+ Delegate delegate;
42
+ delegate.function_ = function;
43
+ delegate.stub_ = function == nullptr ? nullptr : &call_function;
44
+ return delegate;
45
+ }
46
+
47
+ /**
48
+ * @brief Refers to member function @p Method called on @p object.
49
+ * @tparam Method Pointer to a member function of T with this delegate's signature.
50
+ */
51
+ template <auto Method, typename T>
52
+ [[nodiscard]] static constexpr Delegate bind(T& object) noexcept {
53
+ Delegate delegate;
54
+ delegate.object_ = &object;
55
+ delegate.stub_ = &call_member<T, Method>;
56
+ return delegate;
57
+ }
58
+
59
+ /// @brief Refers to the callable object @p callable (for example a lambda); it must be an
60
+ /// lvalue.
61
+ template <typename F>
62
+ [[nodiscard]] static constexpr Delegate from_callable(F& callable) noexcept {
63
+ static_assert(!std::is_const_v<F>, "bind a non-const callable object");
64
+ Delegate delegate;
65
+ delegate.object_ = &callable;
66
+ delegate.stub_ = &call_callable<F>;
67
+ return delegate;
68
+ }
69
+
70
+ /// @brief true when the delegate refers to something callable.
71
+ [[nodiscard]] constexpr explicit operator bool() const noexcept { return stub_ != nullptr; }
72
+
73
+ /**
74
+ * @brief Calls the referenced function.
75
+ * @pre The delegate is not empty.
76
+ */
77
+ R operator()(Args... args) const { return stub_(*this, std::forward<Args>(args)...); }
78
+
79
+ private:
80
+ using Stub = R (*)(const Delegate&, Args...);
81
+
82
+ static R call_function(const Delegate& self, Args... args) {
83
+ return self.function_(std::forward<Args>(args)...);
84
+ }
85
+
86
+ template <typename T, auto Method> static R call_member(const Delegate& self, Args... args) {
87
+ return (static_cast<T*>(self.object_)->*Method)(std::forward<Args>(args)...);
88
+ }
89
+
90
+ template <typename F> static R call_callable(const Delegate& self, Args... args) {
91
+ return (*static_cast<F*>(self.object_))(std::forward<Args>(args)...);
92
+ }
93
+
94
+ void* object_ = nullptr;
95
+ Function function_ = nullptr;
96
+ Stub stub_ = nullptr;
97
+ };
98
+
99
+ } // namespace wireforge
100
+
101
+ #endif // WIREFORGE_DELEGATE_HPP