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

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 }

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

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

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

Generated via doxygen2docusaurus 2.2.2 by Doxygen 1.17.0.