micro-test-plus 4.1.0
µTest++ Testing Framework
Loading...
Searching...
No Matches
detail-inlines.h
Go to the documentation of this file.
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
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
67{
68 namespace detail
69 {
70 // ========================================================================
71
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
96 template <class T>
97 constexpr unary_op_<T>::unary_op_ (const T& t, bool value)
98 : t_{ t }, value_{ value }
99 {
100 }
101
107 template <class T>
108 constexpr unary_op_<T>::
109 operator bool () const
110 {
111 return value_;
112 }
113
119 template <class T>
120 constexpr auto
122 {
123 return get (t_);
124 }
125
126 // ------------------------------------------------------------------------
127
133 template <class Lhs_T, class Rhs_T>
135 const Rhs_T& rhs,
136 bool value)
137 : lhs_{ lhs }, rhs_{ rhs }, value_{ value }
138 {
139 }
140
146 template <class Lhs_T, class Rhs_T>
148 operator bool () const
149 {
150 return value_;
151 }
152
159 template <class Lhs_T, class Rhs_T>
160 constexpr auto
162 {
163 return get (lhs_);
164 }
165
172 template <class Lhs_T, class Rhs_T>
173 constexpr auto
175 {
176 return get (rhs_);
177 }
178
179 // ------------------------------------------------------------------------
180
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
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>
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
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
279 {
280 return Lhs_T::value != Rhs_T::value;
281 }
282 else if constexpr (type_traits::has_epsilon<Lhs_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
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
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
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
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
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
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
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
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
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
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
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
525 constexpr callable_op_::callable_op_ (bool value) : value_{ value }
526 {
527 }
528
534 constexpr callable_op_::
535 operator bool () const
536 {
537 return value_;
538 }
539
540 // ------------------------------------------------------------------------
541
550 template <class Callable_T, class Exception_T>
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
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
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
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// ----------------------------------------------------------------------------
C++20 concept satisfied when T provides an epsilon member.
C++20 concept satisfied when T provides a value member.
Internal implementation details for the µTest++ framework.
void append_number_(std::string &buffer, T v)
Appends the string representation of a numeric value to a buffer, using std::to_chars for allocation-...
constexpr auto get(const T &t)
Generic getter function template for value retrieval.
constexpr auto min_value(const T &lhs, const T &rhs) noexcept -> const T &
Computes the minimum of two comparable values.
constexpr auto abs(const T t) noexcept -> T
Computes the absolute value of a given comparable value.
Primary namespace for the µTest++ testing framework.
constexpr and_(const Lhs_T &lhs={}, const Rhs_T &rhs={})
Constructs a logical AND comparator for the given operands.
const bool value_
Stores the result of the comparison.
Definition detail.h:285
constexpr auto lhs(void) const
Retrieves the left-hand operand.
constexpr binary_op_(const Lhs_T &lhs, const Rhs_T &rhs, bool value)
Constructs a binary comparator with the given operands and pre-computed result.
const Rhs_T rhs_
Stores the right-hand operand.
Definition detail.h:280
const Lhs_T lhs_
Stores the left-hand operand.
Definition detail.h:272
constexpr auto rhs(void) const
Retrieves the right-hand operand.
constexpr callable_op_(bool value)
Constructs a callable operator with the pre-computed boolean result.
const bool value_
Stores the result of the callable invocation.
Definition detail.h:621
constexpr eq_(const Lhs_T &lhs={}, const Rhs_T &rhs={})
Constructs an equality comparator for the given operands.
constexpr ge_(const Lhs_T &lhs={}, const Rhs_T &rhs={})
Constructs a greater than or equal comparator for the given operands.
constexpr gt_(const Lhs_T &lhs={}, const Rhs_T &rhs={})
Constructs a greater than comparator for the given operands.
constexpr le_(const Lhs_T &lhs={}, const Rhs_T &rhs={})
Constructs a less than or equal comparator for the given operands.
constexpr lt_(const Lhs_T &lhs={}, const Rhs_T &rhs={})
Constructs a less than comparator for the given operands.
constexpr ne_(const Lhs_T &lhs={}, const Rhs_T &rhs={})
Constructs a non-equality comparator for the given operands.
constexpr not_(const T &t={})
Constructs a logical NOT comparator for the given operand.
constexpr nothrow_(const Callable_T &func)
Constructs a nothrow checking operator for the given callable.
constexpr or_(const Lhs_T &lhs={}, const Rhs_T &rhs={})
Constructs a logical OR comparator for the given operands.
constexpr throws_(const Callable_T &func)
Constructs an exception checking operator for the given callable.
constexpr auto operand() const
Retrieves the wrapped operand expression.
constexpr unary_op_(const T &t, bool value)
Constructs a unary comparator with the given operand and pre-computed result.
const bool value_
Stores the result of the operation.
Definition detail.h:201