Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
trace.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <uni20/config.hpp>
4
5#include "demangle.hpp"
7#include "floating_eq.hpp"
8#include "namedenum.hpp"
9#include "presentation.hpp"
12#include "terminal.hpp"
13
14#include <algorithm>
15#include <atomic>
16#include <cctype>
17#include <chrono>
18#include <complex>
19#include <concepts>
20#include <coroutine>
21#include <cstdio>
22#include <exception>
23#include <fmt/chrono.h>
24#include <fmt/core.h>
25#include <functional>
26#include <map>
27#include <source_location>
28#include <string>
29#include <string_view>
30#include <thread>
31#include <type_traits>
32#include <utility>
33#include <vector>
34
35// Check for stacktrace support (C++23 and GCC 13+ or Clang+libc++)
36#if UNI20_HAS_STACKTRACE
37#define TRACE_HAS_STACKTRACE 1
38#include <stacktrace>
39#else
40#define TRACE_HAS_STACKTRACE 0
41#endif
42
43#ifndef TRACE_DISABLE
44#define TRACE_DISABLE 0
45#endif
46
47// stringize helper (two levels needed so that macro args expand first)
48#define TRACE_STRINGIZE_IMPL(x) #x
49#define TRACE_STRINGIZE(x) TRACE_STRINGIZE_IMPL(x)
50
51namespace trace::detail
52{
53
54[[nodiscard]] constexpr bool is_constant_evaluation() noexcept { return std::is_constant_evaluated(); }
55
56} // namespace trace::detail
57
58// COMPILER_NOTE emits an information message during compilation. msg must be a string literal.
59#if defined(_MSC_VER)
60// On MSVC, use __pragma without needing quotes around the entire pragma.
61#define COMPILER_NOTE(msg) __pragma(message(msg))
62#else
63// On GCC/Clang, use the standard _Pragma operator with a string.
64#define COMPILER_NOTE(msg) _Pragma(TRACE_STRINGIZE(message msg))
65#endif
66
67// COMPILER_WARNING_NOTE(msg):
68// – On GCC/Clang: emits a compile‐time warning via an unnamed lambda with a deprecation attribute.
69// – On MSVC: no effect.
70#if defined(__clang__) || defined(__GNUC__)
71#define COMPILER_WARNING_NOTE(msg) \
72 do \
73 { \
74 ([]() __attribute__((deprecated(msg))){})(); \
75 } \
76 while (0)
77#else
78#define COMPILER_WARNING_NOTE(msg) \
79 do \
80 {} \
81 while (0)
82#endif
83
84// TRACE MACROS
85// These macros forward both the stringified expression list and the evaluated
86// arguments, along with file and line info, to the corresponding trace functions.
87// In constexpr context there doesn't seem to be any way to make these work - the only
88// option is that in constexpr context they expand to nothing
89#define TRACE(...) \
90 do \
91 { \
92 if (::trace::detail::is_constant_evaluation()) \
93 {} \
94 else \
95 { \
96 if constexpr (TRACE_DISABLE) \
97 {} \
98 else \
99 { \
100 ::trace::TraceCall(#__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
101 } \
102 } \
103 } \
104 while (0)
105
106#define TRACE_IF(cond, ...) \
107 do \
108 { \
109 if (::trace::detail::is_constant_evaluation()) \
110 {} \
111 else \
112 { \
113 if constexpr (TRACE_DISABLE) \
114 {} \
115 else if (cond) \
116 { \
117 if (::trace::detail::is_constant_evaluation()) \
118 {} \
119 else \
120 { \
121 ::trace::TraceCall(#__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
122 } \
123 } \
124 } \
125 } \
126 while (0)
127
128#define TRACE_ONCE(...) \
129 do \
130 { \
131 if (::trace::detail::is_constant_evaluation()) \
132 {} \
133 else \
134 { \
135 if constexpr (TRACE_DISABLE) \
136 {} \
137 else \
138 { \
139 static std::atomic_flag _trace_once_flag = ATOMIC_FLAG_INIT; \
140 if (!_trace_once_flag.test_and_set(std::memory_order_relaxed)) \
141 { \
142 ::trace::TraceOnceCall(#__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
143 } \
144 } \
145 } \
146 } \
147 while (0)
148
149#define TRACE_MODULE(m, ...) \
150 do \
151 { \
152 if (::trace::detail::is_constant_evaluation()) \
153 {} \
154 else \
155 { \
156 if constexpr (ENABLE_TRACE_##m) \
157 { \
158 if constexpr (TRACE_DISABLE) \
159 {} \
160 else \
161 { \
162 ::trace::TraceModuleCall(#m, #__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
163 } \
164 } \
165 } \
166 } \
167 while (0)
168
169#define TRACE_MODULE_IF(m, cond, ...) \
170 do \
171 { \
172 if (::trace::detail::is_constant_evaluation()) \
173 {} \
174 else \
175 { \
176 if constexpr (ENABLE_TRACE_##m) \
177 { \
178 if (cond) \
179 { \
180 if constexpr (TRACE_DISABLE) \
181 {} \
182 else \
183 { \
184 ::trace::TraceModuleCall(#m, #__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
185 } \
186 } \
187 } \
188 } \
189 } \
190 while (0)
191
192#define TRACE_STACK(...) \
193 do \
194 { \
195 if (::trace::detail::is_constant_evaluation()) \
196 {} \
197 else \
198 { \
199 if constexpr (TRACE_DISABLE) \
200 {} \
201 else \
202 { \
203 ::trace::TraceStackCall(#__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
204 } \
205 } \
206 } \
207 while (0)
208
209#define TRACE_IF_STACK(cond, ...) \
210 do \
211 { \
212 if (::trace::detail::is_constant_evaluation()) \
213 {} \
214 else \
215 { \
216 if constexpr (TRACE_DISABLE) \
217 {} \
218 else if (cond) \
219 { \
220 if (::trace::detail::is_constant_evaluation()) \
221 {} \
222 else \
223 { \
224 ::trace::TraceStackCall(#__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
225 } \
226 } \
227 } \
228 } \
229 while (0)
230
231#define TRACE_ONCE_STACK(...) \
232 do \
233 { \
234 if (::trace::detail::is_constant_evaluation()) \
235 {} \
236 else \
237 { \
238 if constexpr (TRACE_DISABLE) \
239 {} \
240 else \
241 { \
242 static std::atomic_flag _trace_once_stack_flag = ATOMIC_FLAG_INIT; \
243 if (!_trace_once_stack_flag.test_and_set(std::memory_order_relaxed)) \
244 { \
245 ::trace::TraceStackOnceCall(#__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
246 } \
247 } \
248 } \
249 } \
250 while (0)
251
252#define TRACE_MODULE_STACK(m, ...) \
253 do \
254 { \
255 if (::trace::detail::is_constant_evaluation()) \
256 {} \
257 else \
258 { \
259 if constexpr (ENABLE_TRACE_##m) \
260 { \
261 if constexpr (TRACE_DISABLE) \
262 {} \
263 else \
264 { \
265 ::trace::TraceModuleStackCall(#m, #__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
266 } \
267 } \
268 } \
269 } \
270 while (0)
271
272#define TRACE_MODULE_IF_STACK(m, cond, ...) \
273 do \
274 { \
275 if (::trace::detail::is_constant_evaluation()) \
276 {} \
277 else \
278 { \
279 if constexpr (ENABLE_TRACE_##m) \
280 { \
281 if (cond) \
282 { \
283 if constexpr (TRACE_DISABLE) \
284 {} \
285 else \
286 { \
287 ::trace::TraceModuleStackCall(#m, #__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
288 } \
289 } \
290 } \
291 } \
292 } \
293 while (0)
294
295// CHECK and PRECONDITION MACROS
296// These macros check a condition and, if false, print diagnostic information and abort.
297// They forward additional debug information similarly to the TRACE macros.
298
299#define CHECK(cond, ...) \
300 do \
301 { \
302 if (!(cond)) \
303 { \
304 ::trace::CheckCall(#cond, #__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
305 } \
306 } \
307 while (0)
308
309#define CHECK_EQUAL(a, b, ...) \
310 do \
311 { \
312 if (!((a) == (b))) \
313 { \
314 ::trace::CheckEqualCall(#a, #b, (#a "," #b __VA_OPT__("," #__VA_ARGS__)), __FILE__, __LINE__, a, \
315 b __VA_OPT__(, __VA_ARGS__)); \
316 } \
317 } \
318 while (0)
319
320// Bind extra arguments for the duration of the check so temporaries remain alive
321// and the failure diagnostic uses the same operands and tolerance as comparison.
322#define CHECK_FLOATING_EQ(a, b, ...) \
323 do \
324 { \
325 auto va = (a); \
326 auto vb = (b); \
327 using T = std::decay_t<decltype(va)>; \
328 static_assert(::uni20::check::UlpComparable<T>, "CHECK_FLOATING_EQ requires a ULP-comparable scalar"); \
329 [&](auto const&... extra) \
330 { \
331 auto const ulps = ::trace::detail::get_ulps(va, vb, extra...); \
332 if (!::uni20::check::FloatingULP<T>::eq(va, vb, ulps)) \
333 { \
334 ::trace::CheckFloatingEqCall(#a, #b, ulps, (#a "," #b __VA_OPT__("," #__VA_ARGS__)), \
335 __FILE__, __LINE__, va, vb, extra...); \
336 } \
337 }(__VA_ARGS__); \
338 } \
339 while (0)
340
341#define PRECONDITION(cond, ...) \
342 do \
343 { \
344 if (!(cond)) \
345 { \
346 ::trace::PreconditionCall(#cond, #__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
347 } \
348 } \
349 while (0)
350
351#define PRECONDITION_EQUAL(a, b, ...) \
352 do \
353 { \
354 if (!((a) == (b))) \
355 { \
356 ::trace::PreconditionEqualCall(#a, #b, (#a "," #b __VA_OPT__("," #__VA_ARGS__)), __FILE__, __LINE__, a, \
357 b __VA_OPT__(, __VA_ARGS__)); \
358 } \
359 } \
360 while (0)
361
362#define PRECONDITION_FLOATING_EQ(a, b, ...) \
363 do \
364 { \
365 auto va = (a); \
366 auto vb = (b); \
367 using T = std::decay_t<decltype(va)>; \
368 static_assert(::uni20::check::UlpComparable<T>, "PRECONDITION_FLOATING_EQ requires a ULP-comparable scalar"); \
369 [&](auto const&... extra) \
370 { \
371 auto const ulps = ::trace::detail::get_ulps(va, vb, extra...); \
372 if (!::uni20::check::FloatingULP<T>::eq(va, vb, ulps)) \
373 { \
374 ::trace::PreconditionFloatingEqCall(#a, #b, ulps, (#a "," #b __VA_OPT__("," #__VA_ARGS__)), \
375 __FILE__, __LINE__, va, vb, extra...); \
376 } \
377 }(__VA_ARGS__); \
378 } \
379 while (0)
380
381// PANIC is used to unconditionally abort
382#define PANIC(...) trace::PanicCall(#__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__));
383
384// ERROR MACROS
385// These macros report an error, printing debug information and then either abort
386// or throw an exception based on a global configuration flag.
387#define ERROR(...) trace::ErrorCall(#__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__));
388
389#define ERROR_IF(cond, ...) \
390 do \
391 { \
392 if (cond) \
393 { \
394 ::trace::ErrorIfCall(#cond, #__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
395 } \
396 } \
397 while (0)
398
399// ---------------------------------------------------------------------------
400// DEBUG MACROS (compile to nothing if NDEBUG is defined)
401#if defined(NDEBUG)
402#define DEBUG_TRACE(...) \
403 do \
404 {} \
405 while (0)
406#define DEBUG_TRACE_IF(...) \
407 do \
408 {} \
409 while (0)
410#define DEBUG_TRACE_ONCE(...) \
411 do \
412 {} \
413 while (0)
414#define DEBUG_TRACE_MODULE(...) \
415 do \
416 {} \
417 while (0)
418#define DEBUG_TRACE_MODULE_IF(...) \
419 do \
420 {} \
421 while (0)
422#define DEBUG_TRACE_STACK(...) \
423 do \
424 {} \
425 while (0)
426#define DEBUG_TRACE_IF_STACK(...) \
427 do \
428 {} \
429 while (0)
430#define DEBUG_TRACE_ONCE_STACK(...) \
431 do \
432 {} \
433 while (0)
434#define DEBUG_TRACE_MODULE_STACK(...) \
435 do \
436 {} \
437 while (0)
438#define DEBUG_TRACE_MODULE_IF_STACK(...) \
439 do \
440 {} \
441 while (0)
442#define DEBUG_CHECK(...) \
443 do \
444 {} \
445 while (0)
446#define DEBUG_CHECK_EQUAL(...) \
447 do \
448 {} \
449 while (0)
450#define DEBUG_CHECK_FLOATING_EQ(...) \
451 do \
452 {} \
453 while (0)
454#define DEBUG_PRECONDITION(...) \
455 do \
456 {} \
457 while (0)
458#define DEBUG_PRECONDITION_EQUAL(...) \
459 do \
460 {} \
461 while (0)
462#define DEBUG_PRECONDITION_FLOATING_EQ(...) \
463 do \
464 {} \
465 while (0)
466#else
467
468#define DEBUG_TRACE(...) \
469 do \
470 { \
471 if constexpr (TRACE_DISABLE) \
472 {} \
473 else \
474 ::trace::DebugTraceCall(#__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
475 } \
476 while (0)
477
478#define DEBUG_TRACE_IF(cond, ...) \
479 do \
480 { \
481 if constexpr (TRACE_DISABLE) \
482 {} \
483 else if (cond) \
484 { \
485 ::trace::DebugTraceCall(#__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
486 } \
487 } \
488 while (0)
489
490#define DEBUG_TRACE_ONCE(...) \
491 do \
492 { \
493 if (::trace::detail::is_constant_evaluation()) \
494 {} \
495 else \
496 { \
497 if constexpr (TRACE_DISABLE) \
498 {} \
499 else \
500 { \
501 static std::atomic_flag _trace_once_flag = ATOMIC_FLAG_INIT; \
502 if (!_trace_once_flag.test_and_set(std::memory_order_relaxed)) \
503 { \
504 ::trace::DebugTraceOnceCall(#__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
505 } \
506 } \
507 } \
508 } \
509 while (0)
510
511#define DEBUG_TRACE_MODULE(m, ...) \
512 do \
513 { \
514 if constexpr (TRACE_DISABLE) \
515 {} \
516 else if constexpr (ENABLE_TRACE_##m) \
517 { \
518 ::trace::DebugTraceModuleCall(#m, #__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
519 } \
520 } \
521 while (0)
522
523#define DEBUG_TRACE_MODULE_IF(m, cond, ...) \
524 do \
525 { \
526 if constexpr (TRACE_DISABLE) \
527 {} \
528 else if constexpr (ENABLE_TRACE_##m) \
529 { \
530 if (cond) \
531 { \
532 ::trace::DebugTraceModuleCall(#m, #__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
533 } \
534 } \
535 } \
536 while (0)
537
538#define DEBUG_TRACE_STACK(...) \
539 do \
540 { \
541 if constexpr (TRACE_DISABLE) \
542 {} \
543 else \
544 ::trace::DebugTraceStackCall(#__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
545 } \
546 while (0)
547
548#define DEBUG_TRACE_IF_STACK(cond, ...) \
549 do \
550 { \
551 if constexpr (TRACE_DISABLE) \
552 {} \
553 else if (cond) \
554 { \
555 ::trace::DebugTraceStackCall(#__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
556 } \
557 } \
558 while (0)
559
560#define DEBUG_TRACE_ONCE_STACK(...) \
561 do \
562 { \
563 if (::trace::detail::is_constant_evaluation()) \
564 {} \
565 else \
566 { \
567 if constexpr (TRACE_DISABLE) \
568 {} \
569 else \
570 { \
571 static std::atomic_flag _debug_trace_once_stack_flag = ATOMIC_FLAG_INIT; \
572 if (!_debug_trace_once_stack_flag.test_and_set(std::memory_order_relaxed)) \
573 { \
574 ::trace::DebugTraceStackOnceCall(#__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
575 } \
576 } \
577 } \
578 } \
579 while (0)
580
581#define DEBUG_TRACE_MODULE_STACK(m, ...) \
582 do \
583 { \
584 if constexpr (TRACE_DISABLE) \
585 {} \
586 else if constexpr (ENABLE_TRACE_##m) \
587 { \
588 ::trace::DebugTraceModuleStackCall(#m, #__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
589 } \
590 } \
591 while (0)
592
593#define DEBUG_TRACE_MODULE_IF_STACK(m, cond, ...) \
594 do \
595 { \
596 if constexpr (TRACE_DISABLE) \
597 {} \
598 else if constexpr (ENABLE_TRACE_##m) \
599 { \
600 if (cond) \
601 { \
602 ::trace::DebugTraceModuleStackCall(#m, #__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
603 } \
604 } \
605 } \
606 while (0)
607
608#define DEBUG_CHECK(cond, ...) \
609 do \
610 { \
611 if (!(cond)) \
612 { \
613 ::trace::DebugCheckCall(#cond, #__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
614 } \
615 } \
616 while (0)
617
618#define DEBUG_CHECK_EQUAL(a, b, ...) \
619 do \
620 { \
621 if (!((a) == (b))) \
622 { \
623 ::trace::DebugCheckEqualCall(#a, #b, (#a "," #b __VA_OPT__("," #__VA_ARGS__)), __FILE__, __LINE__, a, \
624 b __VA_OPT__(, __VA_ARGS__)); \
625 } \
626 } \
627 while (0)
628
629#define DEBUG_CHECK_FLOATING_EQ(a, b, ...) \
630 do \
631 { \
632 auto va = (a); \
633 auto vb = (b); \
634 using T = std::decay_t<decltype(va)>; \
635 static_assert(::uni20::check::UlpComparable<T>, "DEBUG_CHECK_FLOATING_EQ requires a ULP-comparable scalar"); \
636 [&](auto const&... extra) \
637 { \
638 auto const ulps = ::trace::detail::get_ulps(va, vb, extra...); \
639 if (!::uni20::check::FloatingULP<T>::eq(va, vb, ulps)) \
640 { \
641 ::trace::DebugCheckFloatingEqCall(#a, #b, ulps, (#a "," #b __VA_OPT__("," #__VA_ARGS__)), \
642 __FILE__, __LINE__, va, vb, extra...); \
643 } \
644 }(__VA_ARGS__); \
645 } \
646 while (0)
647
648#define DEBUG_PRECONDITION(cond, ...) \
649 do \
650 { \
651 if (!(cond)) \
652 { \
653 ::trace::DebugPreconditionCall(#cond, #__VA_ARGS__, __FILE__, __LINE__ __VA_OPT__(, __VA_ARGS__)); \
654 } \
655 } \
656 while (0)
657
658#define DEBUG_PRECONDITION_EQUAL(a, b, ...) \
659 do \
660 { \
661 if (!((a) == (b))) \
662 { \
663 ::trace::DebugPreconditionEqualCall(#a, #b, (#a "," #b __VA_OPT__("," #__VA_ARGS__)), __FILE__, __LINE__, a, \
664 b __VA_OPT__(, __VA_ARGS__)); \
665 } \
666 } \
667 while (0)
668
669#define DEBUG_PRECONDITION_FLOATING_EQ(a, b, ...) \
670 do \
671 { \
672 auto va = (a); \
673 auto vb = (b); \
674 using T = std::decay_t<decltype(va)>; \
675 static_assert(::uni20::check::UlpComparable<T>, "DEBUG_PRECONDITION_FLOATING_EQ requires a ULP-comparable scalar"); \
676 [&](auto const&... extra) \
677 { \
678 auto const ulps = ::trace::detail::get_ulps(va, vb, extra...); \
679 if (!::uni20::check::FloatingULP<T>::eq(va, vb, ulps)) \
680 { \
681 ::trace::DebugPreconditionFloatingEqCall(#a, #b, ulps, (#a "," #b __VA_OPT__("," #__VA_ARGS__)), \
682 __FILE__, __LINE__, va, vb, extra...); \
683 } \
684 }(__VA_ARGS__); \
685 } \
686 while (0)
687
688#endif
689
690#include "trace_impl.hpp"
691
692namespace trace
693{
694
695//
696// TracingBaseClass
697//
698
703template <size_t N> struct BaseName
704{
705 char value[N];
708 constexpr BaseName(const char (&str)[N])
709 {
710 for (size_t i = 0; i < N; ++i)
711 value[i] = str[i];
712 }
714 constexpr operator std::string_view() const { return {value, N - 1}; }
715};
716
724template <typename Derived> struct TracingBaseClass
725{
727 Derived const* This() const noexcept { return static_cast<Derived const*>(this); }
728 Derived* This() noexcept { return static_cast<Derived*>(this); }
729
731 [[nodiscard]] std::string DerivedName() const { return uni20::demangle::demangle(typeid(Derived).name()); }
732
733 template <typename T> detail::TraceNameValue OtherPointer(T* x) const
734 {
735 return detail::TraceNameValue("other", fmt::format("{:p}", fmt::ptr(x)));
736 }
737 [[nodiscard]] detail::TraceNameValue ThisPointer() const
738 {
739 return detail::TraceNameValue("this", fmt::format("{:p}", fmt::ptr(this)));
740 }
741
743 TracingBaseClass() { TRACE(DerivedName() + " default constructor", ThisPointer()); }
744
745 template <typename... Args> TracingBaseClass(Args&&... args)
746 {
747 TRACE(DerivedName() + " forwarding constructor", ThisPointer(), args...);
748 }
749
752 {
753 TRACE(DerivedName() + " copy constructor", ThisPointer(), OtherPointer(&other));
754 }
755
758 {
759 TRACE(DerivedName() + " move constructor", ThisPointer(), OtherPointer(&other));
760 }
761
763 TracingBaseClass(Derived&& other) noexcept
764 {
765 TRACE(DerivedName() + " move constructor", ThisPointer(), OtherPointer(&other));
766 }
767
770 {
771 if (this != &other)
772 {
773 TRACE(DerivedName() + " copy assignment", ThisPointer(), OtherPointer(&other));
774 }
775 return *this;
776 }
777
780 {
781 if (this != &other)
782 {
783 TRACE(DerivedName() + " move assignment", ThisPointer(), OtherPointer(&other));
784 }
785 return *this;
786 }
787
789 ~TracingBaseClass() { TRACE(DerivedName() + " destructor", ThisPointer()); }
790};
791
792#define TRACE_BASE_CLASS(Name) trace::TracingBaseClass<trace::BaseName(TRACE_STRINGIZE(Name)), Name>
793
794// #define TRACE_BASE_CLASS(Identifier) trace::TracingBaseClass<trace::BaseName{#Identifier}, Identifier>
795} // namespace trace
Structured exception base carrying diagnostic source context.
Definition trace.hpp:52
std::string demangle(const char *name)
Turn a compiler‐mangled name into a human‐readable one.
Definition demangle.hpp:22
Presentation-layer formatting for standard stacktraces.
Compile-time string wrapper for use as a non-type template parameter.
Definition trace.hpp:704
constexpr BaseName(const char(&str)[N])
Construct from a string literal.
Definition trace.hpp:708
char value[N]
The wrapped string literal.
Definition trace.hpp:705
CRTP tracing base class that emits messages with demangled type name.
Definition trace.hpp:725
TracingBaseClass(const TracingBaseClass &other)
Copy constructor. Emits a trace message.
Definition trace.hpp:751
~TracingBaseClass()
Destructor. Emits a trace message.
Definition trace.hpp:789
TracingBaseClass(Args &&... args)
Definition trace.hpp:745
Derived const * This() const noexcept
Get this pointer as Derived* (for CRTP usage).
Definition trace.hpp:727
std::string DerivedName() const
Get demangled class name of the CRTP derived class.
Definition trace.hpp:731
Derived * This() noexcept
Definition trace.hpp:728
TracingBaseClass(Derived &&other) noexcept
Move constructor. Emits a trace message.
Definition trace.hpp:763
TracingBaseClass & operator=(const TracingBaseClass &other)
Copy assignment. Emits a trace message.
Definition trace.hpp:769
detail::TraceNameValue OtherPointer(T *x) const
Definition trace.hpp:733
detail::TraceNameValue ThisPointer() const
Definition trace.hpp:737
TracingBaseClass()
Default constructor. Emits a trace message.
Definition trace.hpp:743
TracingBaseClass(TracingBaseClass &&other) noexcept
Move constructor. Emits a trace message.
Definition trace.hpp:757
TracingBaseClass & operator=(TracingBaseClass &&other) noexcept
Move assignment. Emits a trace message.
Definition trace.hpp:779
#define TRACE(...)
Definition trace.hpp:89