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 654 of file detail-inlines.h.

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 }

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 82 of file detail-inlines.h.

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

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 "-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 ();
615 }
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// ----------------------------------------------------------------------------

Generated via doxygen2docusaurus 2.2.1 by Doxygen 1.17.0.