@micro-os-plus/micro-test-plus 3.3.1 → 4.1.0

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 (65) hide show
  1. package/CHANGELOG.md +412 -2
  2. package/CMakeLists.txt +134 -28
  3. package/README.md +3 -2
  4. package/config/xcdl-build.json +11 -4
  5. package/include/micro-os-plus/micro-test-plus/README.md +6 -0
  6. package/include/micro-os-plus/micro-test-plus/deferred-reporter.h +267 -0
  7. package/include/micro-os-plus/micro-test-plus/detail.h +272 -1425
  8. package/include/micro-os-plus/micro-test-plus/exceptions.h +125 -0
  9. package/include/micro-os-plus/micro-test-plus/expression-formatter.h +669 -0
  10. package/include/micro-os-plus/micro-test-plus/function-comparators.h +15 -7
  11. package/include/micro-os-plus/micro-test-plus/inlines/{details-inlines.h → deferred-reporter-inlines.h} +66 -44
  12. package/include/micro-os-plus/micro-test-plus/inlines/detail-inlines.h +711 -0
  13. package/include/micro-os-plus/micro-test-plus/inlines/exceptions-inline.h +137 -0
  14. package/include/micro-os-plus/micro-test-plus/inlines/{test-reporter-inlines.h → expression-formatter-inlines.h} +232 -198
  15. package/include/micro-os-plus/micro-test-plus/inlines/function-comparators-inlines.h +24 -20
  16. package/include/micro-os-plus/micro-test-plus/inlines/literals-inlines.h +50 -31
  17. package/include/micro-os-plus/micro-test-plus/inlines/math-inlines.h +25 -19
  18. package/include/micro-os-plus/micro-test-plus/inlines/operators-inlines.h +275 -0
  19. package/include/micro-os-plus/micro-test-plus/inlines/reflection-inlines.h +39 -21
  20. package/include/micro-os-plus/micro-test-plus/inlines/reporter-inlines.h +205 -0
  21. package/include/micro-os-plus/micro-test-plus/inlines/runner-inlines.h +151 -0
  22. package/include/micro-os-plus/micro-test-plus/inlines/runner-totals-inlines.h +152 -0
  23. package/include/micro-os-plus/micro-test-plus/inlines/test-inlines.h +555 -0
  24. package/include/micro-os-plus/micro-test-plus/inlines/timings-inlines.h +120 -0
  25. package/include/micro-os-plus/micro-test-plus/inlines/type-traits-inlines.h +231 -0
  26. package/include/micro-os-plus/micro-test-plus/inlines/utility-inlines.h +126 -0
  27. package/include/micro-os-plus/micro-test-plus/literals.h +12 -17
  28. package/include/micro-os-plus/micro-test-plus/math.h +14 -6
  29. package/include/micro-os-plus/micro-test-plus/operators.h +53 -209
  30. package/include/micro-os-plus/micro-test-plus/reflection.h +8 -4
  31. package/include/micro-os-plus/micro-test-plus/{test-reporter-basic.h → reporter-human.h} +80 -74
  32. package/include/micro-os-plus/micro-test-plus/{test-reporter-tap.h → reporter-tap.h} +77 -71
  33. package/include/micro-os-plus/micro-test-plus/reporter.h +619 -0
  34. package/include/micro-os-plus/micro-test-plus/runner-totals.h +250 -0
  35. package/include/micro-os-plus/micro-test-plus/runner.h +472 -0
  36. package/include/micro-os-plus/micro-test-plus/test.h +1013 -0
  37. package/include/micro-os-plus/micro-test-plus/timings.h +363 -0
  38. package/include/micro-os-plus/micro-test-plus/type-traits.h +223 -577
  39. package/include/micro-os-plus/micro-test-plus/utility.h +136 -0
  40. package/include/micro-os-plus/micro-test-plus.h +42 -236
  41. package/meson.build +11 -6
  42. package/package.json +11 -3
  43. package/src/deferred-reporter.cpp +137 -0
  44. package/src/expression-formatter.cpp +289 -0
  45. package/src/reflection.cpp +97 -0
  46. package/src/reporter-human.cpp +816 -0
  47. package/src/reporter-tap.cpp +772 -0
  48. package/src/reporter.cpp +481 -0
  49. package/src/runner-totals.cpp +98 -0
  50. package/src/runner.cpp +669 -0
  51. package/src/test.cpp +503 -0
  52. package/src/timings.cpp +210 -0
  53. package/src/utility.cpp +163 -0
  54. package/.cmake-format.yaml +0 -11
  55. package/include/micro-os-plus/micro-test-plus/inlines/micro-test-plus-inlines.h +0 -313
  56. package/include/micro-os-plus/micro-test-plus/inlines/test-suite-inlines.h +0 -115
  57. package/include/micro-os-plus/micro-test-plus/test-reporter.h +0 -846
  58. package/include/micro-os-plus/micro-test-plus/test-runner.h +0 -281
  59. package/include/micro-os-plus/micro-test-plus/test-suite.h +0 -492
  60. package/src/micro-test-plus.cpp +0 -316
  61. package/src/test-reporter-basic.cpp +0 -466
  62. package/src/test-reporter-tap.cpp +0 -530
  63. package/src/test-reporter.cpp +0 -399
  64. package/src/test-runner.cpp +0 -311
  65. package/src/test-suite.cpp +0 -304
@@ -0,0 +1,711 @@
1
+ /*
2
+ * This file is part of the µOS++ project (https://micro-os-plus.github.io/).
3
+ * Copyright (c) 2021-2026 Liviu Ionescu. All rights reserved.
4
+ *
5
+ * Permission to use, copy, modify, and/or distribute this software for any
6
+ * purpose is hereby granted, under the terms of the MIT license.
7
+ *
8
+ * If a copy of the license was not distributed with this file, it can be
9
+ * obtained from https://opensource.org/licenses/mit.
10
+ *
11
+ * Major parts of the code are inspired from v1.1.8 of the Boost UT project,
12
+ * released under the terms of the Boost Version 1.0 Software License,
13
+ * which can be obtained from https://www.boost.org/LICENSE_1_0.txt.
14
+ */
15
+
16
+ // ----------------------------------------------------------------------------
17
+
18
+ /**
19
+ * @file
20
+ * @brief C++ header file with inline implementations for the µTest++
21
+ * internal detail namespace.
22
+ *
23
+ * @details
24
+ * This header provides the out-of-line template and inline implementations
25
+ * for all types declared in `detail.h`. It defines the bodies of the
26
+ * generic getter, the unary and binary operator base class templates, all
27
+ * relational and logical comparator constructors, and the callable operator
28
+ * types (`throws_`, `nothrow_`).
29
+ *
30
+ * Separating the implementations from the declarations keeps `detail.h`
31
+ * concise and focused on the interface, whilst grouping the complex
32
+ * lambda-based constructor bodies here for maintainability.
33
+ *
34
+ * All definitions reside within the
35
+ * `micro_os_plus::micro_test_plus::detail` namespace, ensuring clear
36
+ * separation from user code and minimising the risk of naming conflicts.
37
+ *
38
+ * This file is intended solely for internal use within the framework and
39
+ * should not be included directly by user code.
40
+ */
41
+
42
+ #ifndef MICRO_TEST_PLUS_DETAIL_INLINES_H_
43
+ #define MICRO_TEST_PLUS_DETAIL_INLINES_H_
44
+
45
+ // ----------------------------------------------------------------------------
46
+
47
+ #ifdef __cplusplus
48
+
49
+ // ----------------------------------------------------------------------------
50
+
51
+ #include <charconv>
52
+
53
+ // ----------------------------------------------------------------------------
54
+
55
+ #if defined(__GNUC__)
56
+ #pragma GCC diagnostic push
57
+ #pragma GCC diagnostic ignored "-Waggregate-return"
58
+ #if defined(__clang__)
59
+ #pragma clang diagnostic ignored "-Wc++98-compat"
60
+ #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
61
+ #endif
62
+ #endif
63
+
64
+ // ============================================================================
65
+
66
+ namespace micro_os_plus::micro_test_plus
67
+ {
68
+ namespace detail
69
+ {
70
+ // ========================================================================
71
+
72
+ /**
73
+ * @details
74
+ * If the type `T` provides a `get()` member function, it is invoked
75
+ * and its result returned. Otherwise the argument itself is returned
76
+ * unchanged. The selection is performed at compile time via
77
+ * `if constexpr` with an inline `requires` expression.
78
+ */
79
+ template <class T>
80
+ constexpr auto
81
+ get (const T& t)
82
+ {
83
+ if constexpr (requires { t.get (); })
84
+ return t.get ();
85
+ else
86
+ return t;
87
+ }
88
+
89
+ // ------------------------------------------------------------------------
90
+
91
+ /**
92
+ * @details
93
+ * Stores the operand and the pre-computed boolean result of the
94
+ * operation in the corresponding private members.
95
+ */
96
+ template <class T>
97
+ constexpr unary_op_<T>::unary_op_ (const T& t, bool value)
98
+ : t_{ t }, value_{ value }
99
+ {
100
+ }
101
+
102
+ /**
103
+ * @details
104
+ * Returns the pre-computed boolean result stored by the derived
105
+ * comparator's constructor.
106
+ */
107
+ template <class T>
108
+ constexpr unary_op_<T>::
109
+ operator bool () const
110
+ {
111
+ return value_;
112
+ }
113
+
114
+ /**
115
+ * @details
116
+ * Returns the wrapped operand, applying the generic getter to ensure
117
+ * correct extraction for both custom and standard types.
118
+ */
119
+ template <class T>
120
+ constexpr auto
121
+ unary_op_<T>::operand () const
122
+ {
123
+ return get (t_);
124
+ }
125
+
126
+ // ------------------------------------------------------------------------
127
+
128
+ /**
129
+ * @details
130
+ * Stores the two operands and the pre-computed boolean result of the
131
+ * comparison in the corresponding private members.
132
+ */
133
+ template <class Lhs_T, class Rhs_T>
134
+ constexpr binary_op_<Lhs_T, Rhs_T>::binary_op_ (const Lhs_T& lhs,
135
+ const Rhs_T& rhs,
136
+ bool value)
137
+ : lhs_{ lhs }, rhs_{ rhs }, value_{ value }
138
+ {
139
+ }
140
+
141
+ /**
142
+ * @details
143
+ * Returns the pre-computed boolean result stored by the derived
144
+ * comparator's constructor.
145
+ */
146
+ template <class Lhs_T, class Rhs_T>
147
+ constexpr binary_op_<Lhs_T, Rhs_T>::
148
+ operator bool () const
149
+ {
150
+ return value_;
151
+ }
152
+
153
+ /**
154
+ * @details
155
+ * Returns the value of the left-hand operand, applying the generic
156
+ * getter to ensure correct extraction for both custom and standard
157
+ * types.
158
+ */
159
+ template <class Lhs_T, class Rhs_T>
160
+ constexpr auto
161
+ binary_op_<Lhs_T, Rhs_T>::lhs (void) const
162
+ {
163
+ return get (lhs_);
164
+ }
165
+
166
+ /**
167
+ * @details
168
+ * Returns the value of the right-hand operand, applying the generic
169
+ * getter to ensure correct extraction for both custom and standard
170
+ * types.
171
+ */
172
+ template <class Lhs_T, class Rhs_T>
173
+ constexpr auto
174
+ binary_op_<Lhs_T, Rhs_T>::rhs (void) const
175
+ {
176
+ return get (rhs_);
177
+ }
178
+
179
+ // ------------------------------------------------------------------------
180
+
181
+ /**
182
+ * @details
183
+ * Evaluates the equality of `lhs` and `rhs` inside an immediately
184
+ * invoked lambda and passes the boolean result to the `binary_op_`
185
+ * base class constructor. Supports types with static `value` members,
186
+ * types with an `epsilon` precision member, and generic types.
187
+ */
188
+ template <class Lhs_T, class Rhs_T>
189
+ constexpr eq_<Lhs_T, Rhs_T>::eq_ (const Lhs_T& lhs, const Rhs_T& rhs)
190
+ : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
191
+ {
192
+ // This lambda is called in the constructor to evaluate the
193
+ // comparison. Its result is implicitly converted to bool via
194
+ // the operator bool() of whatever type the branch returns.
195
+ // This is intentional: all result types (integral_constant,
196
+ // comparator objects, plain bool) define operator bool().
197
+ using std::operator==;
198
+ using std::operator<;
199
+
200
+ #if defined(__GNUC__)
201
+ #pragma GCC diagnostic push
202
+ #pragma GCC diagnostic ignored "-Wfloat-equal"
203
+ #pragma GCC diagnostic ignored "-Wconversion"
204
+ #pragma GCC diagnostic ignored "-Wdouble-promotion"
205
+ #pragma GCC diagnostic ignored "-Wsign-compare"
206
+ #if defined(__clang__)
207
+ #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
208
+ #pragma clang diagnostic ignored "-Wpedantic"
209
+ #endif
210
+ #endif
211
+ if constexpr (type_traits::has_value<Lhs_T>
212
+ and type_traits::has_value<Rhs_T>)
213
+ {
214
+ // If both types have values (like numeric constants),
215
+ // compare them directly.
216
+ return Lhs_T::value == Rhs_T::value;
217
+ }
218
+ else if constexpr (type_traits::has_epsilon<Lhs_T>
219
+ and type_traits::has_epsilon<Rhs_T>)
220
+ {
221
+ // If both values have precision, compare them using
222
+ // the smallest precision.
223
+ return math::abs (get (lhs) - get (rhs))
224
+ < math::min_value (lhs.epsilon, rhs.epsilon);
225
+ }
226
+ else if constexpr (type_traits::has_epsilon<Lhs_T>)
227
+ {
228
+ // If only the left operand has precision, use it.
229
+ return math::abs (get (lhs) - get (rhs)) < lhs.epsilon;
230
+ }
231
+ else if constexpr (type_traits::has_epsilon<Rhs_T>)
232
+ {
233
+ // If only the right operand has precision, use it.
234
+ return math::abs (get (lhs) - get (rhs)) < rhs.epsilon;
235
+ }
236
+ else
237
+ {
238
+ // Call the generic getters, which might
239
+ // either call the type get() or return the value.
240
+ return get (lhs) == get (rhs);
241
+ }
242
+ #if defined(__GNUC__)
243
+ #pragma GCC diagnostic pop
244
+ #endif
245
+ }() }
246
+ {
247
+ }
248
+
249
+ // ------------------------------------------------------------------------
250
+
251
+ /**
252
+ * @details
253
+ * Evaluates the non-equality of `lhs` and `rhs` inside an immediately
254
+ * invoked lambda and passes the boolean result to the `binary_op_`
255
+ * base class constructor. Supports types with static `value` members,
256
+ * types with an `epsilon` precision member, and generic types.
257
+ */
258
+ template <class Lhs_T, class Rhs_T>
259
+ constexpr ne_<Lhs_T, Rhs_T>::ne_ (const Lhs_T& lhs, const Rhs_T& rhs)
260
+ : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
261
+ {
262
+ using std::operator==;
263
+ using std::operator!=;
264
+ using std::operator>;
265
+
266
+ #if defined(__GNUC__)
267
+ #pragma GCC diagnostic push
268
+ #pragma GCC diagnostic ignored "-Wfloat-equal"
269
+ #pragma GCC diagnostic ignored "-Wconversion"
270
+ #pragma GCC diagnostic ignored "-Wdouble-promotion"
271
+ #pragma GCC diagnostic ignored "-Wsign-compare"
272
+ #if defined(__clang__)
273
+ #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
274
+ #pragma clang diagnostic ignored "-Wpedantic"
275
+ #endif
276
+ #endif
277
+ if constexpr (type_traits::has_value<Lhs_T>
278
+ and type_traits::has_value<Rhs_T>)
279
+ {
280
+ return Lhs_T::value != Rhs_T::value;
281
+ }
282
+ else if constexpr (type_traits::has_epsilon<Lhs_T>
283
+ and type_traits::has_epsilon<Rhs_T>)
284
+ {
285
+ return math::abs (get (lhs) - get (rhs))
286
+ >= math::min_value (lhs.epsilon, rhs.epsilon);
287
+ }
288
+ else if constexpr (type_traits::has_epsilon<Lhs_T>)
289
+ {
290
+ return math::abs (get (lhs) - get (rhs)) >= lhs.epsilon;
291
+ }
292
+ else if constexpr (type_traits::has_epsilon<Rhs_T>)
293
+ {
294
+ return math::abs (get (lhs) - get (rhs)) >= rhs.epsilon;
295
+ }
296
+ else
297
+ {
298
+ return get (lhs) != get (rhs);
299
+ }
300
+ #if defined(__GNUC__)
301
+ #pragma GCC diagnostic pop
302
+ #endif
303
+ }() }
304
+ {
305
+ }
306
+
307
+ // ------------------------------------------------------------------------
308
+
309
+ /**
310
+ * @details
311
+ * Evaluates whether `lhs > rhs` inside an immediately invoked lambda
312
+ * and passes the boolean result to the `binary_op_` base class
313
+ * constructor. Supports types with static `value` members and generic
314
+ * types.
315
+ */
316
+ template <class Lhs_T, class Rhs_T>
317
+ constexpr gt_<Lhs_T, Rhs_T>::gt_ (const Lhs_T& lhs, const Rhs_T& rhs)
318
+ : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
319
+ {
320
+ using std::operator>;
321
+
322
+ #if defined(__GNUC__)
323
+ #pragma GCC diagnostic push
324
+ #pragma GCC diagnostic ignored "-Wconversion"
325
+ #pragma GCC diagnostic ignored "-Wdouble-promotion"
326
+ #pragma GCC diagnostic ignored "-Wsign-compare"
327
+ #if defined(__clang__)
328
+ #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
329
+ #pragma clang diagnostic ignored "-Wpedantic"
330
+ #endif
331
+ #endif
332
+ if constexpr (type_traits::has_value<Lhs_T>
333
+ and type_traits::has_value<Rhs_T>)
334
+ {
335
+ return Lhs_T::value > Rhs_T::value;
336
+ }
337
+ else
338
+ {
339
+ return get (lhs) > get (rhs);
340
+ }
341
+ #if defined(__GNUC__)
342
+ #pragma GCC diagnostic pop
343
+ #endif
344
+ }() }
345
+ {
346
+ }
347
+
348
+ // ------------------------------------------------------------------------
349
+
350
+ /**
351
+ * @details
352
+ * Evaluates whether `lhs >= rhs` inside an immediately invoked lambda
353
+ * and passes the boolean result to the `binary_op_` base class
354
+ * constructor. Supports types with static `value` members and generic
355
+ * types.
356
+ */
357
+ template <class Lhs_T, class Rhs_T>
358
+ constexpr ge_<Lhs_T, Rhs_T>::ge_ (const Lhs_T& lhs, const Rhs_T& rhs)
359
+ : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
360
+ {
361
+ using std::operator>=;
362
+
363
+ #if defined(__GNUC__)
364
+ #pragma GCC diagnostic push
365
+ #pragma GCC diagnostic ignored "-Wconversion"
366
+ #pragma GCC diagnostic ignored "-Wdouble-promotion"
367
+ #pragma GCC diagnostic ignored "-Wsign-compare"
368
+ #if defined(__clang__)
369
+ #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
370
+ #pragma clang diagnostic ignored "-Wpedantic"
371
+ #endif
372
+ #endif
373
+ if constexpr (type_traits::has_value<Lhs_T>
374
+ and type_traits::has_value<Rhs_T>)
375
+ {
376
+ return Lhs_T::value >= Rhs_T::value;
377
+ }
378
+ else
379
+ {
380
+ return get (lhs) >= get (rhs);
381
+ }
382
+ #if defined(__GNUC__)
383
+ #pragma GCC diagnostic pop
384
+ #endif
385
+ }() }
386
+ {
387
+ }
388
+
389
+ // ------------------------------------------------------------------------
390
+
391
+ /**
392
+ * @details
393
+ * Evaluates whether `lhs < rhs` inside an immediately invoked lambda
394
+ * and passes the boolean result to the `binary_op_` base class
395
+ * constructor. Supports types with static `value` members and generic
396
+ * types.
397
+ */
398
+ template <class Lhs_T, class Rhs_T>
399
+ constexpr lt_<Lhs_T, Rhs_T>::lt_ (const Lhs_T& lhs, const Rhs_T& rhs)
400
+ : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
401
+ {
402
+ using std::operator<;
403
+
404
+ #if defined(__GNUC__)
405
+ #pragma GCC diagnostic push
406
+ #pragma GCC diagnostic ignored "-Wconversion"
407
+ #pragma GCC diagnostic ignored "-Wdouble-promotion"
408
+ #pragma GCC diagnostic ignored "-Wsign-compare"
409
+ #if defined(__clang__)
410
+ #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
411
+ #pragma clang diagnostic ignored "-Wpedantic"
412
+ #endif
413
+ #endif
414
+ if constexpr (type_traits::has_value<Lhs_T>
415
+ and type_traits::has_value<Rhs_T>)
416
+ {
417
+ return Lhs_T::value < Rhs_T::value;
418
+ }
419
+ else
420
+ {
421
+ return get (lhs) < get (rhs);
422
+ }
423
+ #if defined(__GNUC__)
424
+ #pragma GCC diagnostic pop
425
+ #endif
426
+ }() }
427
+ {
428
+ }
429
+
430
+ // ------------------------------------------------------------------------
431
+
432
+ /**
433
+ * @details
434
+ * Evaluates whether `lhs <= rhs` inside an immediately invoked lambda
435
+ * and passes the boolean result to the `binary_op_` base class
436
+ * constructor. Supports types with static `value` members and generic
437
+ * types.
438
+ */
439
+ template <class Lhs_T, class Rhs_T>
440
+ constexpr le_<Lhs_T, Rhs_T>::le_ (const Lhs_T& lhs, const Rhs_T& rhs)
441
+ : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
442
+ {
443
+ using std::operator<=;
444
+
445
+ #if defined(__GNUC__)
446
+ #pragma GCC diagnostic push
447
+ #pragma GCC diagnostic ignored "-Wconversion"
448
+ #pragma GCC diagnostic ignored "-Wdouble-promotion"
449
+ #pragma GCC diagnostic ignored "-Wsign-compare"
450
+ #if defined(__clang__)
451
+ #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
452
+ #pragma clang diagnostic ignored "-Wpedantic"
453
+ #endif
454
+ #endif
455
+ if constexpr (type_traits::has_value<Lhs_T>
456
+ and type_traits::has_value<Rhs_T>)
457
+ {
458
+ return Lhs_T::value <= Rhs_T::value;
459
+ }
460
+ else
461
+ {
462
+ return get (lhs) <= get (rhs);
463
+ }
464
+ #if defined(__GNUC__)
465
+ #pragma GCC diagnostic pop
466
+ #endif
467
+ }() }
468
+ {
469
+ }
470
+
471
+ // ------------------------------------------------------------------------
472
+
473
+ /**
474
+ * @details
475
+ * Evaluates the logical conjunction `static_cast<bool>(lhs) and
476
+ * static_cast<bool>(rhs)` and passes the result to the `binary_op_`
477
+ * base class constructor.
478
+ */
479
+ template <class Lhs_T, class Rhs_T>
480
+ constexpr and_<Lhs_T, Rhs_T>::and_ (const Lhs_T& lhs, const Rhs_T& rhs)
481
+ : binary_op_<Lhs_T, Rhs_T>{
482
+ lhs, rhs, static_cast<bool> (lhs) and static_cast<bool> (rhs)
483
+ }
484
+ {
485
+ }
486
+
487
+ // ------------------------------------------------------------------------
488
+
489
+ /**
490
+ * @details
491
+ * Evaluates the logical disjunction `static_cast<bool>(lhs) or
492
+ * static_cast<bool>(rhs)` and passes the result to the `binary_op_`
493
+ * base class constructor.
494
+ */
495
+ template <class Lhs_T, class Rhs_T>
496
+ constexpr or_<Lhs_T, Rhs_T>::or_ (const Lhs_T& lhs, const Rhs_T& rhs)
497
+ : binary_op_<Lhs_T, Rhs_T>{
498
+ lhs, rhs, static_cast<bool> (lhs) or static_cast<bool> (rhs)
499
+ }
500
+ {
501
+ }
502
+
503
+ // ------------------------------------------------------------------------
504
+
505
+ /**
506
+ * @details
507
+ * Evaluates the logical negation `not static_cast<bool>(t)` and
508
+ * passes the result to the `unary_op_` base class constructor.
509
+ */
510
+ template <class T>
511
+ constexpr not_<T>::not_ (const T& t)
512
+ : unary_op_<T>{ t, not static_cast<bool> (t) }
513
+ {
514
+ }
515
+
516
+ // ------------------------------------------------------------------------
517
+
518
+ #if defined(__cpp_exceptions)
519
+
520
+ /**
521
+ * @details
522
+ * Stores the pre-computed boolean result in the private `value_`
523
+ * member.
524
+ */
525
+ constexpr callable_op_::callable_op_ (bool value) : value_{ value }
526
+ {
527
+ }
528
+
529
+ /**
530
+ * @details
531
+ * Returns the pre-computed boolean result stored by the derived
532
+ * operator's constructor.
533
+ */
534
+ constexpr callable_op_::
535
+ operator bool () const
536
+ {
537
+ return value_;
538
+ }
539
+
540
+ // ------------------------------------------------------------------------
541
+
542
+ /**
543
+ * @details
544
+ * Invokes `func` inside a try/catch block. If `func` throws an
545
+ * exception of type `Exception_T`, the result is `true`; if it throws
546
+ * any other exception the result is `false`; if no exception is thrown
547
+ * the result is `false`. The result is passed to the `callable_op_`
548
+ * base class constructor.
549
+ */
550
+ template <class Callable_T, class Exception_T>
551
+ constexpr throws_<Callable_T, Exception_T>::throws_ (
552
+ const Callable_T& func)
553
+ : callable_op_{ [&func]
554
+ {
555
+ try
556
+ {
557
+ func ();
558
+ }
559
+ catch (const Exception_T&)
560
+ {
561
+ return true;
562
+ }
563
+ catch (...)
564
+ {
565
+ return false;
566
+ }
567
+ return false;
568
+ }() }
569
+ {
570
+ }
571
+
572
+ // ------------------------------------------------------------------------
573
+
574
+ /**
575
+ * @details
576
+ * Invokes `func` inside a try/catch block. If `func` throws any
577
+ * exception the result is `true`; if no exception is thrown the result
578
+ * is `false`. The result is passed to the `callable_op_` base class
579
+ * constructor.
580
+ */
581
+ template <class Callable_T>
582
+ constexpr throws_<Callable_T, void>::throws_ (const Callable_T& func)
583
+ : callable_op_{ [&func]
584
+ {
585
+ try
586
+ {
587
+ func ();
588
+ }
589
+ catch (...)
590
+ {
591
+ return true;
592
+ }
593
+ return false;
594
+ }() }
595
+ {
596
+ }
597
+
598
+ // ------------------------------------------------------------------------
599
+
600
+ /**
601
+ * @details
602
+ * Invokes `func` inside a try/catch block. If `func` completes without
603
+ * throwing, the result is `true`; if any exception is thrown the result
604
+ * is `false`. The result is passed to the `callable_op_` base class
605
+ * constructor.
606
+ */
607
+ template <class Callable_T>
608
+ constexpr nothrow_<Callable_T>::nothrow_ (const Callable_T& func)
609
+ : callable_op_{ [&func]
610
+ {
611
+ try
612
+ {
613
+ func ();
614
+ }
615
+ catch (...)
616
+ {
617
+ return false;
618
+ }
619
+ return true;
620
+ }() }
621
+ {
622
+ }
623
+
624
+ #endif // defined(__cpp_exceptions)
625
+
626
+ // ------------------------------------------------------------------------
627
+
628
+ #if defined(__GNUC__)
629
+ #pragma GCC diagnostic push
630
+ #if defined(__clang__)
631
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
632
+ #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
633
+ #endif
634
+ #endif
635
+
636
+ /**
637
+ * @details
638
+ * For `long double`, a platform-specific path is chosen:
639
+ * - On Windows and on platforms where `long double` has the same
640
+ * storage width as `double` (e.g., ARM, RISC-V), the value is cast
641
+ * to `double` and formatted with `std::to_chars`.
642
+ * - On x86-64 Linux/macOS with 80-bit extended precision, `snprintf`
643
+ * with `%Lg` is used as a portable fallback because
644
+ * `std::to_chars` for `long double` may be unavailable when linking
645
+ * with `lld`.
646
+ *
647
+ * For all other numeric types, `std::to_chars` is called directly,
648
+ * providing locale-independent, allocation-free formatting.
649
+ */
650
+ template <class T>
651
+ requires std::is_arithmetic_v<T>
652
+ void
653
+ append_number_ (std::string& buffer, const T v)
654
+ {
655
+ char buf[32];
656
+ if constexpr (std::is_same_v<T, long double>)
657
+ {
658
+ #if defined(_WIN32) \
659
+ || (defined(__SIZEOF_LONG_DOUBLE__) \
660
+ && __SIZEOF_LONG_DOUBLE__ == __SIZEOF_DOUBLE__)
661
+ // On Windows (all toolchains: MinGW, Clang, MSVC), the C runtime
662
+ // does not handle the %Lg printf specifier correctly for 80-bit
663
+ // long double, producing garbage output. On platforms where long
664
+ // double has the same width as double (ARM, RISC-V), the cast is
665
+ // lossless. In both cases, cast to double and use std::to_chars.
666
+ const auto [ptr, ec] = std::to_chars (buf, buf + sizeof (buf),
667
+ static_cast<double> (v));
668
+ if (ec == std::errc{})
669
+ buffer.append (buf, ptr);
670
+ #else
671
+ // On x86-64 Linux/macOS with 80-bit extended-precision long double,
672
+ // std::to_chars for long double may be unavailable (e.g., with lld).
673
+ // Use snprintf as a portable fallback; %Lg is supported correctly
674
+ // by glibc and libc++ on these platforms.
675
+ snprintf (buf, sizeof (buf), "%Lg", v);
676
+ buffer.append (buf);
677
+ #endif
678
+ }
679
+ else
680
+ {
681
+ const auto [ptr, ec] = std::to_chars (buf, buf + sizeof (buf), v);
682
+ if (ec == std::errc{})
683
+ buffer.append (buf, ptr);
684
+ }
685
+ }
686
+
687
+ #if defined(__GNUC__)
688
+ #pragma GCC diagnostic pop
689
+ #endif
690
+
691
+ // ------------------------------------------------------------------------
692
+
693
+ } // namespace detail
694
+
695
+ // --------------------------------------------------------------------------
696
+
697
+ } // namespace micro_os_plus::micro_test_plus
698
+
699
+ #if defined(__GNUC__)
700
+ #pragma GCC diagnostic pop
701
+ #endif
702
+
703
+ // ----------------------------------------------------------------------------
704
+
705
+ #endif // __cplusplus
706
+
707
+ // ----------------------------------------------------------------------------
708
+
709
+ #endif // MICRO_TEST_PLUS_DETAIL_INLINES_H_
710
+
711
+ // ----------------------------------------------------------------------------