Skip to main content

expression-formatter-inlines.h File

C++ header file with inline implementations for the µTest++ expression formatter. More...

Included Headers​

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

Description​

C++ header file with inline implementations for the µTest++ expression formatter.

This header provides the inline and template member function implementations for expression_formatter: the constructors, the str() accessor, and the template operators (for the pointers, the value wrappers, the ranges, the scoped enumerations, the pairs, the tuples, the optionals, and the expressions), which are instantiated for each concrete type. It also defines plain_text::append().

The compound expression operators format comparator expressions and wrap them in the pass/fail colour codes of the owning reporter.

Numbers and pointers are formatted with the detail::append_number() and detail::append_pointer() helpers, defined in detail-inlines.h and shared with the reporter.

All definitions reside within the micro_os_plus::micro_test_plus::detail namespace.

This file is included by expression-formatter.h; the applications include micro-os-plus/micro-test-plus.h, not this file.

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.0 Software License,
13 * which can be obtained from https://www.boost.org/LICENSE_1_0.txt.
14 */
15
16// ----------------------------------------------------------------------------
17
44
45#ifndef MICRO_OS_PLUS_MICRO_TEST_PLUS_INLINES_EXPRESSION_FORMATTER_INLINES_H_
46#define MICRO_OS_PLUS_MICRO_TEST_PLUS_INLINES_EXPRESSION_FORMATTER_INLINES_H_
47
48// ----------------------------------------------------------------------------
49
50#if defined(__cplusplus)
51
52// ----------------------------------------------------------------------------
53
55
56// ----------------------------------------------------------------------------
57
58#if defined(__GNUC__)
59#pragma GCC diagnostic push
60
61#pragma GCC diagnostic ignored "-Waggregate-return"
62#if defined(__clang__)
63#pragma clang diagnostic ignored "-Wunknown-warning-option"
64#pragma clang diagnostic ignored "-Wc++98-compat"
65#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
66#endif // defined(__clang__)
67#endif // defined(__GNUC__)
68
69// ============================================================================
70
72{
73 // --------------------------------------------------------------------------
74
82 {
83 }
84
91 const colours& colours) noexcept
93 {
94 }
95
102 std::string&& buffer) noexcept
103 : colours_ (colours_none), buffer_ (std::move (buffer)), plain_ (true)
104 {
105 }
106
107 // --------------------------------------------------------------------------
108
115 expression_formatter::colour (const bool cond) const
116 {
117 return colour_code{ cond ? colours_.pass : colours_.fail };
118 }
119
126 {
127 return colour_code{ colours_.none };
128 }
129
137 {
138 buffer_.append (code.sequence);
139 return *this;
140 }
141
142 // --------------------------------------------------------------------------
143
148 inline const std::string&
149 expression_formatter::str (void) const noexcept
150 {
151 return buffer_;
152 }
153
154#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_DEPRECATED_ENABLED)
159 // Deprecated since 6.0.0; use str().c_str().
160 inline const char*
161 expression_formatter::c_str (void) const noexcept
162 {
163 return buffer_.c_str ();
164 }
165
170 // Deprecated since 6.0.0; use str().empty().
171 inline bool
172 expression_formatter::empty (void) const noexcept
173 {
174 return buffer_.empty ();
175 }
176#endif // defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_DEPRECATED_ENABLED)
177
178 // --------------------------------------------------------------------------
179
186 template <typename Pointer_T>
190 {
191 append_c_string (s, std::string_view::npos);
192 return *this;
193 }
194
200 template <typename Char_T, std::size_t N>
201 requires (detail::character<Char_T> and not std::is_volatile_v<Char_T>)
203 expression_formatter::operator<< (const Char_T (&s)[N])
204 {
205 append_c_string (static_cast<const Char_T*> (s), N);
206 return *this;
207 }
208
214 template <typename T>
217 expression_formatter::operator<< (T* v)
218 {
220 return *this;
221 }
222
223#if defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_DEPRECATED_ENABLED)
232 template <class T>
233 requires (extension::is_op<T> and requires (const T& v) { v.get (); })
235 expression_formatter::operator<< (const T& t)
236 {
237 *this << detail::unwrap (t);
238 return *this;
239 }
240#endif // defined(MICRO_OS_PLUS_MICRO_TEST_PLUS_DEPRECATED_ENABLED)
241
260 template <class T>
264 {
265 *this << "{";
266 size_t count = 0;
267 for (const auto& arg : t)
268 {
269 if (count < max_range_elements_)
270 {
271 *this << (count == 0 ? "" : ", ");
272 using element_type = std::remove_cvref_t<decltype (arg)>;
273 if constexpr (std::is_same_v<element_type, signed char>
274 or std::is_same_v<element_type, unsigned char>)
275 {
276 *this << static_cast<int> (arg);
277 }
278 else
279 {
280 append_value (arg);
281 }
282 }
283 ++count;
284 }
285 if (count > max_range_elements_)
286 {
287 *this << ", ... (";
289 *this << " elements)";
290 }
291 *this << "}";
292 return *this;
293 }
294
301 template <class T>
302 requires (std::is_enum_v<T>
303 and not std::is_convertible_v<T, std::underlying_type_t<T>>)
305 expression_formatter::operator<< (T v)
306 {
307 return *this << static_cast<std::underlying_type_t<T>> (v);
308 }
309
315 template <class T1, class T2>
318 expression_formatter::operator<< (const std::pair<T1, T2>& p)
319 {
320 *this << "(";
321 append_elements (p, std::index_sequence_for<T1, T2>{});
322 *this << ")";
323 return *this;
324 }
325
331 template <class... Ts>
332 requires (extension::printable<Ts> and ...)
334 expression_formatter::operator<<(const std::tuple<Ts...>& t)
335 {
336 *this << "(";
337 append_elements (t, std::index_sequence_for<Ts...>{});
338 *this << ")";
339 return *this;
340 }
341
347 template <class T>
350 expression_formatter::operator<<(const std::optional<T>& o)
351 {
352 if (o.has_value ())
353 {
354 append_value (*o);
355 return *this;
356 }
357 return *this << std::nullopt;
358 }
359
368 template <class Rep, class Period>
369 requires std::is_arithmetic_v<Rep>
372 const std::chrono::duration<Rep, Period>& d)
373 {
374 detail::append_number (buffer_, d.count ());
375
376 if constexpr (std::is_same_v<Period, std::nano>)
377 {
378 return *this << "ns";
379 }
380 else if constexpr (std::is_same_v<Period, std::micro>)
381 {
382 return *this << "us";
383 }
384 else if constexpr (std::is_same_v<Period, std::milli>)
385 {
386 return *this << "ms";
387 }
388 else if constexpr (std::is_same_v<Period, std::ratio<1>>)
389 {
390 return *this << "s";
391 }
392 else if constexpr (std::is_same_v<Period, std::ratio<60>>)
393 {
394 return *this << "min";
395 }
396 else if constexpr (std::is_same_v<Period, std::ratio<3600>>)
397 {
398 return *this << "h";
399 }
400 else
401 {
402 *this << "[";
403 detail::append_number (buffer_, static_cast<long long> (Period::num));
404 *this << "/";
405 detail::append_number (buffer_, static_cast<long long> (Period::den));
406 return *this << "]s";
407 }
408 }
409
416 template <class Tuple_T, std::size_t... Is>
417 void
418 expression_formatter::append_elements ([[maybe_unused]] const Tuple_T& t,
419 std::index_sequence<Is...>)
420 {
421 ((*this << (Is == 0 ? "" : ", "), append_value (std::get<Is> (t))), ...);
422 }
423
424 // --------------------------------------------------------------------------
425
434 template <character Char_T>
437 {
438 if (s.is_null ())
439 {
440 buffer_.append ("nullptr");
441 }
442 else
443 {
444 *this << s.view ();
445 }
446 return *this;
447 }
448
449 // --------------------------------------------------------------------------
450
459 template <class T>
460 void
462 {
463 using pointee_type = std::remove_cv_t<std::remove_pointer_t<T>>;
464 if constexpr (std::is_pointer_v<T> and detail::character<pointee_type>)
465 {
466 *this << detail::basic_c_string<pointee_type>{ value };
467 }
468 else
469 {
471 }
472 }
473
486 template <class T>
487 void
489 {
490 if constexpr (detail::member_printable<T>
492 {
493 *this << value;
494 }
495 else
496 {
498 }
499 }
500
501 // --------------------------------------------------------------------------
502 // Compound expression operators (with pass/fail colour codes).
503
516 template <class Lhs_T, class Rhs_T, class Compare_T>
520 {
521 *this << colour (op);
522 append_operand (op.lhs ());
523 // A tag without a symbol is rejected when the expression is built, and
524 // must not give a second error here.
526 {
527 *this << Compare_T::symbol;
528 }
529 append_operand (op.rhs ());
530 using lhs_type = std::remove_cvref_t<decltype (op.lhs ())>;
531 using rhs_type = std::remove_cvref_t<decltype (op.rhs ())>;
532 if constexpr (std::is_same_v<Compare_T, detail::equal_to>
535 and requires (const lhs_type& l, const rhs_type& r) {
536 {
537 *std::begin (l) == *std::begin (r)
538 } -> std::convertible_to<bool>;
539 })
540 {
541 append_first_difference (op.lhs (), op.rhs ());
542 }
543 return (*this << colour_none ());
544 }
545
553 template <class Lhs_T, class Rhs_T>
554 void
556 const Rhs_T& rhs)
557 {
558 auto l = std::begin (lhs);
559 const auto l_end = std::end (lhs);
560 auto r = std::begin (rhs);
561 const auto r_end = std::end (rhs);
562
563 size_t index = 0;
564 while (l != l_end and r != r_end and detail::equal_to::apply (*l, *r))
565 {
566 l = std::next (l);
567 r = std::next (r);
568 ++index;
569 }
570
571 if (index < max_range_elements_ or (l == l_end and r == r_end))
572 {
573 return;
574 }
575
576 *this << " (first difference at index ";
578 *this << ")";
579 }
580
587 template <class Lhs_T, class Rhs_T, class Epsilon_T>
591 {
592 return (*this << colour (op) << op.lhs () << " ~ (" << op.rhs () << " +/- "
593 << op.epsilon () << ")" << colour_none ());
594 }
595
603 template <class Lhs_T, class Rhs_T>
606 {
607 *this << "(";
609 return (*this << ")");
610 }
611
616 template <class T>
617 void
619 {
621 }
622
629 template <class Lhs_T, class Rhs_T>
630 void
633 {
634 append_and_operand (op.lhs ());
635 *this << colour (op) << " and " << colour_none ();
636 append_operand (op.rhs ());
637 }
638
646 template <class Lhs_T, class Rhs_T>
649 {
650 *this << "(";
652 return (*this << ")");
653 }
654
659 template <class T>
660 void
662 {
664 }
665
672 template <class Lhs_T, class Rhs_T>
673 void
676 {
677 append_or_operand (op.lhs ());
678 *this << colour (op) << " or " << colour_none ();
679 append_operand (op.rhs ());
680 }
681
688 template <class T>
691 {
692 const colour_code not_colour = colour (op);
693 *this << not_colour << "not ";
694 append_not_operand (op.operand (), not_colour);
695 return (*this << colour_none ());
696 }
697
702 template <class T>
703 void
705 [[maybe_unused]] colour_code)
706 {
708 }
709
717 template <class Lhs_T, class Rhs_T, class Compare_T>
718 void
721 colour_code outer_colour)
722 {
723 *this << "(" << op << outer_colour << ")";
724 }
725
732 template <class Lhs_T, class Rhs_T, class Epsilon_T>
733 void
736 colour_code outer_colour)
737 {
738 *this << "(" << op << outer_colour << ")";
739 }
740
747 template <class T, class Predicate_T>
751 {
752 *this << colour (op);
754 {
755 *this << Predicate_T::symbol;
756 }
757 append_operand (op.operand ());
758 return (*this << colour_none ());
759 }
760
761#if defined(__cpp_exceptions)
768 template <class Exception_T>
771 {
772 if constexpr (std::is_void_v<Exception_T>)
773 {
774 *this << colour (op) << "throws";
775 }
776 else
777 {
778 // The type name is not a string operand, so it is not quoted.
779 *this << colour (op) << "throws<";
781 *this << ">";
782 }
784 return (*this << colour_none ());
785 }
786
797 {
798 *this << colour (op) << "nothrow";
800 return (*this << colour_none ());
801 }
802
809 inline void
811 {
812 if (static_cast<bool> (op))
813 {
814 return;
815 }
816
818 {
819 buffer_.append (", but a std::exception was thrown: ");
820 *this << std::string_view{ op.what () };
821 }
822 else if (op.outcome () == detail::throw_outcome::unknown_exception)
823 {
824 buffer_.append (", but an exception of unknown type was thrown");
825 }
826 else
827 {
828 buffer_.append (", but nothing was thrown");
829 }
830 }
831#endif // defined(__cpp_exceptions)
832
833 // --------------------------------------------------------------------------
834
841 template <class T>
843 void
844 plain_text::append (std::string& text, const T& value)
845 {
846 expression_formatter formatter{ std::move (text) };
847 formatter << value;
848 text = std::move (formatter.buffer_);
849 }
850
851 // --------------------------------------------------------------------------
852} // namespace micro_os_plus::micro_test_plus::detail
853
854#if defined(__GNUC__)
855#pragma GCC diagnostic pop
856#endif // defined(__GNUC__)
857
858// ----------------------------------------------------------------------------
859
860#endif // defined(__cplusplus)
861
862// ----------------------------------------------------------------------------
863
864#endif // MICRO_OS_PLUS_MICRO_TEST_PLUS_INLINES_EXPRESSION_FORMATTER_INLINES_H_
865
866// ----------------------------------------------------------------------------

Generated via doxygen2docusaurus 2.2.8 by Doxygen 1.18.0.