micro-test-plus 5.0.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 "-Wunknown-warning-option"
60#pragma clang diagnostic ignored "-Wc++98-compat"
61#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
62#endif
63#endif
64
65// ============================================================================
66
68{
69 namespace detail
70 {
71 // ========================================================================
72
80 template <class T>
81 constexpr auto
82 get (const T& t)
83 {
84 if constexpr (requires { t.get (); })
85 return t.get ();
86 else
87 return t;
88 }
89
90 // ------------------------------------------------------------------------
91
97 template <class T>
98 constexpr unary_op_<T>::unary_op_ (const T& t, bool value)
99 : t_{ t }, value_{ value }
100 {
101 }
102
108 template <class T>
109 constexpr unary_op_<T>::
110 operator bool () const
111 {
112 return value_;
113 }
114
120 template <class T>
121 constexpr auto
123 {
124 return get (t_);
125 }
126
127 // ------------------------------------------------------------------------
128
134 template <class Lhs_T, class Rhs_T>
136 const Rhs_T& rhs,
137 bool value)
138 : lhs_{ lhs }, rhs_{ rhs }, value_{ value }
139 {
140 }
141
147 template <class Lhs_T, class Rhs_T>
149 operator bool () const
150 {
151 return value_;
152 }
153
160 template <class Lhs_T, class Rhs_T>
161 constexpr auto
163 {
164 return get (lhs_);
165 }
166
173 template <class Lhs_T, class Rhs_T>
174 constexpr auto
176 {
177 return get (rhs_);
178 }
179
180 // ------------------------------------------------------------------------
181
189 template <class Lhs_T, class Rhs_T>
190 constexpr eq_<Lhs_T, Rhs_T>::eq_ (const Lhs_T& lhs, const Rhs_T& rhs)
191 : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
192 {
193 // This lambda is called in the constructor to evaluate the
194 // comparison. Its result is implicitly converted to bool via
195 // the operator bool() of whatever type the branch returns.
196 // This is intentional: all result types (integral_constant,
197 // comparator objects, plain bool) define operator bool().
198 using std::operator==;
199 using std::operator<;
200
201#if defined(__GNUC__)
202#pragma GCC diagnostic push
203#pragma GCC diagnostic ignored "-Wfloat-equal"
204#pragma GCC diagnostic ignored "-Wconversion"
205#pragma GCC diagnostic ignored "-Wdouble-promotion"
206#pragma GCC diagnostic ignored "-Wsign-compare"
207#if defined(__clang__)
208#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
209#pragma clang diagnostic ignored "-Wpedantic"
210#endif
211#endif
214 {
215 // If both types have values (like numeric constants),
216 // compare them directly.
217 return Lhs_T::value == Rhs_T::value;
218 }
219 else if constexpr (type_traits::has_epsilon<Lhs_T>
221 {
222 // If both values have precision, compare them using
223 // the smallest precision.
224 return math::abs (get (lhs) - get (rhs))
225 < math::min_value (lhs.epsilon, rhs.epsilon);
226 }
227 else if constexpr (type_traits::has_epsilon<Lhs_T>)
228 {
229 // If only the left operand has precision, use it.
230 return math::abs (get (lhs) - get (rhs)) < lhs.epsilon;
231 }
232 else if constexpr (type_traits::has_epsilon<Rhs_T>)
233 {
234 // If only the right operand has precision, use it.
235 return math::abs (get (lhs) - get (rhs)) < rhs.epsilon;
236 }
237 else
238 {
239 // Call the generic getters, which might
240 // either call the type get() or return the value.
241 return get (lhs) == get (rhs);
242 }
243#if defined(__GNUC__)
244#pragma GCC diagnostic pop
245#endif
246 }() }
247 {
248 }
249
250 // ------------------------------------------------------------------------
251
259 template <class Lhs_T, class Rhs_T>
260 constexpr ne_<Lhs_T, Rhs_T>::ne_ (const Lhs_T& lhs, const Rhs_T& rhs)
261 : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
262 {
263 using std::operator==;
264 using std::operator!=;
265 using std::operator>;
266
267#if defined(__GNUC__)
268#pragma GCC diagnostic push
269#pragma GCC diagnostic ignored "-Wfloat-equal"
270#pragma GCC diagnostic ignored "-Wconversion"
271#pragma GCC diagnostic ignored "-Wdouble-promotion"
272#pragma GCC diagnostic ignored "-Wsign-compare"
273#if defined(__clang__)
274#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
275#pragma clang diagnostic ignored "-Wpedantic"
276#endif
277#endif
280 {
281 return Lhs_T::value != Rhs_T::value;
282 }
283 else if constexpr (type_traits::has_epsilon<Lhs_T>
285 {
286 return math::abs (get (lhs) - get (rhs))
287 >= math::min_value (lhs.epsilon, rhs.epsilon);
288 }
289 else if constexpr (type_traits::has_epsilon<Lhs_T>)
290 {
291 return math::abs (get (lhs) - get (rhs)) >= lhs.epsilon;
292 }
293 else if constexpr (type_traits::has_epsilon<Rhs_T>)
294 {
295 return math::abs (get (lhs) - get (rhs)) >= rhs.epsilon;
296 }
297 else
298 {
299 return get (lhs) != get (rhs);
300 }
301#if defined(__GNUC__)
302#pragma GCC diagnostic pop
303#endif
304 }() }
305 {
306 }
307
308 // ------------------------------------------------------------------------
309
317 template <class Lhs_T, class Rhs_T>
318 constexpr gt_<Lhs_T, Rhs_T>::gt_ (const Lhs_T& lhs, const Rhs_T& rhs)
319 : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
320 {
321 using std::operator>;
322
323#if defined(__GNUC__)
324#pragma GCC diagnostic push
325#pragma GCC diagnostic ignored "-Wconversion"
326#pragma GCC diagnostic ignored "-Wdouble-promotion"
327#pragma GCC diagnostic ignored "-Wsign-compare"
328#if defined(__clang__)
329#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
330#pragma clang diagnostic ignored "-Wpedantic"
331#endif
332#endif
335 {
336 return Lhs_T::value > Rhs_T::value;
337 }
338 else
339 {
340 return get (lhs) > get (rhs);
341 }
342#if defined(__GNUC__)
343#pragma GCC diagnostic pop
344#endif
345 }() }
346 {
347 }
348
349 // ------------------------------------------------------------------------
350
358 template <class Lhs_T, class Rhs_T>
359 constexpr ge_<Lhs_T, Rhs_T>::ge_ (const Lhs_T& lhs, const Rhs_T& rhs)
360 : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
361 {
362 using std::operator>=;
363
364#if defined(__GNUC__)
365#pragma GCC diagnostic push
366#pragma GCC diagnostic ignored "-Wconversion"
367#pragma GCC diagnostic ignored "-Wdouble-promotion"
368#pragma GCC diagnostic ignored "-Wsign-compare"
369#if defined(__clang__)
370#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
371#pragma clang diagnostic ignored "-Wpedantic"
372#endif
373#endif
376 {
377 return Lhs_T::value >= Rhs_T::value;
378 }
379 else
380 {
381 return get (lhs) >= get (rhs);
382 }
383#if defined(__GNUC__)
384#pragma GCC diagnostic pop
385#endif
386 }() }
387 {
388 }
389
390 // ------------------------------------------------------------------------
391
399 template <class Lhs_T, class Rhs_T>
400 constexpr lt_<Lhs_T, Rhs_T>::lt_ (const Lhs_T& lhs, const Rhs_T& rhs)
401 : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
402 {
403 using std::operator<;
404
405#if defined(__GNUC__)
406#pragma GCC diagnostic push
407#pragma GCC diagnostic ignored "-Wconversion"
408#pragma GCC diagnostic ignored "-Wdouble-promotion"
409#pragma GCC diagnostic ignored "-Wsign-compare"
410#if defined(__clang__)
411#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
412#pragma clang diagnostic ignored "-Wpedantic"
413#endif
414#endif
417 {
418 return Lhs_T::value < Rhs_T::value;
419 }
420 else
421 {
422 return get (lhs) < get (rhs);
423 }
424#if defined(__GNUC__)
425#pragma GCC diagnostic pop
426#endif
427 }() }
428 {
429 }
430
431 // ------------------------------------------------------------------------
432
440 template <class Lhs_T, class Rhs_T>
441 constexpr le_<Lhs_T, Rhs_T>::le_ (const Lhs_T& lhs, const Rhs_T& rhs)
442 : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
443 {
444 using std::operator<=;
445
446#if defined(__GNUC__)
447#pragma GCC diagnostic push
448#pragma GCC diagnostic ignored "-Wconversion"
449#pragma GCC diagnostic ignored "-Wdouble-promotion"
450#pragma GCC diagnostic ignored "-Wsign-compare"
451#if defined(__clang__)
452#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
453#pragma clang diagnostic ignored "-Wpedantic"
454#endif
455#endif
458 {
459 return Lhs_T::value <= Rhs_T::value;
460 }
461 else
462 {
463 return get (lhs) <= get (rhs);
464 }
465#if defined(__GNUC__)
466#pragma GCC diagnostic pop
467#endif
468 }() }
469 {
470 }
471
472 // ------------------------------------------------------------------------
473
480 template <class Lhs_T, class Rhs_T>
481 constexpr and_<Lhs_T, Rhs_T>::and_ (const Lhs_T& lhs, const Rhs_T& rhs)
482 : binary_op_<Lhs_T, Rhs_T>{
483 lhs, rhs, static_cast<bool> (lhs) and static_cast<bool> (rhs)
484 }
485 {
486 }
487
488 // ------------------------------------------------------------------------
489
496 template <class Lhs_T, class Rhs_T>
497 constexpr or_<Lhs_T, Rhs_T>::or_ (const Lhs_T& lhs, const Rhs_T& rhs)
498 : binary_op_<Lhs_T, Rhs_T>{
499 lhs, rhs, static_cast<bool> (lhs) or static_cast<bool> (rhs)
500 }
501 {
502 }
503
504 // ------------------------------------------------------------------------
505
511 template <class T>
512 constexpr not_<T>::not_ (const T& t)
513 : unary_op_<T>{ t, not static_cast<bool> (t) }
514 {
515 }
516
517 // ------------------------------------------------------------------------
518
519#if defined(__cpp_exceptions)
520
526 constexpr callable_op_::callable_op_ (bool value) : value_{ value }
527 {
528 }
529
535 constexpr callable_op_::
536 operator bool () const
537 {
538 return value_;
539 }
540
541 // ------------------------------------------------------------------------
542
551 template <class Callable_T, class Exception_T>
553 const Callable_T& func)
554 : callable_op_{ [&func]
555 {
556 try
557 {
558 func ();
559 }
560 catch (const Exception_T&)
561 {
562 return true;
563 }
564 catch (...)
565 {
566 return false;
567 }
568 return false;
569 }() }
570 {
571 }
572
573 // ------------------------------------------------------------------------
574
582 template <class Callable_T>
583 constexpr throws_<Callable_T, void>::throws_ (const Callable_T& func)
584 : callable_op_{ [&func]
585 {
586 try
587 {
588 func ();
589 }
590 catch (...)
591 {
592 return true;
593 }
594 return false;
595 }() }
596 {
597 }
598
599 // ------------------------------------------------------------------------
600
608 template <class Callable_T>
609 constexpr nothrow_<Callable_T>::nothrow_ (const Callable_T& func)
610 : callable_op_{ [&func]
611 {
612 try
613 {
614 func ();
616 catch (...)
617 {
618 return false;
619 }
620 return true;
621 }() }
622 {
623 }
624
625#endif // defined(__cpp_exceptions)
626
627 // ------------------------------------------------------------------------
628
629#if defined(__GNUC__)
630#pragma GCC diagnostic push
631#if defined(__clang__)
632#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
633#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
634#endif
635#endif
636
651 template <class T>
652 requires std::is_arithmetic_v<T>
653 void
654 append_number_ (std::string& buffer, const T v)
655 {
656 char buf[32];
657 if constexpr (std::is_same_v<T, long double>)
658 {
659#if defined(_WIN32) \
660 || (defined(__SIZEOF_LONG_DOUBLE__) \
661 && __SIZEOF_LONG_DOUBLE__ == __SIZEOF_DOUBLE__)
662 // On Windows (all toolchains: MinGW, Clang, MSVC), the C runtime
663 // does not handle the %Lg printf specifier correctly for 80-bit
664 // long double, producing garbage output. On platforms where long
665 // double has the same width as double (ARM, RISC-V), the cast is
666 // lossless. In both cases, cast to double and use std::to_chars.
667 const auto [ptr, ec] = std::to_chars (buf, buf + sizeof (buf),
668 static_cast<double> (v));
669 if (ec == std::errc{})
670 buffer.append (buf, ptr);
671#else
672 // On x86-64 Linux/macOS with 80-bit extended-precision long double,
673 // std::to_chars for long double may be unavailable (e.g., with lld).
674 // Use snprintf as a portable fallback; %Lg is supported correctly
675 // by glibc and libc++ on these platforms.
676 snprintf (buf, sizeof (buf), "%Lg", v);
677 buffer.append (buf);
678#endif
679 }
680 else
681 {
682 const auto [ptr, ec] = std::to_chars (buf, buf + sizeof (buf), v);
683 if (ec == std::errc{})
684 buffer.append (buf, ptr);
685 }
686 }
687
688#if defined(__GNUC__)
689#pragma GCC diagnostic pop
690#endif
691
692 // ------------------------------------------------------------------------
693
694 } // namespace detail
695
696 // --------------------------------------------------------------------------
697
698} // namespace micro_os_plus::micro_test_plus
699
700#if defined(__GNUC__)
701#pragma GCC diagnostic pop
702#endif
703
704// ----------------------------------------------------------------------------
705
706#endif // __cplusplus
707
708// ----------------------------------------------------------------------------
709
710#endif // MICRO_TEST_PLUS_DETAIL_INLINES_H_
711
712// ----------------------------------------------------------------------------
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