Skip to main content

detail-inlines.h File

C++ header file with inline implementations for the µTest++ internal detail namespace. More...

Included Headers

#include <charconv>

Namespaces Index

namespacemicro_os_plus

The primary namespace for the µOS++ framework. More...

namespacemicro_test_plus

Primary namespace for the µTest++ testing framework. More...

namespacedetail

Internal implementation details for the µTest++ framework. More...

Functions Index

template <class T>
voidappend_number_ (std::string &buffer, T v)

Appends the string representation of a numeric value to a buffer, using std::to_chars for allocation-free, locale-independent formatting. More...

template <class T>
constexpr autoget (const T &t)

Generic getter function template for value retrieval. More...

Description

C++ header file with inline implementations for the µTest++ internal detail namespace.

This header provides the out-of-line template and inline implementations for all types declared in detail.h. It defines the bodies of the generic getter, the unary and binary operator base class templates, all relational and logical comparator constructors, and the callable operator types (throws_, nothrow_).

Separating the implementations from the declarations keeps detail.h concise and focused on the interface, whilst grouping the complex lambda-based constructor bodies here for maintainability.

All definitions reside within the micro_os_plus::micro_test_plus::detail namespace, ensuring clear separation from user code and minimising the risk of naming conflicts.

This file is intended solely for internal use within the framework and should not be included directly by user code.

Functions

append_number_()

template <class T>
void micro_os_plus::micro_test_plus::detail::append_number_ (std::string & buffer, T v)

Appends the string representation of a numeric value to a buffer, using std::to_chars for allocation-free, locale-independent formatting.

Template Parameters
T

The numeric type to format.

Parameters
buffer

The string to append to.

v

The value to format.

Returns

Nothing.

For long double, a platform-specific path is chosen:

  • On Windows and on platforms where long double has the same storage width as double (e.g., ARM, RISC-V), the value is cast to double and formatted with std::to_chars.
  • On x86-64 Linux/macOS with 80-bit extended precision, snprintf with Lg is used as a portable fallback because std::to_chars for long double may be unavailable when linking with lld.

For all other numeric types, std::to_chars is called directly, providing locale-independent, allocation-free formatting.

Definition at line 653 of file detail-inlines.h.

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 }

Referenced by micro_os_plus::micro_test_plus::detail::expression_formatter::operator<<, micro_os_plus::micro_test_plus::detail::expression_formatter::operator<<, micro_os_plus::micro_test_plus::detail::expression_formatter::operator<<, micro_os_plus::micro_test_plus::detail::expression_formatter::operator<<, micro_os_plus::micro_test_plus::detail::expression_formatter::operator<<, micro_os_plus::micro_test_plus::detail::expression_formatter::operator<<, micro_os_plus::micro_test_plus::detail::expression_formatter::operator<<, micro_os_plus::micro_test_plus::detail::expression_formatter::operator<<, micro_os_plus::micro_test_plus::detail::expression_formatter::operator<<, micro_os_plus::micro_test_plus::detail::expression_formatter::operator<<, micro_os_plus::micro_test_plus::detail::expression_formatter::operator<<, micro_os_plus::micro_test_plus::detail::expression_formatter::operator<<, micro_os_plus::micro_test_plus::detail::expression_formatter::operator<<, micro_os_plus::micro_test_plus::detail::expression_formatter::operator<< and micro_os_plus::micro_test_plus::reporter::operator<<.

get()

template <class T>
auto micro_os_plus::micro_test_plus::detail::get (const T & t)
nodiscard constexpr

Generic getter function template for value retrieval.

Template Parameters
T

The type from which the value is to be retrieved.

Parameters
t

The object or value to be accessed.

Returns

The value obtained via the relevant getter implementation.

If the type T provides a get() member function, it is invoked and its result returned. Otherwise the argument itself is returned unchanged. The selection is performed at compile time via if constexpr with an inline requires expression.

Definition at line 81 of file detail-inlines.h.

81 get (const T& t)
82 {
83 if constexpr (requires { t.get (); })
84 return t.get ();
85 else
86 return t;
87 }

Referenced by micro_os_plus::micro_test_plus::detail::eq_< Lhs_T, Rhs_T >::eq_, micro_os_plus::micro_test_plus::detail::ge_< Lhs_T, Rhs_T >::ge_, micro_os_plus::micro_test_plus::detail::gt_< Lhs_T, Rhs_T >::gt_, micro_os_plus::micro_test_plus::detail::le_< Lhs_T, Rhs_T >::le_, micro_os_plus::micro_test_plus::detail::lt_< Lhs_T, Rhs_T >::lt_, micro_os_plus::micro_test_plus::detail::ne_< Lhs_T, Rhs_T >::ne_, micro_os_plus::micro_test_plus::detail::binary_op_< Lhs_T, Rhs_T >::lhs, micro_os_plus::micro_test_plus::detail::unary_op_< T >::operand, micro_os_plus::micro_test_plus::detail::expression_formatter::operator<< and micro_os_plus::micro_test_plus::detail::binary_op_< Lhs_T, Rhs_T >::rhs.

File Listing

The file content with the documentation metadata removed is:

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 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// ----------------------------------------------------------------------------

Generated via doxygen2docusaurus 2.2.0 by Doxygen 1.17.0.