micro-test-plus 5.0.1
µ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_OS_PLUS_MICRO_TEST_PLUS_INLINES_DETAIL_INLINES_H_
43#define MICRO_OS_PLUS_MICRO_TEST_PLUS_INLINES_DETAIL_INLINES_H_
44
45// ----------------------------------------------------------------------------
46
47#if defined(__cplusplus)
48
49// ----------------------------------------------------------------------------
50
51#include <charconv>
52
53// ----------------------------------------------------------------------------
54
55#if defined(__GNUC__)
56#pragma GCC diagnostic push
57
58#pragma GCC diagnostic ignored "-Waggregate-return"
59#if defined(__clang__)
60#pragma clang diagnostic ignored "-Wunknown-warning-option"
61#pragma clang diagnostic ignored "-Wc++98-compat"
62#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
63#endif // defined(__clang__)
64#endif // defined(__GNUC__)
65
66// ============================================================================
67
69{
70 namespace detail
71 {
72 // ========================================================================
73
81 template <class T>
82 constexpr auto
83 get (const T& t)
84 {
85 if constexpr (requires { t.get (); })
86 return t.get ();
87 else
88 return t;
89 }
90
91 // ------------------------------------------------------------------------
92
98 template <class T>
99 constexpr unary_op_<T>::unary_op_ (const T& t, bool value)
100 : t_{ t }, value_{ value }
101 {
102 }
103
109 template <class T>
110 constexpr unary_op_<T>::
111 operator bool () const
112 {
113 return value_;
114 }
115
121 template <class T>
122 constexpr auto
124 {
125 return get (t_);
126 }
127
128 // ------------------------------------------------------------------------
129
135 template <class Lhs_T, class Rhs_T>
137 const Rhs_T& rhs,
138 bool value)
139 : lhs_{ lhs }, rhs_{ rhs }, value_{ value }
140 {
141 }
142
148 template <class Lhs_T, class Rhs_T>
150 operator bool () const
151 {
152 return value_;
153 }
154
161 template <class Lhs_T, class Rhs_T>
162 constexpr auto
164 {
165 return get (lhs_);
166 }
167
174 template <class Lhs_T, class Rhs_T>
175 constexpr auto
177 {
178 return get (rhs_);
179 }
180
181 // ------------------------------------------------------------------------
182
190 template <class Lhs_T, class Rhs_T>
191 constexpr eq_<Lhs_T, Rhs_T>::eq_ (const Lhs_T& lhs, const Rhs_T& rhs)
192 : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
193 {
194 // This lambda is called in the constructor to evaluate the
195 // comparison. Its result is implicitly converted to bool via
196 // the operator bool() of whatever type the branch returns.
197 // This is intentional: all result types (integral_constant,
198 // comparator objects, plain bool) define operator bool().
199 using std::operator==;
200 using std::operator<;
201
202#if defined(__GNUC__)
203#pragma GCC diagnostic push
204
205#pragma GCC diagnostic ignored "-Wfloat-equal"
206#pragma GCC diagnostic ignored "-Wconversion"
207#pragma GCC diagnostic ignored "-Wdouble-promotion"
208#pragma GCC diagnostic ignored "-Wsign-compare"
209#if defined(__clang__)
210#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
211#pragma clang diagnostic ignored "-Wpedantic"
212#endif // defined(__clang__)
213#endif // defined(__GNUC__)
214
217 {
218 // If both types have values (like numeric constants),
219 // compare them directly.
220 return Lhs_T::value == Rhs_T::value;
221 }
222 else if constexpr (type_traits::has_epsilon<Lhs_T>
224 {
225 // If both values have precision, compare them using
226 // the smallest precision.
227 return math::abs (get (lhs) - get (rhs))
228 < math::min_value (lhs.epsilon, rhs.epsilon);
229 }
230 else if constexpr (type_traits::has_epsilon<Lhs_T>)
231 {
232 // If only the left operand has precision, use it.
233 return math::abs (get (lhs) - get (rhs)) < lhs.epsilon;
234 }
235 else if constexpr (type_traits::has_epsilon<Rhs_T>)
236 {
237 // If only the right operand has precision, use it.
238 return math::abs (get (lhs) - get (rhs)) < rhs.epsilon;
239 }
240 else
241 {
242 // Call the generic getters, which might
243 // either call the type get() or return the value.
244 return get (lhs) == get (rhs);
245 }
246
247#if defined(__GNUC__)
248#pragma GCC diagnostic pop
249#endif // defined(__GNUC__)
250 }() }
251 {
252 }
253
254 // ------------------------------------------------------------------------
255
263 template <class Lhs_T, class Rhs_T>
264 constexpr ne_<Lhs_T, Rhs_T>::ne_ (const Lhs_T& lhs, const Rhs_T& rhs)
265 : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
266 {
267 using std::operator==;
268 using std::operator!=;
269 using std::operator>;
270
271#if defined(__GNUC__)
272#pragma GCC diagnostic push
273
274#pragma GCC diagnostic ignored "-Wfloat-equal"
275#pragma GCC diagnostic ignored "-Wconversion"
276#pragma GCC diagnostic ignored "-Wdouble-promotion"
277#pragma GCC diagnostic ignored "-Wsign-compare"
278#if defined(__clang__)
279#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
280#pragma clang diagnostic ignored "-Wpedantic"
281#endif // defined(__clang__)
282#endif // defined(__GNUC__)
283
286 {
287 return Lhs_T::value != Rhs_T::value;
288 }
289 else if constexpr (type_traits::has_epsilon<Lhs_T>
291 {
292 return math::abs (get (lhs) - get (rhs))
293 >= math::min_value (lhs.epsilon, rhs.epsilon);
294 }
295 else if constexpr (type_traits::has_epsilon<Lhs_T>)
296 {
297 return math::abs (get (lhs) - get (rhs)) >= lhs.epsilon;
298 }
299 else if constexpr (type_traits::has_epsilon<Rhs_T>)
300 {
301 return math::abs (get (lhs) - get (rhs)) >= rhs.epsilon;
302 }
303 else
304 {
305 return get (lhs) != get (rhs);
306 }
307
308#if defined(__GNUC__)
309#pragma GCC diagnostic pop
310#endif // defined(__GNUC__)
311 }() }
312 {
313 }
314
315 // ------------------------------------------------------------------------
316
324 template <class Lhs_T, class Rhs_T>
325 constexpr gt_<Lhs_T, Rhs_T>::gt_ (const Lhs_T& lhs, const Rhs_T& rhs)
326 : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
327 {
328 using std::operator>;
329
330#if defined(__GNUC__)
331#pragma GCC diagnostic push
332
333#pragma GCC diagnostic ignored "-Wconversion"
334#pragma GCC diagnostic ignored "-Wdouble-promotion"
335#pragma GCC diagnostic ignored "-Wsign-compare"
336#if defined(__clang__)
337#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
338#pragma clang diagnostic ignored "-Wpedantic"
339#endif // defined(__clang__)
340#endif // defined(__GNUC__)
341
344 {
345 return Lhs_T::value > Rhs_T::value;
346 }
347 else
348 {
349 return get (lhs) > get (rhs);
350 }
351
352#if defined(__GNUC__)
353#pragma GCC diagnostic pop
354#endif // defined(__GNUC__)
355 }() }
356 {
357 }
358
359 // ------------------------------------------------------------------------
360
368 template <class Lhs_T, class Rhs_T>
369 constexpr ge_<Lhs_T, Rhs_T>::ge_ (const Lhs_T& lhs, const Rhs_T& rhs)
370 : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
371 {
372 using std::operator>=;
373
374#if defined(__GNUC__)
375#pragma GCC diagnostic push
376
377#pragma GCC diagnostic ignored "-Wconversion"
378#pragma GCC diagnostic ignored "-Wdouble-promotion"
379#pragma GCC diagnostic ignored "-Wsign-compare"
380#if defined(__clang__)
381#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
382#pragma clang diagnostic ignored "-Wpedantic"
383#endif // defined(__clang__)
384#endif // defined(__GNUC__)
385
388 {
389 return Lhs_T::value >= Rhs_T::value;
390 }
391 else
392 {
393 return get (lhs) >= get (rhs);
394 }
395
396#if defined(__GNUC__)
397#pragma GCC diagnostic pop
398#endif // defined(__GNUC__)
399 }() }
400 {
401 }
402
403 // ------------------------------------------------------------------------
404
412 template <class Lhs_T, class Rhs_T>
413 constexpr lt_<Lhs_T, Rhs_T>::lt_ (const Lhs_T& lhs, const Rhs_T& rhs)
414 : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
415 {
416 using std::operator<;
417
418#if defined(__GNUC__)
419#pragma GCC diagnostic push
420
421#pragma GCC diagnostic ignored "-Wconversion"
422#pragma GCC diagnostic ignored "-Wdouble-promotion"
423#pragma GCC diagnostic ignored "-Wsign-compare"
424#if defined(__clang__)
425#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
426#pragma clang diagnostic ignored "-Wpedantic"
427#endif // defined(__clang__)
428#endif // defined(__GNUC__)
429
432 {
433 return Lhs_T::value < Rhs_T::value;
434 }
435 else
436 {
437 return get (lhs) < get (rhs);
438 }
439
440#if defined(__GNUC__)
441#pragma GCC diagnostic pop
442#endif // defined(__GNUC__)
443 }() }
444 {
445 }
446
447 // ------------------------------------------------------------------------
448
456 template <class Lhs_T, class Rhs_T>
457 constexpr le_<Lhs_T, Rhs_T>::le_ (const Lhs_T& lhs, const Rhs_T& rhs)
458 : binary_op_<Lhs_T, Rhs_T>{ lhs, rhs, [&]
459 {
460 using std::operator<=;
461
462#if defined(__GNUC__)
463#pragma GCC diagnostic push
464
465#pragma GCC diagnostic ignored "-Wconversion"
466#pragma GCC diagnostic ignored "-Wdouble-promotion"
467#pragma GCC diagnostic ignored "-Wsign-compare"
468#if defined(__clang__)
469#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"
470#pragma clang diagnostic ignored "-Wpedantic"
471#endif // defined(__clang__)
472#endif // defined(__GNUC__)
473
476 {
477 return Lhs_T::value <= Rhs_T::value;
478 }
479 else
480 {
481 return get (lhs) <= get (rhs);
482 }
483
484#if defined(__GNUC__)
485#pragma GCC diagnostic pop
486#endif // defined(__GNUC__)
487 }() }
488 {
489 }
490
491 // ------------------------------------------------------------------------
492
499 template <class Lhs_T, class Rhs_T>
500 constexpr and_<Lhs_T, Rhs_T>::and_ (const Lhs_T& lhs, const Rhs_T& rhs)
501 : binary_op_<Lhs_T, Rhs_T>{
502 lhs, rhs, static_cast<bool> (lhs) and static_cast<bool> (rhs)
503 }
504 {
505 }
506
507 // ------------------------------------------------------------------------
508
515 template <class Lhs_T, class Rhs_T>
516 constexpr or_<Lhs_T, Rhs_T>::or_ (const Lhs_T& lhs, const Rhs_T& rhs)
517 : binary_op_<Lhs_T, Rhs_T>{
518 lhs, rhs, static_cast<bool> (lhs) or static_cast<bool> (rhs)
519 }
520 {
521 }
522
523 // ------------------------------------------------------------------------
524
530 template <class T>
531 constexpr not_<T>::not_ (const T& t)
532 : unary_op_<T>{ t, not static_cast<bool> (t) }
533 {
534 }
535
536 // ------------------------------------------------------------------------
537
538#if defined(__cpp_exceptions)
539
545 constexpr callable_op_::callable_op_ (bool value) : value_{ value }
546 {
547 }
548
554 constexpr callable_op_::
555 operator bool () const
556 {
557 return value_;
558 }
559
560 // ------------------------------------------------------------------------
561
570 template <class Callable_T, class Exception_T>
572 const Callable_T& func)
573 : callable_op_{ [&func]
574 {
575 try
576 {
577 func ();
578 }
579 catch (const Exception_T&)
580 {
581 return true;
582 }
583 catch (...)
584 {
585 return false;
586 }
587 return false;
588 }() }
589 {
590 }
591
592 // ------------------------------------------------------------------------
593
601 template <class Callable_T>
602 constexpr throws_<Callable_T, void>::throws_ (const Callable_T& func)
603 : callable_op_{ [&func]
604 {
605 try
606 {
607 func ();
608 }
609 catch (...)
610 {
611 return true;
612 }
613 return false;
614 }() }
615 {
616 }
617
618 // ------------------------------------------------------------------------
619
624 * is `false`. The result is passed to the `callable_op_` base class
625 * constructor.
626 */
627 template <class Callable_T>
628 constexpr nothrow_<Callable_T>::nothrow_ (const Callable_T& func)
629 : callable_op_{ [&func]
630 {
631 try
632 {
633 func ();
634 }
635 catch (...)
636 {
637 return false;
638 }
639 return true;
640 }() }
641 {
642 }
643
644#endif // defined(__cpp_exceptions)
645
646 // ------------------------------------------------------------------------
647
648#if defined(__GNUC__)
649#pragma GCC diagnostic push
650
651#if defined(__clang__)
652#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
653#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
654#endif // defined(__clang__)
655#endif // defined(__GNUC__)
656
671 template <class T>
672 requires std::is_arithmetic_v<T>
673 void
674 append_number_ (std::string& buffer, const T v)
675 {
676 char buf[32];
677 if constexpr (std::is_same_v<T, long double>)
678 {
679#if defined(_WIN32) \
680 || (defined(__SIZEOF_LONG_DOUBLE__) \
681 && __SIZEOF_LONG_DOUBLE__ == __SIZEOF_DOUBLE__)
682 // On Windows (all toolchains: MinGW, Clang, MSVC), the C runtime
683 // does not handle the %Lg printf specifier correctly for 80-bit
684 // long double, producing garbage output. On platforms where long
685 // double has the same width as double (ARM, RISC-V), the cast is
686 // lossless. In both cases, cast to double and use std::to_chars.
687 const auto [ptr, ec] = std::to_chars (buf, buf + sizeof (buf),
688 static_cast<double> (v));
689 if (ec == std::errc{})
690 buffer.append (buf, ptr);
691#else
692 // On x86-64 Linux/macOS with 80-bit extended-precision long double,
693 // std::to_chars for long double may be unavailable (e.g., with lld).
694 // Use snprintf as a portable fallback; %Lg is supported correctly
695 // by glibc and libc++ on these platforms.
696 snprintf (buf, sizeof (buf), "%Lg", v);
697 buffer.append (buf);
698#endif // defined(_WIN32) || (defined(__SIZEOF_LONG_DOUBLE__) && __SIZEOF_LONG_DOUBLE__ == __SIZEOF_DOUBLE__)
699 }
700 else
701 {
702 const auto [ptr, ec] = std::to_chars (buf, buf + sizeof (buf), v);
703 if (ec == std::errc{})
704 buffer.append (buf, ptr);
705 }
706 }
707
708#if defined(__GNUC__)
709#pragma GCC diagnostic pop
710#endif // defined(__GNUC__)
711
712 // ------------------------------------------------------------------------
713
714 } // namespace detail
715
716 // --------------------------------------------------------------------------
717
718} // namespace micro_os_plus::micro_test_plus
719
720#if defined(__GNUC__)
721#pragma GCC diagnostic pop
722#endif // defined(__GNUC__)
723
724// ----------------------------------------------------------------------------
725
726#endif // defined(__cplusplus)
727
728// ----------------------------------------------------------------------------
729
730#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_INLINES_DETAIL_INLINES_H_
731
732// ----------------------------------------------------------------------------
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:294
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:289
const Lhs_T lhs_
Stores the left-hand operand.
Definition detail.h:281
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:630
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:210