Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
trace_impl.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <fmt/chrono.h>
6#include <fmt/core.h>
7#include <fmt/format.h>
8
9#include <atomic>
10#include <cctype>
11#include <chrono>
12#include <cstdint>
13#include <cstdlib>
14#include <cstring>
15#include <filesystem>
16#include <format>
17#include <fstream>
18#include <functional>
19#include <ranges>
20#include <type_traits>
21
22// implementation of trace functions.
23// TODO: the formatting part, uncluding formatValue, could be moved to a public header
24
25// trace namespace: Global settings for our trace facility.
26namespace trace
27{
28
29namespace detail
30{
31
32template <typename T, std::integral U, typename... Extra>
33inline std::int64_t get_ulps(T const& a, T const& b, U ulps, Extra&&... extra)
34{
35 return static_cast<std::int64_t>(ulps);
36}
37
38template <typename T> inline std::int64_t get_ulps(T const& a, T const& b) { return 4; }
39
40template <typename T, typename... Extra> inline std::int64_t get_ulps(T const& a, T const& b, Extra&&...) { return 4; }
41
42} // namespace detail
43
44struct FormattingOptions;
45
48inline FormattingOptions& get_formatting_options(const std::string& module = "");
49
52{
53 //--- Precision settings ---------------------------------------------------
54
57
60
63
64 //--- Output layout ---------------------------------------------------------
65
68
69 //--- Color configuration --------------------------------------------------
70
73 {
74 enum Enum
75 {
79 };
80 static constexpr Enum Default = autocolor;
81 static constexpr const char* StaticName = "Color options (yes/no/auto)";
82 static constexpr std::array<const char*, 3> Names = {"yes", "no", "auto"};
83 };
84
87
90
91 //--- Global flags ----------------------------------------------------------
92
94 inline static bool errorsAbort = true;
95
97 bool timestamp = false;
98
102 {
103 enum Enum
104 {
108 };
109 static constexpr Enum Default = auto_detect;
110 static constexpr const char* StaticName = "Thread-id options (yes/no/auto)";
111 static constexpr std::array<const char*, 3> Names = {"yes", "no", "auto"};
112 };
113
116
117 //--- Output sink -----------------------------------------------------------
118
120 FILE* outputStream = stderr;
121
122 // The output sink is a function that takes a string and returns void
123 // Default version writes to stderr
124 using Sink = std::function<void(std::string)>;
125
127 Sink sink = [](std::string s) {
128 std::fputs(s.c_str(), stderr);
129 std::fflush(stderr);
130 };
131
134
137
140
141 //--- Style map -------------------------------------------------------------
142
144 mutable std::map<std::string, terminal::TerminalStyle> Styles;
145
146 private:
147 static ColorOptions color_option_from_policy(uni20::presentation::color_mode mode)
148 {
149 using CO = ColorOptions::Enum;
150 switch (mode)
151 {
153 return CO::yes;
155 return CO::no;
157 return CO::autocolor;
158 }
159 return CO::autocolor;
160 }
161
162 public:
163 //--- Constructors ---------------------------------------------------------
164
168 {
169 color = color_option_from_policy(renderPolicy.color);
170 updateShowColor();
171
172 // Default style definitions
173 static constexpr std::pair<std::string_view, std::string_view> kDefaults[] = {{"TRACE", "Cyan"},
174 {"DEBUG_TRACE", "Green"},
175 {"TRACE_EXPR", "Blue"},
176 {"TRACE_VALUE", ""},
177 {"TRACE_MODULE", "Cyan;Bold"},
178 {"TRACE_FILENAME", "Red"},
179 {"TRACE_LINE", "Bold"},
180 {"TRACE_STRING", "Cyan"},
181 {"CHECK", "Red"},
182 {"DEBUG_CHECK", "Red"},
183 {"PRECONDITION", "Red"},
184 {"DEBUG_PRECONDITION", "Red"},
185 {"PANIC", "Red"},
186 {"ERROR", "Red"},
187 {"TIMESTAMP", "LightGray"},
188 {"THREAD_ID", "LightMagenta"}};
189
190 for (auto [kind, def] : kDefaults)
191 {
192 std::string env = std::string("UNI20_COLOR_") + kind;
193 Styles[std::string(kind)] = terminal::getenv_or_default<terminal::TerminalStyle>(env, def);
194 }
195
196 // Global sink override
197 if (auto* path = std::getenv("UNI20_TRACEFILE"))
198 {
199 FILE* out = nullptr;
200
201 if (std::strcmp(path, "-") == 0 || std::strcmp(path, "stdout") == 0)
202 {
203 out = stdout;
204 }
205 else if (std::strcmp(path, "stderr") == 0)
206 {
207 out = stderr;
208 }
209 else if (std::strlen(path) > 0)
210 {
211 bool append = false;
212 if (path[0] == '+')
213 {
214 ++path;
215 append = true;
216 }
217 out = std::fopen(path, append ? "a" : "w");
218 }
219
220 if (out)
221 {
222 this->set_output_stream(out);
223 }
224 }
225
226 // Precision
227 fp_precision_float32 = terminal::getenv_or_default<int>("UNI20_FP_PRECISION_FLOAT32", 6);
228 fp_precision_float64 = terminal::getenv_or_default<int>("UNI20_FP_PRECISION_FLOAT64", 15);
229 fp_precision_float128 = terminal::getenv_or_default<int>("UNI20_FP_PRECISION_FLOAT128", 36);
230
231 // Global flags
232 timestamp = terminal::getenv_or_default<terminal::toggle>("UNI20_TRACE_TIMESTAMP", true);
233 threadId = parse_thread_id_option_from_env("UNI20_TRACE_THREAD_ID", threadId);
234 }
235
242 FormattingOptions(std::string_view module)
243 {
244 *this = trace::get_formatting_options(""); // inherit all global settings
245
246 std::string mod{module};
247
248 // Style "TRACE" is special: the inherited version comes from "TRACE_MODULE", as a default for all modules
249 Styles["TRACE"] = Styles["TRACE_MODULE"];
250 // Override with _MODULE_XXXX versions from the environment, if they exist
251 static constexpr std::string_view kDefaults[] = {"TRACE", "DEBUG_TRACE", "TRACE_EXPR",
252 "TRACE_VALUE", "TRACE_FILENAME", "TRACE_LINE",
253 "TRACE_STRING", "TIMESTAMP", "THREAD_ID"};
254
255 for (auto kind : kDefaults)
256 {
257 std::string env = fmt::format("UNI20_COLOR_{}_MODULE_{}", kind, module);
258 terminal::TerminalStyle def = Styles[std::string(kind)];
259 Styles[std::string(kind)] = terminal::getenv_or_default<terminal::TerminalStyle>(env, def);
260 }
261
262 // Module sink override via UNI20_TRACEFILE_MODULE_<MODULE>
263 if (auto* path = std::getenv(("UNI20_TRACEFILE_MODULE_" + mod).c_str()))
264 {
265 FILE* out = nullptr;
266
267 if (std::strcmp(path, "-") == 0 || std::strcmp(path, "stdout") == 0)
268 {
269 out = stdout;
270 }
271 else if (std::strcmp(path, "stderr") == 0)
272 {
273 out = stderr;
274 }
275 else if (std::strlen(path) > 0)
276 {
277 bool append = false;
278 if (path[0] == '+')
279 {
280 ++path;
281 append = true;
282 }
283 out = std::fopen(path, append ? "a" : "w");
284 }
285
286 if (out)
287 {
288 this->set_output_stream(out);
289 }
290 }
291
292 // Precision overrides
294 terminal::getenv_or_default<int>("UNI20_FP_PRECISION_FLOAT32_MODULE_" + mod, fp_precision_float32);
296 terminal::getenv_or_default<int>("UNI20_FP_PRECISION_FLOAT64_MODULE_" + mod, fp_precision_float64);
298 terminal::getenv_or_default<int>("UNI20_FP_PRECISION_FLOAT128_MODULE_" + mod, fp_precision_float128);
299
300 // flags overrides
301 timestamp = terminal::getenv_or_default<terminal::toggle>("UNI20_TRACE_TIMESTAMP_MODULE_" + mod, timestamp);
302 threadId = parse_thread_id_option_from_env("UNI20_TRACE_THREAD_ID_MODULE_" + mod, threadId);
303 }
304
305 //--- Public Interface ------------------------------------------------------
306
309 {
310 sink = std::move(s);
311 outputStream = nullptr;
313 updateShowColor();
314 }
315
317 void set_output_stream(FILE* f)
318 {
319 outputStream = f;
321 sink = [f](std::string s) {
322 std::fputs(s.c_str(), f);
323 std::fflush(f);
324 };
325 updateShowColor();
326 }
327
330 {
331 color = c;
332 updateShowColor();
333 }
334
337 {
338 color = color_option_from_policy(c);
339 updateShowColor();
340 }
341
343 bool should_show_color() const { return showColor; }
344
348
352
356
360
364
368
372 {
373 auto policy = mdspanFormatPolicy.numeric;
375 policy.float64_precision = fp_precision_float64;
376 policy.float128_precision = fp_precision_float128;
378 return policy;
379 }
380
382 static void set_errors_abort(bool b) { errorsAbort = b; }
383
385 static bool errors_abort() { return errorsAbort; }
386
388 // terminal::TerminalStyle get_module_terminal_style(const std::string& kind, const std::string& module) const
389 // {
390 // std::string key = kind + "_MODULE_" + module;
391 // if (!Styles.count(key))
392 // {
393 // std::string env_mod = "UNI20_COLOR_" + kind + "_MODULE_" + module;
394 // std::string env_glob = "UNI20_COLOR_" + kind;
395 // Styles[key] = terminal::getenv_or_default<terminal::TerminalStyle>(
396 // env_mod, terminal::getenv_or_default<terminal::TerminalStyle>(env_glob, Styles[kind]));
397 // }
398 // return Styles[key];
399 // }
400
402 std::string format_style(const std::string& str, const std::string& kind) const
403 {
404 // empty module name = global/default styles
405 if (Styles.find(kind) == Styles.end())
406 {
407 fmt::print(stderr, "UNEXPECTED: unknown format style: {}\n", kind);
408 return str;
409 }
410
412 this->append_style(text, str, kind);
413 return this->render(text);
414 }
415
420 std::string format_glyph(uni20::presentation::semantic_glyph glyph, std::string const& kind) const
421 {
422 if (Styles.find(kind) == Styles.end())
423 {
424 fmt::print(stderr, "UNEXPECTED: unknown format style: {}\n", kind);
426 }
427
429 this->append_glyph(text, glyph, kind);
430 return this->render(text);
431 }
432
437 void append_style(uni20::presentation::styled_text& text, std::string_view str, std::string const& kind) const
438 {
439 auto const it = Styles.find(kind);
440 if (it == Styles.end())
441 {
442 fmt::print(stderr, "UNEXPECTED: unknown format style: {}\n", kind);
443 text.append(str);
444 return;
445 }
446 text.append(str, it->second);
447 }
448
454 std::string const& kind) const
455 {
456 auto const it = Styles.find(kind);
457 if (it == Styles.end())
458 {
459 fmt::print(stderr, "UNEXPECTED: unknown format style: {}\n", kind);
461 return;
462 }
463 text.append(glyph, it->second);
464 }
465
469 [[nodiscard]] terminal::TerminalStyle presentation_style(std::string_view kind) const
470 {
471 auto const it = Styles.find(std::string(kind));
472 return it == Styles.end() ? terminal::TerminalStyle{} : it->second;
473 }
474
482
483 private:
484 static ThreadIdOptions parse_thread_id_option_from_string(std::string_view value, ThreadIdOptions fallback)
485 {
486 using Mode = ThreadIdOptions::Enum;
487
488 if (iequals(value, "auto")) return Mode::auto_detect;
489
490 if (iequals(value, "yes") || iequals(value, "true") || iequals(value, "on") || iequals(value, "1"))
491 return Mode::yes;
492
493 if (iequals(value, "no") || iequals(value, "false") || iequals(value, "off") || iequals(value, "0"))
494 return Mode::no;
495
496 return fallback;
497 }
498
499 static ThreadIdOptions parse_thread_id_option_from_env(std::string const& var, ThreadIdOptions fallback)
500 {
501 if (auto const* raw = std::getenv(var.c_str()))
502 {
503 return parse_thread_id_option_from_string(raw, fallback);
504 }
505 return fallback;
506 }
507
509 void updateShowColor()
510 {
511 using CO = ColorOptions::Enum;
513 if (color == CO::yes)
515 else if (color == CO::no)
517 else /* auto */
519
521 }
522
523 friend FormattingOptions& get_formatting_options(const std::string& module);
524};
525
528inline FormattingOptions& get_formatting_options(const std::string& module)
529{
530 static std::recursive_mutex mtx;
531 static std::unordered_map<std::string, FormattingOptions> table;
532 std::lock_guard lock(mtx);
533
534 auto it = table.find(module);
535 if (it != table.end()) return it->second;
536
537 // else construct new
538
539 if (module.empty())
540 {
541 // Use default-constructed instance with no recursion
542 auto [it, _] = table.try_emplace("", FormattingOptions());
543 return it->second;
544 }
545 else
546 {
547 // Copy from global instance
548 auto [it, _] = table.try_emplace(module, module);
549 return it->second;
550 }
551}
552
553// Concept for a type that fmt can format.
554template <typename T>
555concept HasFmtFormatter = fmt::is_formattable<std::remove_cvref_t<T>, char>::value;
556
557// NVCC 12.x cannot parse libstdc++'s std::formattable implementation. CUDA
558// translation units retain the fmt path and omit this optional host formatter.
559template <typename T, typename CharT = char>
560#if defined(__CUDACC__)
561concept HasStdFormatter = false;
562#else
563concept HasStdFormatter = std::formattable<std::remove_cvref_t<T>, CharT>;
564#endif
565
566// Formatted output of containers, if they look like a range and have no fmt formatter.
567template <typename T>
568concept Container = std::ranges::forward_range<T> && (!HasFmtFormatter<T>);
569
570// formatValue: Converts a value to a string using fmt::format.
571// The generic version works for most types.
572
578template <typename T>
579std::string formatValue(const T& value, FormattingOptions const& opts)
581{
582 return fmt::format("{}", value);
583}
584
585template <typename T>
586std::string formatValue(const T& value, FormattingOptions const& /*opts*/)
588{
589 return std::format("{}", value);
590}
591
597template <uni20::Real T> inline std::string formatValue(T value, FormattingOptions const& opts)
598{
600}
601
607template <uni20::Complex T>
609inline std::string formatValue(T const& value, FormattingOptions const& opts)
610{
612}
613
614// Overload for any coroutine_handle type (primary template)
615template <typename Promise>
616inline std::string formatValue(const std::coroutine_handle<Promise>& h, FormattingOptions const& /*opts*/)
617{
618 // Print the address as a pointer (may be null)
619 return fmt::format("coroutine_handle<{}> @ {:p}", uni20::demangle::demangle(typeid(h.promise()).name()),
620 reinterpret_cast<const void*>(h.address()));
621}
622
623// Overload for std::coroutine_handle<>
624inline std::string formatValue(const std::coroutine_handle<>& h, FormattingOptions const& /*opts*/)
625{
626 return fmt::format("coroutine_handle<> @ {:p}", reinterpret_cast<const void*>(h.address()));
627}
628
634template <Container ContainerType>
635auto formatValue(const ContainerType& c,
636 FormattingOptions const& opts) -> std::vector<decltype(formatValue(*std::begin(c), opts))>
637{
638 std::vector<decltype(formatValue(*std::begin(c), opts))> result;
639 result.reserve(std::ranges::distance(c));
640 for (auto const& elem : c)
641 {
642 result.push_back(formatValue(elem, opts));
643 }
644 return result;
645}
646
651inline std::string formatValue(std::string_view s, FormattingOptions const& /*opts*/)
652{
653 if (s.empty()) return std::string("(null)");
654 return fmt::format("{}", s);
655}
656
661inline std::string formatValue(const char* s, FormattingOptions const& /*opts*/)
662{
663 if (!s) return std::string("(null)");
664 return fmt::format("{}", s);
665}
666
671inline std::string formatValue(char* s, FormattingOptions const& opts)
672{
673 return formatValue(static_cast<const char*>(s), opts);
674}
675
682template <typename U>
683inline std::string formatValue(U* ptr, FormattingOptions const& /*opts*/)
684 requires(!std::is_same_v<U, char> && !std::is_same_v<U, const char>)
685{
686 return fmt::format("{}* @ {:p}", uni20::demangle::demangle(typeid(U).name()), fmt::ptr(ptr));
687}
688
689template <typename T>
693
695{
696 normal,
697 fatal
698};
699
700namespace detail
701{
702inline bool trace_dump_enabled()
703{
704 if (auto const* value = std::getenv("UNI20_TRACE_DUMP"))
705 {
706 std::string_view mode(value);
707 if (iequals(mode, "never") || iequals(mode, "no") || iequals(mode, "off") || iequals(mode, "false") ||
708 iequals(mode, "0"))
709 {
710 return false;
711 }
712 }
713 return true;
714}
715
716inline std::string sanitize_dump_label(std::string_view label)
717{
718 std::string out;
719 out.reserve(std::min<std::size_t>(label.size(), 80));
720 for (auto const ch : label)
721 {
722 auto const c = static_cast<unsigned char>(ch);
723 if (std::isalnum(c) || ch == '_' || ch == '-' || ch == '.')
724 {
725 out.push_back(ch);
726 }
727 else if (!out.empty() && out.back() != '_')
728 {
729 out.push_back('_');
730 }
731 if (out.size() >= 80) break;
732 }
733 if (out.empty()) out = "value";
734 return out;
735}
736
737inline std::filesystem::path trace_dump_directory()
738{
739 if (auto const* dir = std::getenv("UNI20_TRACE_DUMP_DIR"); dir != nullptr && std::strlen(dir) > 0)
740 {
741 return std::filesystem::path(dir);
742 }
743 return std::filesystem::temp_directory_path() / "uni20-trace";
744}
745
746inline std::filesystem::path next_trace_dump_path(std::string_view label)
747{
748 static std::atomic<std::uint64_t> counter = 0;
749 auto dir = trace_dump_directory();
750 std::filesystem::create_directories(dir);
751 auto const now = std::chrono::system_clock::now().time_since_epoch();
752 auto const stamp = std::chrono::duration_cast<std::chrono::microseconds>(now).count();
753 auto const id = counter.fetch_add(1, std::memory_order_relaxed);
754 return dir / fmt::format("uni20_trace_{}_{}_{}.txt", stamp, id, sanitize_dump_label(label));
755}
756
757template <typename MDS, std::size_t Rank>
758std::string dump_index_string(std::array<typename std::remove_cvref_t<MDS>::index_type, Rank> const& indices)
759{
760 if constexpr (Rank == 0)
761 {
762 return "[]";
763 }
764 else
765 {
766 std::string out = "[";
767 for (std::size_t i = 0; i < Rank; ++i)
768 {
769 if (i > 0) out += ", ";
770 out += std::to_string(static_cast<std::size_t>(indices[i]));
771 }
772 out += "]";
773 return out;
774 }
775}
776
777inline std::string dump_shape_string(std::vector<std::size_t> const& extents)
778{
779 std::string out = "shape=(";
780 for (std::size_t i = 0; i < extents.size(); ++i)
781 {
782 if (i > 0) out += ", ";
783 out += std::to_string(extents[i]);
784 }
785 out += ")";
786 return out;
787}
788
789template <typename MDS, typename ElementFormatter, std::size_t Rank>
790void write_mdspan_dump_recursive(std::ofstream& out, MDS const& mds,
791 std::array<typename std::remove_cvref_t<MDS>::index_type, Rank>& indices,
792 std::size_t axis, uni20::presentation::output_policy const& policy,
793 ElementFormatter& formatter)
794{
795 if (axis == Rank)
796 {
797 out << dump_index_string<MDS>(indices) << '\t'
798 << uni20::presentation::render_text(std::invoke(formatter, mds[indices]), policy) << '\n';
799 return;
800 }
801
802 auto const extent = static_cast<std::size_t>(mds.extent(axis));
803 for (std::size_t i = 0; i < extent; ++i)
804 {
805 indices[axis] = static_cast<typename std::remove_cvref_t<MDS>::index_type>(i);
806 write_mdspan_dump_recursive(out, mds, indices, axis + 1, policy, formatter);
807 }
808}
809
810template <uni20::presentation::mdspan_like MDS, typename ElementFormatter>
811std::string write_mdspan_dump(MDS const& mds, FormattingOptions const& opts, std::string_view label,
812 std::vector<std::size_t> const& extents, std::size_t element_count,
813 ElementFormatter&& formatter)
814{
815 if (!trace_dump_enabled()) return "full data dump disabled by UNI20_TRACE_DUMP";
816
817 try
818 {
819 auto path = next_trace_dump_path(label);
820 std::ofstream out(path);
821 if (!out) return "full data dump failed: could not open " + path.string();
822
823 auto dump_policy = opts.presentation_policy();
824 dump_policy.color = uni20::presentation::color_mode::never;
825 dump_policy.charset = uni20::presentation::text_charset::utf8;
826 dump_policy.wrap_width = std::nullopt;
827
828 out << "# Uni20 trace mdspan dump\n";
829 out << "# " << dump_shape_string(extents) << '\n';
830 out << "# elements=" << element_count << '\n';
831 out << "# index\tvalue\n";
832
833 constexpr std::size_t rank = std::remove_cvref_t<MDS>::rank();
834 using index_type = typename std::remove_cvref_t<MDS>::index_type;
835 auto element_formatter = std::forward<ElementFormatter>(formatter);
836 std::array<index_type, rank> indices{};
837 if constexpr (rank == 0)
838 {
839 out << "[]\t" << uni20::presentation::render_text(std::invoke(element_formatter, mds[indices]), dump_policy)
840 << '\n';
841 }
842 else
843 {
844 write_mdspan_dump_recursive(out, mds, indices, 0, dump_policy, element_formatter);
845 }
846
847 return "full data: " + path.string();
848 }
849 catch (std::exception const& ex)
850 {
851 return std::string("full data dump failed: ") + ex.what();
852 }
853}
854} // namespace detail
855
856template <uni20::presentation::mdspan_like MDS>
857inline std::string formatMdspanForTrace(MDS const& mds, FormattingOptions const& opts, int value_width,
858 trace_format_context context, std::string_view label)
859{
860 auto preview_options = opts.mdspan_preview_policy();
861 preview_options.format = opts.mdspan_format_policy();
862 preview_options.format.numeric = opts.numeric_format_policy();
863
864 auto policy = opts.presentation_policy();
865 if (value_width > 0)
866 {
867 policy.wrap_width = static_cast<std::size_t>(value_width);
868 }
869
870 auto formatter = [&opts](auto const& value) { return formatValue(value, opts); };
871 auto preview = uni20::presentation::format_mdspan_preview(mds, policy, formatter, preview_options);
872 if (context == trace_format_context::fatal && preview.elided)
873 {
874 preview.text += "\n";
875 preview.text += detail::write_mdspan_dump(mds, opts, label, preview.extents, preview.element_count, formatter);
876 }
877 return preview.text;
878}
879
885template <uni20::presentation::mdspan_like MDS>
886inline std::string formatValue(MDS const& mds, FormattingOptions const& opts)
887{
889}
890
896template <HasPresentationMdspanView T> inline std::string formatValue(T const& value, FormattingOptions const& opts)
897{
898 return formatValue(value.mdspan(), opts);
899}
900
901template <uni20::presentation::mdspan_like MDS>
902inline std::string formatValueForContext(MDS const& mds, FormattingOptions const& opts, int value_width,
903 trace_format_context context, std::string_view label)
904{
905 return formatMdspanForTrace(mds, opts, value_width, context, label);
906}
907
908template <HasPresentationMdspanView T>
909inline std::string formatValueForContext(T const& value, FormattingOptions const& opts, int value_width,
910 trace_format_context context, std::string_view label)
911{
912 return formatMdspanForTrace(value.mdspan(), opts, value_width, context, label);
913}
914
915template <typename T>
916auto formatValueForContext(T const& value, FormattingOptions const& opts, int /*value_width*/,
917 trace_format_context /*context*/, std::string_view /*label*/)
918{
919 return formatValue(value, opts);
920}
921
922// Helper function to trim leading and trailing whitespace.
923inline std::string trim(const std::string& s)
924{
925 // Find the first non-space character.
926 size_t start = 0;
927 while (start < s.size() && std::isspace(static_cast<unsigned char>(s[start])))
928 ++start;
929 if (start == s.size())
930 {
931 return "";
932 }
933 // Find the last non-space character.
934 size_t end = s.size() - 1;
935 while (end > start && std::isspace(static_cast<unsigned char>(s[end])))
936 --end;
937 return s.substr(start, end - start + 1);
938}
939
940namespace detail
941{
942struct TraceNameValue
943{
944 std::string name;
945 std::string value;
946};
947} // namespace detail
948
950inline detail::TraceNameValue formatValue(detail::TraceNameValue const& nv, FormattingOptions const& opts)
951{
952 return nv;
953}
954
955// parseNames: Splits the stringified parameter list into tokens.
956// Each token is paired with a boolean flag that is true if a top-level
957// string or character literal was encountered.
958inline std::vector<std::pair<std::string, bool>> parseNames(std::string_view s)
959{
960 std::vector<std::pair<std::string, bool>> tokens;
961 std::string current;
962 bool tokenHasTopLevelLiteral = false;
963
964 // Counters for grouping symbols.
965 int parenCount = 0, squareCount = 0, curlyCount = 0;
966
967 // Flags for being inside a literal.
968 bool inDoubleQuote = false, inSingleQuote = false;
969
970 for (size_t i = 0; i < s.size(); ++i)
971 {
972 char c = s[i];
973
974 // Inside a double-quoted string literal.
975 if (inDoubleQuote)
976 {
977 current.push_back(c);
978 if (c == '\\')
979 {
980 // Copy the escaped character.
981 if (i + 1 < s.size())
982 {
983 current.push_back(s[i + 1]);
984 ++i;
985 }
986 }
987 else if (c == '"')
988 {
989 inDoubleQuote = false;
990 }
991 continue;
992 }
993
994 // Inside a single-quoted character literal.
995 if (inSingleQuote)
996 {
997 current.push_back(c);
998 if (c == '\\')
999 {
1000 if (i + 1 < s.size())
1001 {
1002 current.push_back(s[i + 1]);
1003 ++i;
1004 }
1005 }
1006 else if (c == '\'')
1007 {
1008 inSingleQuote = false;
1009 }
1010 continue;
1011 }
1012
1013 // At top-level, if we see a literal-start, mark the token.
1014 if ((c == '"' || c == '\'') && parenCount == 0 && squareCount == 0 && curlyCount == 0)
1015 {
1016 tokenHasTopLevelLiteral = true;
1017 if (c == '"')
1018 inDoubleQuote = true;
1019 else
1020 inSingleQuote = true;
1021 current.push_back(c);
1022 continue;
1023 }
1024
1025 // Split at commas if we are not inside any grouping.
1026 if (c == ',' && parenCount == 0 && squareCount == 0 && curlyCount == 0)
1027 {
1028 tokens.push_back({trim(current), tokenHasTopLevelLiteral});
1029 current.clear();
1030 tokenHasTopLevelLiteral = false;
1031 continue; // Skip adding the comma.
1032 }
1033
1034 // Update grouping counters.
1035 if (c == '(')
1036 ++parenCount;
1037 else if (c == ')')
1038 {
1039 if (parenCount > 0) --parenCount;
1040 }
1041 else if (c == '[')
1042 ++squareCount;
1043 else if (c == ']')
1044 {
1045 if (squareCount > 0) --squareCount;
1046 }
1047 else if (c == '{')
1048 ++curlyCount;
1049 else if (c == '}')
1050 {
1051 if (curlyCount > 0) --curlyCount;
1052 }
1053
1054 current.push_back(c);
1055 }
1056
1057 // Push any remaining text as the final token.
1058 if (!current.empty()) tokens.push_back({trim(current), tokenHasTopLevelLiteral});
1059
1060 return tokens;
1061}
1062
1063// returns true if str spans more than one line
1064inline bool isMultiline(const std::string& str) { return std::find(str.begin(), str.end(), '\n') != str.end(); }
1065
1066template <typename T> bool isMultiline(const std::vector<T>& str)
1067{
1068 for (const auto& s : str)
1069 {
1070 if (isMultiline(s)) return true;
1071 }
1072 return false;
1073}
1074
1075inline std::size_t displayWidth(std::string_view str, uni20::presentation::output_policy const& policy)
1076{
1077 return uni20::presentation::display_width(str, policy);
1078}
1079
1080inline std::size_t displayWidth(std::string_view str, FormattingOptions const& opts)
1081{
1082 return displayWidth(str, opts.presentation_policy());
1083}
1084
1085// get the maximum display width of a line for a multi-line string
1086inline std::size_t getMaxLineWidth(const std::string& str, uni20::presentation::output_policy const& policy)
1087{
1088 std::size_t maxWidth = 0;
1089 std::size_t lineStart = 0;
1090 while (lineStart <= str.size())
1091 {
1092 auto const lineEnd = str.find('\n', lineStart);
1093 auto const line = lineEnd == std::string::npos ? std::string_view(str).substr(lineStart)
1094 : std::string_view(str).substr(lineStart, lineEnd - lineStart);
1095 maxWidth = std::max(maxWidth, displayWidth(line, policy));
1096 if (lineEnd == std::string::npos) break;
1097 lineStart = lineEnd + 1;
1098 }
1099 return maxWidth;
1100}
1101
1102inline int getMaxLineWidth(const std::string& str)
1103{
1104 return static_cast<int>(getMaxLineWidth(str, uni20::presentation::plain_policy()));
1105}
1106
1107// recursively get the maximum width of a string or nested vectors of strings.
1108inline size_t maxWidth(const std::string& str, uni20::presentation::output_policy const& policy)
1109{
1110 return getMaxLineWidth(str, policy);
1111}
1112
1113inline size_t maxWidth(const std::string& str) { return maxWidth(str, uni20::presentation::plain_policy()); }
1114
1115template <typename T> size_t maxWidth(const std::vector<T>& elems, uni20::presentation::output_policy const& policy)
1116{
1117 size_t max_width = 0;
1118 for (const auto& e : elems)
1119 max_width = std::max(max_width, maxWidth(e, policy));
1120 return max_width;
1121}
1122
1123template <typename T> size_t maxWidth(const std::vector<T>& elems)
1124{
1126}
1127
1128inline bool canFormatAsPresentationMatrix(const std::vector<std::vector<std::string>>& elems)
1129{
1130 if (elems.empty()) return false;
1131
1132 auto const cols = elems.front().size();
1133 if (cols == 0) return false;
1134
1135 for (auto const& row : elems)
1136 {
1137 if (row.size() != cols) return false;
1138 if (isMultiline(row)) return false;
1139 }
1140 return true;
1141}
1142
1143inline std::string formatRectangularContainerAsPresentationMatrix(const std::vector<std::vector<std::string>>& elems,
1144 FormattingOptions const& opts)
1145{
1146 std::vector<std::string> flat;
1147 flat.reserve(elems.size() * elems.front().size());
1148 for (auto const& row : elems)
1149 {
1150 flat.insert(flat.end(), row.begin(), row.end());
1151 }
1152
1153 using extents_type = stdex::extents<std::size_t, stdex::dynamic_extent, stdex::dynamic_extent>;
1154 auto matrix = stdex::mdspan<std::string const, extents_type, stdex::layout_right>(flat.data(), elems.size(),
1155 elems.front().size());
1156 auto options = opts.mdspan_format_policy();
1157 options.numeric = opts.numeric_format_policy();
1159 matrix, opts.presentation_policy(), [](std::string const& value) { return value; }, options);
1160}
1161
1162inline std::string formatContainerToStringImpl(const std::string& elem,
1163 uni20::presentation::output_policy const& policy, size_t max_width = 0)
1164{
1165 return uni20::presentation::pad_left(elem, max_width, policy);
1166}
1167
1168inline std::string formatContainerToString(const std::string& elem, size_t max_width = 0)
1169{
1171}
1172
1173inline std::string formatContainerToString(const std::string& elem, FormattingOptions const& opts, size_t max_width = 0)
1174{
1175 return formatContainerToStringImpl(elem, opts.presentation_policy(), max_width);
1176}
1177
1178inline std::string formatContainerToStringImpl(const std::vector<std::string>& elems,
1179 uni20::presentation::output_policy const& policy, size_t max_width = 0)
1180{
1181 std::string inlineStr;
1182
1183 // The multiline case; put line breaks between each element
1184 if (isMultiline(elems))
1185 {
1186 inlineStr = "[\n";
1187 for (int i = 0; i < std::ssize(elems); ++i)
1188 {
1189 if (i > 0) inlineStr += ",\n ";
1190 inlineStr += uni20::presentation::indent_text(elems[i], 2, policy, false);
1191 }
1192 inlineStr += "\n]";
1193 }
1194 else
1195 {
1196 // Get the maximum width of the components
1197 max_width = std::max(max_width, maxWidth(elems, policy));
1198 inlineStr = "[ ";
1199 for (int i = 0; i < std::ssize(elems); ++i)
1200 {
1201 if (i > 0) inlineStr += ", ";
1202 inlineStr += formatContainerToStringImpl(elems[i], policy, max_width);
1203 }
1204 inlineStr += " ]";
1205 }
1206 return inlineStr;
1207}
1208
1209inline std::string formatContainerToString(const std::vector<std::string>& elems, size_t max_width = 0)
1210{
1212}
1213
1214inline std::string formatContainerToString(const std::vector<std::string>& elems, FormattingOptions const& opts,
1215 size_t max_width = 0)
1216{
1217 return formatContainerToStringImpl(elems, opts.presentation_policy(), max_width);
1218}
1219
1220// This probably works fine for arbitrary nesting
1221inline std::string formatContainerToStringImpl(const std::vector<std::vector<std::string>>& elems,
1222 uni20::presentation::output_policy const& policy, size_t max_width = 0)
1223{
1224 std::string inlineStr = "[ ";
1225 if (isMultiline(elems))
1226 {
1227 for (int i = 0; i < std::ssize(elems); ++i)
1228 {
1229 if (i > 0) inlineStr += ",\n ";
1230 inlineStr += uni20::presentation::indent_text(formatContainerToStringImpl(elems[i], policy), 2, policy, false);
1231 }
1232 inlineStr += "\n]";
1233 }
1234 else
1235 {
1236 // Get the maximum width of the components
1237 max_width = std::max(max_width, maxWidth(elems, policy));
1238 for (size_t i = 0; i < elems.size(); ++i)
1239 {
1240 if (i > 0) inlineStr += ",\n ";
1241 inlineStr += formatContainerToStringImpl(elems[i], policy, max_width);
1242 }
1243 inlineStr += " ]";
1244 }
1245 return inlineStr;
1246}
1247
1248inline std::string formatContainerToString(const std::vector<std::vector<std::string>>& elems, size_t max_width = 0)
1249{
1251}
1252
1253inline std::string formatContainerToString(const std::vector<std::vector<std::string>>& elems,
1254 FormattingOptions const& opts, size_t max_width = 0)
1255{
1257 {
1259 }
1260 return formatContainerToStringImpl(elems, opts.presentation_policy(), max_width);
1261}
1262
1263// For non-container (plain string) output.
1264inline uni20::presentation::styled_text formatItemText(const std::pair<std::string, bool>& name,
1265 const std::string& value, const FormattingOptions& opts,
1266 int available_width)
1267{
1269
1270 // If the name is a string literal, then just return the value separately
1271 if (name.second)
1272 {
1273 opts.append_style(text, value, "TRACE_STRING");
1274 return text;
1275 }
1276
1277 // If we'd spill over onto another line, then insert a line break
1278 auto const item_width = displayWidth(name.first, opts) + 3 + getMaxLineWidth(value, opts.presentation_policy());
1279 if (isMultiline(value) || (available_width > 0 && item_width > static_cast<std::size_t>(available_width)))
1280 {
1281 auto const indent = displayWidth(name.first, opts) + 3;
1282 text.append("\n");
1283 opts.append_style(text, name.first, "TRACE_EXPR");
1284 text.append(" = ");
1286 "TRACE_VALUE");
1287 return text;
1288 }
1289 opts.append_style(text, name.first, "TRACE_EXPR");
1290 text.append(" = ");
1291 opts.append_style(text, value, "TRACE_VALUE");
1292 return text;
1293}
1294
1295inline std::string formatItemString(const std::pair<std::string, bool>& name, const std::string& value,
1296 const FormattingOptions& opts, int available_width)
1297{
1298 return opts.render(formatItemText(name, value, opts, available_width));
1299}
1300
1301inline uni20::presentation::styled_text formatItemText(const std::pair<std::string, bool>& name,
1302 detail::TraceNameValue const& value,
1303 const FormattingOptions& opts, int available_width)
1304{
1306
1307 // If we'd spill over onto another line, then insert a line break
1308 auto const item_width = displayWidth(value.name, opts) + 3 + getMaxLineWidth(value.value, opts.presentation_policy());
1309 if (isMultiline(value.value) || (available_width > 0 && item_width > static_cast<std::size_t>(available_width)))
1310 {
1311 auto const indent = displayWidth(value.name, opts) + 3;
1312 text.append("\n");
1313 opts.append_style(text, value.name, "TRACE_EXPR");
1314 text.append(" = ");
1316 "TRACE_VALUE");
1317 return text;
1318 }
1319 opts.append_style(text, value.name, "TRACE_EXPR");
1320 text.append(" = ");
1321 opts.append_style(text, value.value, "TRACE_VALUE");
1322 return text;
1323}
1324
1325inline std::string formatItemString(const std::pair<std::string, bool>& name, detail::TraceNameValue const& value,
1326 const FormattingOptions& opts, int available_width)
1327{
1328 return opts.render(formatItemText(name, value, opts, available_width));
1329}
1330
1331// Containers (can be nested)
1332template <typename T>
1333uni20::presentation::styled_text formatItemText(const std::pair<std::string, bool>& name, const std::vector<T>& values,
1334 const FormattingOptions& opts, int available_width)
1335{
1336 std::string formatted = formatContainerToString(values, opts);
1337 return formatItemText(name, formatted, opts, available_width);
1338}
1339
1340template <typename T>
1341std::string formatItemString(const std::pair<std::string, bool>& name, const std::vector<T>& values,
1342 const FormattingOptions& opts, int available_width)
1343{
1344 return opts.render(formatItemText(name, values, opts, available_width));
1345}
1346
1347// formatTrace: Recursively formats all trace items into one string.
1348
1350formatParametersText(std::vector<std::pair<std::string, bool>>::const_iterator b, const FormattingOptions& opts)
1351{
1352 return {};
1353}
1354
1356formatParametersTextForContext(std::vector<std::pair<std::string, bool>>::const_iterator b,
1357 const FormattingOptions& opts, trace_format_context context)
1358{
1359 return {};
1360}
1361
1362inline std::string formatParameters(std::vector<std::pair<std::string, bool>>::const_iterator b,
1363 const FormattingOptions& opts)
1364{
1365 return opts.render(formatParametersText(b, opts));
1366}
1367
1368template <typename T, typename... Ts>
1370formatParametersTextForContext(std::vector<std::pair<std::string, bool>>::const_iterator b,
1371 const FormattingOptions& opts, trace_format_context context, const T& first,
1372 const Ts&... rest)
1373{
1374 auto value_width = opts.terminal_width;
1375 if (value_width > 0 && !b->second)
1376 {
1377 auto const indent = displayWidth(b->first, opts) + 3;
1378 if (indent < static_cast<std::size_t>(value_width))
1379 {
1380 value_width -= static_cast<int>(indent);
1381 }
1382 }
1383
1384 auto result =
1385 formatItemText(*b, formatValueForContext(first, opts, value_width, context, b->first), opts, opts.terminal_width);
1386
1387 ++b;
1388 if constexpr (sizeof...(rest) > 0)
1389 {
1390 result.append(", ");
1391 result.append(formatParametersTextForContext(b, opts, context, rest...));
1392 }
1393 return result;
1394}
1395
1396template <typename T, typename... Ts>
1397uni20::presentation::styled_text formatParametersText(std::vector<std::pair<std::string, bool>>::const_iterator b,
1398 const FormattingOptions& opts, const T& first, const Ts&... rest)
1399{
1400 return formatParametersTextForContext(b, opts, trace_format_context::normal, first, rest...);
1401}
1402
1403template <typename T, typename... Ts>
1404std::string formatParameters(std::vector<std::pair<std::string, bool>>::const_iterator b, const FormattingOptions& opts,
1405 const T& first, const Ts&... rest)
1406{
1407 return opts.render(formatParametersText(b, opts, first, rest...));
1408}
1409
1410template <typename... Args>
1412 const Args&... args)
1413{
1414 auto names = parseNames(exprList);
1415 return formatParametersText(names.begin(), opts, args...);
1416}
1417
1418template <typename... Args>
1420 trace_format_context context, const Args&... args)
1421{
1422 auto names = parseNames(exprList);
1423 return formatParametersTextForContext(names.begin(), opts, context, args...);
1424}
1425
1426template <typename... Args>
1427std::string formatParameterList(const char* exprList, const FormattingOptions& opts, const Args&... args)
1428{
1429 return opts.render(formatParameterListText(exprList, opts, args...));
1430}
1431
1433template <typename... Args> void print(fmt::format_string<Args...> fmt_str, Args&&... args)
1434{
1435 auto& opts = get_formatting_options(); // empty-module defaults
1437 text.append(fmt::format(fmt_str, std::forward<Args>(args)...));
1438 opts.sink(opts.render(text));
1439}
1440
1441inline std::string format_timestamp()
1442{
1443 auto const now = std::chrono::system_clock::now();
1444 auto const whole_seconds = std::chrono::floor<std::chrono::seconds>(now);
1445 auto const nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(now - whole_seconds);
1446 auto const local_time = fmt::localtime(std::chrono::system_clock::to_time_t(whole_seconds));
1447 return fmt::format("{:%F %T}.{:09}", local_time, nanos.count());
1448}
1449
1450namespace detail
1451{
1452inline std::thread::id const startup_thread_id = std::this_thread::get_id();
1453} // namespace detail
1454
1456{
1458
1459 if (opts.threadId == Mode::yes) return true;
1460 if (opts.threadId == Mode::no) return false;
1461 return std::this_thread::get_id() != detail::startup_thread_id;
1462}
1463
1465{
1467 bool has_prefix = false;
1468
1469 if (opts.timestamp)
1470 {
1471 opts.append_style(prefix, format_timestamp(), "TIMESTAMP");
1472 has_prefix = true;
1473 }
1474
1475 if (should_show_thread_id(opts))
1476 {
1477 if (has_prefix) prefix.append(" ");
1478 auto id = std::hash<std::thread::id>{}(std::this_thread::get_id());
1479 std::string th = fmt::format("[TID {:>8x}]", id);
1480 opts.append_style(prefix, th, "THREAD_ID");
1481 has_prefix = true;
1482 }
1483
1484 if (has_prefix) prefix.append(" ");
1485 return prefix;
1486}
1487
1488inline std::string format_trace_prefix(FormattingOptions const& opts)
1489{
1490 return opts.render(format_trace_prefix_text(opts));
1491}
1492
1494 std::string_view label, std::string_view style_kind, const char* file, int line)
1495{
1496 opts.append_style(text, label, std::string(style_kind));
1497 text.append(" at ");
1498 opts.append_style(text, file, "TRACE_FILENAME");
1499 opts.append_style(text, fmt::format(":{}", line), "TRACE_LINE");
1500}
1501
1503 std::string_view label, std::string_view style_kind, const char* module,
1504 const char* file, int line)
1505{
1506 opts.append_style(text, label, std::string(style_kind));
1507 text.append(" in module ");
1508 opts.append_style(text, module, std::string(style_kind));
1509 text.append(" at ");
1510 opts.append_style(text, file, "TRACE_FILENAME");
1511 opts.append_style(text, fmt::format(":{}", line), "TRACE_LINE");
1512}
1513
1515 std::string_view style_kind, bool start_newline)
1516{
1517 if (start_newline)
1518 {
1519 text.append("\n ");
1520 }
1521 else
1522 {
1523 text.append(" ");
1524 }
1526 text.append(" ");
1527}
1528
1530 uni20::presentation::styled_text const& parameters, std::string_view style_kind,
1531 bool start_newline = false)
1532{
1533 if (parameters.empty()) return;
1534 append_parameter_separator(text, opts, style_kind, start_newline);
1535 text.append(parameters);
1536}
1537
1538//-----------------------------------------------------------------------------
1539// Non-module TRACE
1540//-----------------------------------------------------------------------------
1541template <typename... Args> void TraceCall(const char* exprList, const char* file, int line, const Args&... args)
1542{
1543 auto& opts = get_formatting_options();
1544
1545 auto parameters = formatParameterListText(exprList, opts, args...);
1546 auto text = format_trace_prefix_text(opts);
1547 append_trace_location(text, opts, "TRACE", "TRACE", file, line);
1548 append_optional_parameters(text, opts, parameters, "TRACE");
1549 text.append("\n");
1550 opts.sink(opts.render(text));
1551}
1552
1553//-----------------------------------------------------------------------------
1554// TRACE_ONCE
1555//-----------------------------------------------------------------------------
1556template <typename... Args> void TraceOnceCall(const char* exprList, const char* file, int line, const Args&... args)
1557{
1558 auto& opts = get_formatting_options();
1559
1560 auto parameters = formatParameterListText(exprList, opts, args...);
1561 auto text = format_trace_prefix_text(opts);
1562 append_trace_location(text, opts, "TRACE_ONCE", "TRACE", file, line);
1563 append_optional_parameters(text, opts, parameters, "TRACE");
1564 text.append("\n");
1565 opts.sink(opts.render(text));
1566}
1567
1568//-----------------------------------------------------------------------------
1569// Module-aware TRACE
1570//-----------------------------------------------------------------------------
1571template <typename... Args>
1572void TraceModuleCall(const char* module, const char* exprList, const char* file, int line, const Args&... args)
1573{
1574 auto& opts = get_formatting_options(module);
1575
1576 auto parameters = formatParameterListText(exprList, opts, args...);
1577 auto text = format_trace_prefix_text(opts);
1578 append_trace_module_location(text, opts, "TRACE", "TRACE", module, file, line);
1579 append_optional_parameters(text, opts, parameters, "TRACE");
1580 text.append("\n");
1581 opts.sink(opts.render(text));
1582}
1583
1584//-----------------------------------------------------------------------------
1585// Non-module DEBUG_TRACE
1586//-----------------------------------------------------------------------------
1587template <typename... Args> void DebugTraceCall(const char* exprList, const char* file, int line, const Args&... args)
1588{
1589 auto& opts = get_formatting_options();
1590
1591 auto parameters = formatParameterListText(exprList, opts, args...);
1592 auto text = format_trace_prefix_text(opts);
1593 append_trace_location(text, opts, "DEBUG_TRACE", "DEBUG_TRACE", file, line);
1594 append_optional_parameters(text, opts, parameters, "DEBUG_TRACE");
1595 text.append("\n");
1596 opts.sink(opts.render(text));
1597}
1598
1599//-----------------------------------------------------------------------------
1600// DEBUG_TRACE_ONCE
1601//-----------------------------------------------------------------------------
1602template <typename... Args>
1603void DebugTraceOnceCall(const char* exprList, const char* file, int line, const Args&... args)
1604{
1605 auto& opts = get_formatting_options();
1606
1607 auto parameters = formatParameterListText(exprList, opts, args...);
1608 auto text = format_trace_prefix_text(opts);
1609 append_trace_location(text, opts, "DEBUG_TRACE_ONCE", "DEBUG_TRACE", file, line);
1610 append_optional_parameters(text, opts, parameters, "DEBUG_TRACE");
1611 text.append("\n");
1612 opts.sink(opts.render(text));
1613}
1614
1615//-----------------------------------------------------------------------------
1616// Module-aware DEBUG_TRACE
1617//-----------------------------------------------------------------------------
1618template <typename... Args>
1619void DebugTraceModuleCall(const char* module, const char* exprList, const char* file, int line, const Args&... args)
1620{
1621 auto& opts = get_formatting_options(module);
1622
1623 auto parameters = formatParameterListText(exprList, opts, args...);
1624 auto text = format_trace_prefix_text(opts);
1625 append_trace_module_location(text, opts, "DEBUG_TRACE", "DEBUG_TRACE", module, file, line);
1626 append_optional_parameters(text, opts, parameters, "DEBUG_TRACE");
1627 text.append("\n");
1628 opts.sink(opts.render(text));
1629}
1630
1631namespace detail
1632{
1633inline uni20::presentation::output_policy diagnostic_presentation_policy(FormattingOptions const& opts)
1634{
1635 auto policy = opts.presentation_policy();
1636 if (!policy.wrap_width.has_value() && opts.terminal_width > 0)
1637 {
1638 policy.wrap_width = static_cast<std::size_t>(opts.terminal_width);
1639 }
1640 return policy;
1641}
1642
1643#if TRACE_HAS_STACKTRACE
1644inline uni20::presentation::stacktrace_format_options stacktrace_presentation_options(FormattingOptions const& opts)
1645{
1646 return uni20::presentation::stacktrace_format_options{
1647 .indent = 2,
1648 .tree_style = opts.presentation_style("TRACE_LINE"),
1649 .index_style = opts.presentation_style("TRACE_LINE"),
1650 .description_style = opts.presentation_style("TRACE"),
1651 .source_file_style = opts.presentation_style("TRACE_FILENAME"),
1652 .source_line_style = opts.presentation_style("TRACE_LINE"),
1653 };
1654}
1655#endif
1656
1657inline void emit_stacktrace(FormattingOptions& opts, std::string_view style_kind, std::size_t skip_frames)
1658{
1659#if TRACE_HAS_STACKTRACE
1661 opts.append_style(heading, "Stacktrace:", std::string(style_kind));
1662 heading.append("\n");
1663 opts.sink(opts.render(heading));
1664 auto const policy = diagnostic_presentation_policy(opts);
1665 opts.sink(opts.render(uni20::presentation::format_stacktrace(std::stacktrace::current(skip_frames), policy,
1666 stacktrace_presentation_options(opts))));
1667#else
1669 opts.append_glyph(warning, uni20::presentation::semantic_glyph::warning, std::string(style_kind));
1670 warning.append(" ");
1671 opts.append_style(warning, "WARNING: std::stacktrace is unavailable in this build; stacktrace omitted.",
1672 std::string(style_kind));
1673 warning.append("\n");
1674 opts.sink(opts.render(warning));
1675#endif
1676}
1677
1678[[noreturn]] inline void abort_with_stacktrace(FormattingOptions& opts, std::string const& message,
1679 std::string_view style_kind, std::size_t skip_frames)
1680{
1681 opts.sink(message);
1682 emit_stacktrace(opts, style_kind, skip_frames);
1683 std::fflush(nullptr); // flush all output streams
1684 std::abort();
1685}
1686
1687[[noreturn]] inline void abort_with_stacktrace(FormattingOptions& opts, uni20::presentation::styled_text const& message,
1688 std::string_view style_kind, std::size_t skip_frames)
1689{
1690 opts.sink(opts.render(message));
1691 emit_stacktrace(opts, style_kind, skip_frames);
1692 std::fflush(nullptr); // flush all output streams
1693 std::abort();
1694}
1695
1696[[noreturn]] inline void abort_after_diagnostic(FormattingOptions& opts,
1697 uni20::presentation::styled_text const& message)
1698{
1699 opts.sink(opts.render(message));
1700 std::fflush(nullptr);
1701 std::abort();
1702}
1703
1704template <typename... Args>
1705inline void emit_trace_line(FormattingOptions& opts, std::string_view label, std::string_view style_kind,
1706 const char* exprList, const char* file, int line, const Args&... args)
1707{
1708 auto parameters = formatParameterListText(exprList, opts, args...);
1709 auto text = format_trace_prefix_text(opts);
1710 append_trace_location(text, opts, label, style_kind, file, line);
1711 append_optional_parameters(text, opts, parameters, style_kind);
1712 text.append("\n");
1713 opts.sink(opts.render(text));
1714}
1715
1716template <typename... Args>
1717inline void emit_trace_line_module(FormattingOptions& opts, std::string_view label, std::string_view style_kind,
1718 const char* module, const char* exprList, const char* file, int line,
1719 const Args&... args)
1720{
1721 auto parameters = formatParameterListText(exprList, opts, args...);
1722 auto text = format_trace_prefix_text(opts);
1723 append_trace_module_location(text, opts, label, style_kind, module, file, line);
1724 append_optional_parameters(text, opts, parameters, style_kind);
1725 text.append("\n");
1726 opts.sink(opts.render(text));
1727}
1728
1729inline uni20::presentation::styled_text make_diagnostic_header(FormattingOptions const& opts, std::string_view label,
1730 std::string_view style_kind, const char* file, int line)
1731{
1733 auto const glyph = [&] {
1734 if (style_kind == "ERROR") return uni20::presentation::semantic_glyph::failure;
1735 if (style_kind == "CHECK" || style_kind == "DEBUG_CHECK" || style_kind == "PRECONDITION" ||
1736 style_kind == "DEBUG_PRECONDITION" || style_kind == "PANIC")
1737 {
1739 }
1741 }();
1742 opts.append_glyph(text, glyph, std::string(style_kind));
1743 text.append(" ");
1744 append_trace_location(text, opts, label, style_kind, file, line);
1745 return text;
1746}
1747
1748inline void append_condition_false(uni20::presentation::styled_text& text, FormattingOptions const& opts,
1749 const char* cond)
1750{
1751 text.append("\n");
1752 opts.append_style(text, cond, "TRACE_EXPR");
1753 text.append(" is ");
1754 opts.append_style(text, "false", "TRACE_VALUE");
1755 text.append("!");
1756}
1757
1758inline void append_not_equal(uni20::presentation::styled_text& text, FormattingOptions const& opts, const char* a,
1759 const char* b)
1760{
1761 text.append("\n");
1762 opts.append_style(text, a, "TRACE_EXPR");
1763 text.append(" is not equal to ");
1764 opts.append_style(text, b, "TRACE_EXPR");
1765 text.append("!");
1766}
1767
1768inline void append_not_approx_equal(uni20::presentation::styled_text& text, FormattingOptions const& opts,
1769 const char* a, const char* b, std::int64_t ulps)
1770{
1771 text.append("\n");
1772 opts.append_style(text, a, "TRACE_EXPR");
1773 text.append(" is not approx-equal to ");
1774 opts.append_style(text, b, "TRACE_EXPR");
1775 text.append(" (to ");
1776 opts.append_style(text, fmt::format("{}", ulps), "TRACE_EXPR");
1777 text.append(" ULP)!");
1778}
1779
1780inline void append_diagnostic_parameters(uni20::presentation::styled_text& text, FormattingOptions const& opts,
1781 uni20::presentation::styled_text const& parameters,
1782 std::string_view style_kind)
1783{
1784 append_optional_parameters(text, opts, parameters, style_kind, true);
1785 text.append("\n");
1786}
1787} // namespace detail
1788
1789template <typename... Args> void TraceStackCall(const char* exprList, const char* file, int line, const Args&... args)
1790{
1791 auto& opts = get_formatting_options();
1792 detail::emit_trace_line(opts, "TRACE_STACK", "TRACE", exprList, file, line, args...);
1793 detail::emit_stacktrace(opts, "TRACE", 2);
1794}
1795
1796template <typename... Args>
1797void TraceStackOnceCall(const char* exprList, const char* file, int line, const Args&... args)
1798{
1799 auto& opts = get_formatting_options();
1800 detail::emit_trace_line(opts, "TRACE_ONCE_STACK", "TRACE", exprList, file, line, args...);
1801 detail::emit_stacktrace(opts, "TRACE", 2);
1802}
1803
1804template <typename... Args>
1805void TraceModuleStackCall(const char* module, const char* exprList, const char* file, int line, const Args&... args)
1806{
1807 auto& opts = get_formatting_options(module);
1808 detail::emit_trace_line_module(opts, "TRACE_MODULE_STACK", "TRACE", module, exprList, file, line, args...);
1809 detail::emit_stacktrace(opts, "TRACE", 2);
1810}
1811
1812template <typename... Args>
1813void DebugTraceStackCall(const char* exprList, const char* file, int line, const Args&... args)
1814{
1815 auto& opts = get_formatting_options();
1816 detail::emit_trace_line(opts, "DEBUG_TRACE_STACK", "DEBUG_TRACE", exprList, file, line, args...);
1817 detail::emit_stacktrace(opts, "DEBUG_TRACE", 2);
1818}
1819
1820template <typename... Args>
1821void DebugTraceStackOnceCall(const char* exprList, const char* file, int line, const Args&... args)
1822{
1823 auto& opts = get_formatting_options();
1824 detail::emit_trace_line(opts, "DEBUG_TRACE_ONCE_STACK", "DEBUG_TRACE", exprList, file, line, args...);
1825 detail::emit_stacktrace(opts, "DEBUG_TRACE", 2);
1826}
1827
1828template <typename... Args>
1829void DebugTraceModuleStackCall(const char* module, const char* exprList, const char* file, int line,
1830 const Args&... args)
1831{
1832 auto& opts = get_formatting_options(module);
1833 detail::emit_trace_line_module(opts, "DEBUG_TRACE_MODULE_STACK", "DEBUG_TRACE", module, exprList, file, line,
1834 args...);
1835 detail::emit_stacktrace(opts, "DEBUG_TRACE", 2);
1836}
1837
1838template <typename... Args>
1839[[noreturn]] void CheckCall(const char* cond, const char* exprList, const char* file, int line, const Args&... args)
1840{
1841 auto& opts = get_formatting_options();
1842
1843 auto parameters = formatParameterListTextForContext(exprList, opts, trace_format_context::fatal, args...);
1844 auto text = detail::make_diagnostic_header(opts, "CHECK", "CHECK", file, line);
1845 detail::append_condition_false(text, opts, cond);
1846 detail::append_diagnostic_parameters(text, opts, parameters, "CHECK");
1847 detail::abort_with_stacktrace(opts, text, "CHECK", 2);
1848}
1849
1850//------------------------------------------------------------------------------
1851// DEBUG_CHECK
1852//------------------------------------------------------------------------------
1853template <typename... Args>
1854[[noreturn]] void DebugCheckCall(const char* cond, const char* exprList, const char* file, int line,
1855 const Args&... args)
1856{
1857 auto& opts = get_formatting_options();
1858
1859 auto parameters = formatParameterListTextForContext(exprList, opts, trace_format_context::fatal, args...);
1860 auto text = detail::make_diagnostic_header(opts, "DEBUG_CHECK", "DEBUG_CHECK", file, line);
1861 detail::append_condition_false(text, opts, cond);
1862 detail::append_diagnostic_parameters(text, opts, parameters, "DEBUG_CHECK");
1863 detail::abort_with_stacktrace(opts, text, "DEBUG_CHECK", 2);
1864}
1865
1866//------------------------------------------------------------------------------
1867// CHECK_EQUAL
1868//------------------------------------------------------------------------------
1869template <typename... Args>
1870[[noreturn]] void CheckEqualCall(const char* a, const char* b, const char* exprList, const char* file, int line,
1871 const Args&... args)
1872{
1873 auto& opts = get_formatting_options();
1874
1875 auto parameters = formatParameterListTextForContext(exprList, opts, trace_format_context::fatal, args...);
1876 auto text = detail::make_diagnostic_header(opts, "CHECK_EQUAL", "CHECK", file, line);
1877 detail::append_not_equal(text, opts, a, b);
1878 detail::append_diagnostic_parameters(text, opts, parameters, "CHECK");
1879 detail::abort_with_stacktrace(opts, text, "CHECK", 2);
1880}
1881
1882//------------------------------------------------------------------------------
1883// DEBUG_CHECK_EQUAL
1884//------------------------------------------------------------------------------
1885template <typename... Args>
1886[[noreturn]] void DebugCheckEqualCall(const char* a, const char* b, const char* exprList, const char* file, int line,
1887 const Args&... args)
1888{
1889 auto& opts = get_formatting_options();
1890
1891 auto parameters = formatParameterListTextForContext(exprList, opts, trace_format_context::fatal, args...);
1892 auto text = detail::make_diagnostic_header(opts, "DEBUG_CHECK_EQUAL", "DEBUG_CHECK", file, line);
1893 detail::append_not_equal(text, opts, a, b);
1894 detail::append_diagnostic_parameters(text, opts, parameters, "DEBUG_CHECK");
1895 detail::abort_with_stacktrace(opts, text, "DEBUG_CHECK", 2);
1896}
1897
1898//------------------------------------------------------------------------------
1899// CHECK_FLOATING_EQ
1900//------------------------------------------------------------------------------
1901template <typename... Args>
1902[[noreturn]] void CheckFloatingEqCall(const char* a, const char* b, std::int64_t ulps, const char* exprList,
1903 const char* file, int line, const Args&... args)
1904{
1905 auto& opts = get_formatting_options();
1906
1907 auto parameters = formatParameterListTextForContext(exprList, opts, trace_format_context::fatal, args...);
1908 auto text = detail::make_diagnostic_header(opts, "CHECK_FLOATING_EQ", "CHECK", file, line);
1909 detail::append_not_approx_equal(text, opts, a, b, ulps);
1910 detail::append_diagnostic_parameters(text, opts, parameters, "CHECK");
1911 detail::abort_with_stacktrace(opts, text, "CHECK", 2);
1912}
1913
1914//------------------------------------------------------------------------------
1915// DEBUG_CHECK_FLOATING_EQ
1916//------------------------------------------------------------------------------
1917template <typename... Args>
1918[[noreturn]] void DebugCheckFloatingEqCall(const char* a, const char* b, std::int64_t ulps, const char* exprList,
1919 const char* file, int line, const Args&... args)
1920{
1921 auto& opts = get_formatting_options();
1922
1923 auto parameters = formatParameterListTextForContext(exprList, opts, trace_format_context::fatal, args...);
1924 auto text = detail::make_diagnostic_header(opts, "DEBUG_CHECK_FLOATING_EQ", "DEBUG_CHECK", file, line);
1925 detail::append_not_approx_equal(text, opts, a, b, ulps);
1926 detail::append_diagnostic_parameters(text, opts, parameters, "DEBUG_CHECK");
1927 detail::abort_with_stacktrace(opts, text, "DEBUG_CHECK", 2);
1928}
1929
1930//------------------------------------------------------------------------------
1931// PRECONDITION
1932//------------------------------------------------------------------------------
1933template <typename... Args>
1934[[noreturn]] void PreconditionCall(const char* cond, const char* exprList, const char* file, int line,
1935 const Args&... args)
1936{
1937 auto& opts = get_formatting_options();
1938
1939 auto parameters = formatParameterListTextForContext(exprList, opts, trace_format_context::fatal, args...);
1940 auto text = detail::make_diagnostic_header(opts, "PRECONDITION", "PRECONDITION", file, line);
1941 detail::append_condition_false(text, opts, cond);
1942 detail::append_diagnostic_parameters(text, opts, parameters, "PRECONDITION");
1943 detail::abort_with_stacktrace(opts, text, "PRECONDITION", 2);
1944}
1945
1946//------------------------------------------------------------------------------
1947// DEBUG_PRECONDITION
1948//------------------------------------------------------------------------------
1949template <typename... Args>
1950[[noreturn]] void DebugPreconditionCall(const char* cond, const char* exprList, const char* file, int line,
1951 const Args&... args)
1952{
1953 auto& opts = get_formatting_options();
1954
1955 auto parameters = formatParameterListTextForContext(exprList, opts, trace_format_context::fatal, args...);
1956 auto text = detail::make_diagnostic_header(opts, "DEBUG_PRECONDITION", "DEBUG_PRECONDITION", file, line);
1957 detail::append_condition_false(text, opts, cond);
1958 detail::append_diagnostic_parameters(text, opts, parameters, "DEBUG_PRECONDITION");
1959 detail::abort_with_stacktrace(opts, text, "DEBUG_PRECONDITION", 2);
1960}
1961
1962//------------------------------------------------------------------------------
1963// PRECONDITION_EQUAL
1964//------------------------------------------------------------------------------
1965template <typename... Args>
1966[[noreturn]] void PreconditionEqualCall(const char* a, const char* b, const char* exprList, const char* file, int line,
1967 const Args&... args)
1968{
1969 auto& opts = get_formatting_options();
1970
1971 auto parameters = formatParameterListTextForContext(exprList, opts, trace_format_context::fatal, args...);
1972 auto text = detail::make_diagnostic_header(opts, "PRECONDITION_EQUAL", "PRECONDITION", file, line);
1973 detail::append_not_equal(text, opts, a, b);
1974 detail::append_diagnostic_parameters(text, opts, parameters, "PRECONDITION");
1975 detail::abort_with_stacktrace(opts, text, "PRECONDITION", 2);
1976}
1977
1978//------------------------------------------------------------------------------
1979// DEBUG_PRECONDITION_EQUAL
1980//------------------------------------------------------------------------------
1981template <typename... Args>
1982[[noreturn]] void DebugPreconditionEqualCall(const char* a, const char* b, const char* exprList, const char* file,
1983 int line, const Args&... args)
1984{
1985 auto& opts = get_formatting_options();
1986
1987 auto parameters = formatParameterListTextForContext(exprList, opts, trace_format_context::fatal, args...);
1988 auto text = detail::make_diagnostic_header(opts, "DEBUG_PRECONDITION_EQUAL", "DEBUG_PRECONDITION", file, line);
1989 detail::append_not_equal(text, opts, a, b);
1990 detail::append_diagnostic_parameters(text, opts, parameters, "DEBUG_PRECONDITION");
1991 detail::abort_with_stacktrace(opts, text, "DEBUG_PRECONDITION", 2);
1992}
1993
1994//------------------------------------------------------------------------------
1995// PRECONDITION_FLOATING_EQ
1996//------------------------------------------------------------------------------
1997template <typename... Args>
1998[[noreturn]] void PreconditionFloatingEqCall(const char* a, const char* b, std::int64_t ulps, const char* exprList,
1999 const char* file, int line, const Args&... args)
2000{
2001 auto& opts = get_formatting_options();
2002
2003 auto parameters = formatParameterListTextForContext(exprList, opts, trace_format_context::fatal, args...);
2004 auto text = detail::make_diagnostic_header(opts, "PRECONDITION_FLOATING_EQ", "PRECONDITION", file, line);
2005 detail::append_not_approx_equal(text, opts, a, b, ulps);
2006 detail::append_diagnostic_parameters(text, opts, parameters, "PRECONDITION");
2007 detail::abort_with_stacktrace(opts, text, "PRECONDITION", 2);
2008}
2009
2010//------------------------------------------------------------------------------
2011// DEBUG_PRECONDITION_FLOATING_EQ
2012//------------------------------------------------------------------------------
2013template <typename... Args>
2014[[noreturn]] void DebugPreconditionFloatingEqCall(const char* a, const char* b, std::int64_t ulps, const char* exprList,
2015 const char* file, int line, const Args&... args)
2016{
2017 auto& opts = get_formatting_options();
2018
2019 auto parameters = formatParameterListTextForContext(exprList, opts, trace_format_context::fatal, args...);
2020 auto text = detail::make_diagnostic_header(opts, "DEBUG_PRECONDITION_FLOATING_EQ", "DEBUG_PRECONDITION", file, line);
2021 detail::append_not_approx_equal(text, opts, a, b, ulps);
2022 detail::append_diagnostic_parameters(text, opts, parameters, "DEBUG_PRECONDITION");
2023 detail::abort_with_stacktrace(opts, text, "DEBUG_PRECONDITION", 2);
2024}
2025
2026//------------------------------------------------------------------------------
2027// PANIC
2028//------------------------------------------------------------------------------
2029template <typename... Args>
2030[[noreturn]] void PanicCall(const char* exprList, const char* file, int line, const Args&... args)
2031{
2032 auto& opts = get_formatting_options();
2033
2034 auto parameters = formatParameterListTextForContext(exprList, opts, trace_format_context::fatal, args...);
2035 auto text = detail::make_diagnostic_header(opts, "PANIC", "PANIC", file, line);
2036 append_optional_parameters(text, opts, parameters, "PANIC");
2037 text.append("\n");
2038 detail::abort_with_stacktrace(opts, text, "PANIC", 2);
2039}
2040
2041//------------------------------------------------------------------------------
2042// ERROR
2043//------------------------------------------------------------------------------
2044template <typename... Args>
2045[[noreturn]] void ErrorCall(const char* exprList, const char* file, int line, const Args&... args)
2046{
2047 auto& opts = get_formatting_options();
2048
2049 auto const context = opts.errors_abort() ? trace_format_context::fatal : trace_format_context::normal;
2050 auto parameters = formatParameterListTextForContext(exprList, opts, context, args...);
2051 auto text = detail::make_diagnostic_header(opts, "ERROR", "ERROR", file, line);
2052 append_optional_parameters(text, opts, parameters, "ERROR");
2053 text.append("\n");
2054
2055 if (opts.errors_abort())
2056 {
2057 detail::abort_with_stacktrace(opts, text, "ERROR", 2);
2058 }
2059 throw std::runtime_error(opts.render(text));
2060}
2061
2062//------------------------------------------------------------------------------
2063// ERROR_IF
2064//------------------------------------------------------------------------------
2065template <typename... Args>
2066[[noreturn]] void ErrorIfCall(const char* cond, const char* exprList, const char* file, int line, const Args&... args)
2067{
2068 auto& opts = get_formatting_options();
2069
2070 auto const context = opts.errors_abort() ? trace_format_context::fatal : trace_format_context::normal;
2071 auto parameters = formatParameterListTextForContext(exprList, opts, context, args...);
2072 auto text = detail::make_diagnostic_header(opts, "ERROR", "ERROR", file, line);
2073 append_optional_parameters(text, opts, parameters, "ERROR");
2074 detail::append_condition_false(text, opts, cond);
2075 text.append("\n");
2076
2077 if (opts.errors_abort())
2078 {
2079 detail::abort_with_stacktrace(opts, text, "ERROR", 2);
2080 }
2081 throw std::runtime_error(opts.render(text));
2082}
2083
2084namespace detail
2085{
2086template <class Error>
2087concept HasDiagnosticReport = requires(Error const& error) {
2088 { diagnostic_report(error) } -> std::same_as<uni20::presentation::report_builder>;
2089};
2090
2091inline void append_diagnostic_context(uni20::presentation::styled_text& text, FormattingOptions const& opts,
2092 uni20::diagnostic_error const& error,
2094{
2095 if (error.source_location().has_value())
2096 {
2097 auto const& location = *error.source_location();
2098 opts.append_style(text, "Source location:", "ERROR");
2099 text.append("\n ");
2100 opts.append_style(text, location.file_name(), "TRACE_FILENAME");
2101 opts.append_style(text, fmt::format(":{}", location.line()), "TRACE_LINE");
2102 text.append("\n");
2103 }
2104
2105#if TRACE_HAS_STACKTRACE
2106 if (error.stacktrace().has_value())
2107 {
2108 opts.append_style(text, "Stacktrace:", "ERROR");
2109 text.append("\n");
2110 text.append(
2111 uni20::presentation::format_stacktrace(*error.stacktrace(), policy, stacktrace_presentation_options(opts)));
2112 }
2113#endif
2114}
2115
2116template <class Error>
2117 requires std::derived_from<std::remove_cvref_t<Error>, uni20::diagnostic_error> && HasDiagnosticReport<Error>
2118uni20::presentation::styled_text format_diagnostic_text(FormattingOptions const& opts, Error const& error,
2120{
2122 append_diagnostic_context(text, opts, error, policy);
2123 return text;
2124}
2125
2126template <class Error>
2127uni20::presentation::styled_text raised_error_text(FormattingOptions const& opts, Error const& error,
2128 std::source_location where)
2129{
2130 if constexpr (HasDiagnosticReport<Error>)
2131 {
2132 if constexpr (std::derived_from<std::remove_cvref_t<Error>, uni20::diagnostic_error>)
2133 {
2134 return format_diagnostic_text(opts, error, diagnostic_presentation_policy(opts));
2135 }
2136 else
2137 {
2138 return uni20::presentation::render_report(diagnostic_report(error), opts.presentation_policy());
2139 }
2140 }
2141 else
2142 {
2143 auto text = make_diagnostic_header(opts, "ERROR", "ERROR", where.file_name(), static_cast<int>(where.line()));
2144 text.append("\n");
2145 opts.append_style(text, error.what(), "TRACE_VALUE");
2146 text.append("\n");
2147 return text;
2148 }
2149}
2150} // namespace detail
2151
2157template <class Error>
2158 requires std::derived_from<std::remove_cvref_t<Error>, uni20::diagnostic_error> && detail::HasDiagnosticReport<Error>
2160{
2161 return detail::format_diagnostic_text(get_formatting_options(), error, policy);
2162}
2163
2168template <class Error>
2169 requires std::derived_from<std::remove_cvref_t<Error>, uni20::diagnostic_error> && detail::HasDiagnosticReport<Error>
2171{
2172 auto& opts = get_formatting_options();
2173 return detail::format_diagnostic_text(opts, error, detail::diagnostic_presentation_policy(opts));
2174}
2175
2182template <class Exception>
2183 requires std::derived_from<std::remove_cvref_t<Exception>, std::exception>
2184[[noreturn]] void raise(Exception&& exception, std::source_location where = std::source_location::current())
2185{
2186 using error_type = std::remove_cvref_t<Exception>;
2187 error_type error(std::forward<Exception>(exception));
2188
2189 if constexpr (std::derived_from<error_type, uni20::diagnostic_error>)
2190 {
2191 error.set_source_location(where);
2192#if TRACE_HAS_STACKTRACE
2193 error.set_stacktrace(std::stacktrace::current(1));
2194#endif
2195 }
2196
2197 auto& opts = get_formatting_options();
2198 if (opts.errors_abort())
2199 {
2200 auto text = detail::raised_error_text(opts, error, where);
2201#if TRACE_HAS_STACKTRACE
2202 if constexpr (std::derived_from<error_type, uni20::diagnostic_error> && detail::HasDiagnosticReport<error_type>)
2203 {
2204 if (error.stacktrace().has_value())
2205 {
2206 detail::abort_after_diagnostic(opts, text);
2207 }
2208 }
2209#endif
2210 detail::abort_with_stacktrace(opts, text, "ERROR", 2);
2211 }
2212 throw error;
2213}
2214
2215} // namespace trace
typename Traits::Enum Enum
Definition namedenum.hpp:39
Definition terminal.hpp:262
Base class for exceptions raised through Uni20's diagnostic boundary.
Definition diagnostic_error.hpp:25
A sequence of styled text spans and semantic glyph tokens.
Definition presentation.hpp:216
styled_text & append(std::string_view text, terminal::TerminalStyle style={})
Append a text span.
Definition presentation.cpp:1164
bool empty() const noexcept
Return whether this document has no spans.
Definition presentation.cpp:1182
Definition trace_impl.hpp:568
Definition trace_impl.hpp:555
Definition trace_impl.hpp:690
Definition trace_impl.hpp:563
Concept for types that are either real or complex.
Definition scalar_concepts.hpp:87
Concept for real scalar types (float, double, etc.).
Definition scalar_concepts.hpp:69
Detect mdspan-like objects without depending on trace or tensor.
Definition presentation_mdspan.hpp:70
locale_t value
Definition data_table.cpp:49
presentation::styled_text text
Definition display.cpp:141
Formatting support for mdspan extents.
bool is_a_terminal(std::FILE *stream)
Test whether a C output stream refers to a terminal.
Definition terminal.cpp:508
int columns()
Return the output terminal column count.
Definition terminal.cpp:502
Definition trace.hpp:52
std::size_t displayWidth(std::string_view str, uni20::presentation::output_policy const &policy)
Definition trace_impl.hpp:1075
std::string formatMdspanForTrace(MDS const &mds, FormattingOptions const &opts, int value_width, trace_format_context context, std::string_view label)
Definition trace_impl.hpp:857
void CheckCall(const char *cond, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1839
void TraceModuleStackCall(const char *module, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1805
void DebugTraceCall(const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1587
void DebugCheckCall(const char *cond, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1854
void print(fmt::format_string< Args... > fmt_str, Args &&... args)
Print formatted string via the default (empty-module) sink.
Definition trace_impl.hpp:1433
std::string format_trace_prefix(FormattingOptions const &opts)
Definition trace_impl.hpp:1488
void raise(Exception &&exception, std::source_location where=std::source_location::current())
Raise a concrete exception through Uni20's configured error boundary.
Definition trace_impl.hpp:2184
std::string formatRectangularContainerAsPresentationMatrix(const std::vector< std::vector< std::string > > &elems, FormattingOptions const &opts)
Definition trace_impl.hpp:1143
void PreconditionCall(const char *cond, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1934
uni20::presentation::styled_text formatItemText(const std::pair< std::string, bool > &name, const std::string &value, const FormattingOptions &opts, int available_width)
Definition trace_impl.hpp:1264
void append_optional_parameters(uni20::presentation::styled_text &text, FormattingOptions const &opts, uni20::presentation::styled_text const &parameters, std::string_view style_kind, bool start_newline=false)
Definition trace_impl.hpp:1529
uni20::presentation::styled_text formatParametersTextForContext(std::vector< std::pair< std::string, bool > >::const_iterator b, const FormattingOptions &opts, trace_format_context context)
Definition trace_impl.hpp:1356
void DebugPreconditionFloatingEqCall(const char *a, const char *b, std::int64_t ulps, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:2014
void PreconditionFloatingEqCall(const char *a, const char *b, std::int64_t ulps, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1998
void CheckEqualCall(const char *a, const char *b, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1870
void TraceStackCall(const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1789
void DebugCheckFloatingEqCall(const char *a, const char *b, std::int64_t ulps, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1918
void DebugTraceStackOnceCall(const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1821
std::size_t getMaxLineWidth(const std::string &str, uni20::presentation::output_policy const &policy)
Definition trace_impl.hpp:1086
std::string formatItemString(const std::pair< std::string, bool > &name, const std::string &value, const FormattingOptions &opts, int available_width)
Definition trace_impl.hpp:1295
void PreconditionEqualCall(const char *a, const char *b, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1966
FormattingOptions & get_formatting_options(const std::string &module="")
get the FormattingOptions object for a given module; use "" as the module name (or default parameter)...
Definition trace_impl.hpp:528
void DebugPreconditionCall(const char *cond, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1950
void append_trace_module_location(uni20::presentation::styled_text &text, FormattingOptions const &opts, std::string_view label, std::string_view style_kind, const char *module, const char *file, int line)
Definition trace_impl.hpp:1502
std::string formatValue(const T &value, FormattingOptions const &opts)
Format a non-container, non-Uni20-scalar type with an fmt formatter.
Definition trace_impl.hpp:579
void append_parameter_separator(uni20::presentation::styled_text &text, FormattingOptions const &opts, std::string_view style_kind, bool start_newline)
Definition trace_impl.hpp:1514
void DebugCheckEqualCall(const char *a, const char *b, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1886
std::string trim(const std::string &s)
Definition trace_impl.hpp:923
void DebugTraceOnceCall(const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1603
uni20::presentation::styled_text formatParameterListText(const char *exprList, const FormattingOptions &opts, const Args &... args)
Definition trace_impl.hpp:1411
uni20::presentation::styled_text format_trace_prefix_text(FormattingOptions const &opts)
Definition trace_impl.hpp:1464
std::vector< std::pair< std::string, bool > > parseNames(std::string_view s)
Definition trace_impl.hpp:958
uni20::presentation::styled_text formatParameterListTextForContext(const char *exprList, const FormattingOptions &opts, trace_format_context context, const Args &... args)
Definition trace_impl.hpp:1419
void TraceStackOnceCall(const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1797
std::string formatValueForContext(MDS const &mds, FormattingOptions const &opts, int value_width, trace_format_context context, std::string_view label)
Definition trace_impl.hpp:902
trace_format_context
Definition trace_impl.hpp:695
void PanicCall(const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:2030
std::string formatContainerToStringImpl(const std::string &elem, uni20::presentation::output_policy const &policy, size_t max_width=0)
Definition trace_impl.hpp:1162
void DebugTraceModuleStackCall(const char *module, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1829
void DebugPreconditionEqualCall(const char *a, const char *b, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1982
uni20::presentation::styled_text formatParametersText(std::vector< std::pair< std::string, bool > >::const_iterator b, const FormattingOptions &opts)
Definition trace_impl.hpp:1350
bool canFormatAsPresentationMatrix(const std::vector< std::vector< std::string > > &elems)
Definition trace_impl.hpp:1128
void ErrorCall(const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:2045
void append_trace_location(uni20::presentation::styled_text &text, FormattingOptions const &opts, std::string_view label, std::string_view style_kind, const char *file, int line)
Definition trace_impl.hpp:1493
void ErrorIfCall(const char *cond, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:2066
std::string format_timestamp()
Definition trace_impl.hpp:1441
void TraceModuleCall(const char *module, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1572
std::string formatParameters(std::vector< std::pair< std::string, bool > >::const_iterator b, const FormattingOptions &opts)
Definition trace_impl.hpp:1362
uni20::presentation::styled_text format_diagnostic(Error const &error, uni20::presentation::output_policy const &policy)
Format a structured diagnostic using its ADL presentation report and attached context.
Definition trace_impl.hpp:2159
bool should_show_thread_id(FormattingOptions const &opts)
Definition trace_impl.hpp:1455
void DebugTraceModuleCall(const char *module, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1619
bool isMultiline(const std::string &str)
Definition trace_impl.hpp:1064
void CheckFloatingEqCall(const char *a, const char *b, std::int64_t ulps, const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1902
void DebugTraceStackCall(const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1813
void TraceOnceCall(const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1556
size_t maxWidth(const std::string &str, uni20::presentation::output_policy const &policy)
Definition trace_impl.hpp:1108
void TraceCall(const char *exprList, const char *file, int line, const Args &... args)
Definition trace_impl.hpp:1541
std::string formatParameterList(const char *exprList, const FormattingOptions &opts, const Args &... args)
Definition trace_impl.hpp:1427
std::string formatContainerToString(const std::string &elem, size_t max_width=0)
Definition trace_impl.hpp:1168
Definition cli.cpp:6
presentation::report_builder diagnostic_report(CublasError const &error)
Build a presentation report for a cuBLAS provider failure.
Definition cublas_error_presentation.hpp:18
std::string demangle(const char *name)
Turn a compiler‐mangled name into a human‐readable one.
Definition demangle.hpp:22
void warning(checked_format_string< Args... > format, Args &&... args)
Emit a non-fatal warning display message.
Definition display.hpp:403
std::string format_mdspan(MDS const &mds, output_policy const &policy, ElementFormatter &&formatter, mdspan_format_options const &options={})
Render every mdspan/tensor element as display-cell-aligned text art.
Definition presentation_mdspan.hpp:585
semantic_glyph
Names semantic glyphs independently of their rendered spelling.
Definition presentation.hpp:117
std::string render_text(std::string_view text, output_policy const &policy)
Render raw UTF-8 text through charset and ASCII fallback policy.
Definition presentation.cpp:1231
std::string indent_text(std::string_view text, std::size_t spaces, output_policy const &policy, bool indent_first)
Indent each rendered line by a fixed number of spaces.
Definition presentation.cpp:2690
bool should_emit_color(output_policy const &policy)
Return whether a policy currently permits ANSI color output.
Definition presentation.cpp:1211
output_policy plain_policy()
Build a policy for plain file/log rendering with no ANSI color.
Definition presentation.cpp:1195
std::string render_glyph(semantic_glyph glyph, output_policy const &policy)
Render a semantic glyph according to an output policy.
Definition presentation.cpp:1218
color_mode
Selects when terminal color/style control sequences may be emitted.
Definition presentation.hpp:28
std::string format_real(T value, numeric_format_options const &options)
Format a real scalar value according to numeric presentation options.
Definition presentation.hpp:913
std::string format_complex(T const &value, numeric_format_options const &options)
Format a complex value as real+imagi.
Definition presentation.hpp:928
mdspan_preview_result format_mdspan_preview(MDS const &mds, output_policy const &policy, ElementFormatter &&formatter, mdspan_preview_options const &options={})
Render a bounded mdspan/tensor preview without visiting omitted elements.
Definition presentation_mdspan.hpp:632
output_policy terminal_policy(std::FILE *stream)
Build a policy for terminal rendering.
Definition presentation.cpp:1186
std::string pad_left(std::string_view text, std::size_t target_width, output_policy const &policy)
Left-pad rendered text to a target width.
Definition presentation.cpp:2571
std::string render(styled_text const &text, output_policy const &policy)
Render styled text with color/style sequences only when policy allows.
Definition presentation.cpp:1289
styled_text render_report(report_builder const &report, output_policy const &policy)
Format a high-level report builder into styled text.
Definition presentation.cpp:2366
std::size_t display_width(std::string_view text, output_policy const &policy, std::size_t initial_column)
Measure rendered text width under the selected policy.
Definition presentation.cpp:2560
std::ptrdiff_t index_type
Definition types.hpp:19
std::optional< semantic_glyph > glyph
Definition presentation.cpp:2435
bool iequals(std::string_view a, std::string_view b)
Definition string_util.hpp:52
Enables/disables/auto color for this module (from UNI20_COLOR or programmatic override).
Definition trace_impl.hpp:73
static constexpr Enum Default
Definition trace_impl.hpp:80
@ no
Definition trace_impl.hpp:77
@ autocolor
Definition trace_impl.hpp:78
@ yes
Definition trace_impl.hpp:76
static constexpr const char * StaticName
Definition trace_impl.hpp:81
static constexpr std::array< const char *, 3 > Names
Definition trace_impl.hpp:82
Controls thread-id prefixing (from UNI20_TRACE_THREAD_ID or module override).
Definition trace_impl.hpp:102
static constexpr const char * StaticName
Definition trace_impl.hpp:110
static constexpr Enum Default
Definition trace_impl.hpp:109
static constexpr std::array< const char *, 3 > Names
Definition trace_impl.hpp:111
@ auto_detect
Definition trace_impl.hpp:107
@ yes
Definition trace_impl.hpp:105
Configuration and formatting options for a trace module.
Definition trace_impl.hpp:52
std::map< std::string, terminal::TerminalStyle > Styles
Holds per-kind styles (keys like "TRACE", "TRACE_LINE", etc).
Definition trace_impl.hpp:144
int fp_precision_float128
Floating-point precision for formatting configured float128 values.
Definition trace_impl.hpp:62
int fp_precision_float64
Floating-point precision for formatting float64 values.
Definition trace_impl.hpp:59
FILE * outputStream
File to which this module writes (stderr by default, or UNI20_TRACEFILE).
Definition trace_impl.hpp:120
FormattingOptions(std::string_view module)
Module‐specific constructor.
Definition trace_impl.hpp:242
static bool errorsAbort
Abort on error if true. This is static, since it only makes sense globally.
Definition trace_impl.hpp:94
FormattingOptions()
Default constructor.
Definition trace_impl.hpp:167
Sink sink
Function that actually emits strings (defaults to fputs to stderr).
Definition trace_impl.hpp:127
bool should_show_color() const
Query whether color should be used in this module.
Definition trace_impl.hpp:343
bool timestamp
Prefix each trace with a timestamp (from UNI20_TRACE_TIMESTAMP or module override).
Definition trace_impl.hpp:97
uni20::presentation::output_policy renderPolicy
Shared presentation rendering policy used by trace formatting.
Definition trace_impl.hpp:133
void append_style(uni20::presentation::styled_text &text, std::string_view str, std::string const &kind) const
Append styled text without rendering it immediately.
Definition trace_impl.hpp:437
friend FormattingOptions & get_formatting_options(const std::string &module)
get the FormattingOptions object for a given module; use "" as the module name (or default parameter)...
Definition trace_impl.hpp:528
uni20::presentation::mdspan_format_options & mdspan_format_policy()
Return the shared mdspan/tensor presentation policy used by this trace module.
Definition trace_impl.hpp:355
NamedEnumeration< ThreadIdOptionTraits > ThreadIdOptions
Definition trace_impl.hpp:114
bool showColor
Whether to actually emit color sequences.
Definition trace_impl.hpp:89
std::function< void(std::string)> Sink
Definition trace_impl.hpp:124
uni20::presentation::mdspan_preview_options const & mdspan_preview_policy() const
Return the shared mdspan/tensor preview policy used by this trace module.
Definition trace_impl.hpp:367
uni20::presentation::output_policy const & presentation_policy() const
Return the shared presentation output policy used by this trace module.
Definition trace_impl.hpp:351
uni20::presentation::mdspan_preview_options mdspanPreviewPolicy
Presentation preview policy used for mdspan and tensor-like trace values.
Definition trace_impl.hpp:139
uni20::presentation::output_policy & presentation_policy()
Return the shared presentation output policy used by this trace module.
Definition trace_impl.hpp:347
terminal::TerminalStyle presentation_style(std::string_view kind) const
Return the terminal style associated with a trace style-map key.
Definition trace_impl.hpp:469
void set_color_output(uni20::presentation::color_mode c)
Enable, disable, or auto-detect color output using the shared presentation color mode.
Definition trace_impl.hpp:336
static bool errors_abort()
Query the abort-on-error setting.
Definition trace_impl.hpp:385
void set_output_stream(FILE *f)
Change the output FILE* for this module.
Definition trace_impl.hpp:317
void set_color_output(ColorOptions c)
Enable or disable color output for this module.
Definition trace_impl.hpp:329
std::string format_glyph(uni20::presentation::semantic_glyph glyph, std::string const &kind) const
Format a semantic glyph using this module's style for the given kind.
Definition trace_impl.hpp:420
std::string render(uni20::presentation::styled_text const &text) const
Render a presentation document through this trace module's policy.
Definition trace_impl.hpp:478
uni20::presentation::mdspan_format_options const & mdspan_format_policy() const
Return the shared mdspan/tensor presentation policy used by this trace module.
Definition trace_impl.hpp:359
int terminal_width
Maximum width (in characters) before switching to multi-line.
Definition trace_impl.hpp:67
uni20::presentation::mdspan_preview_options & mdspan_preview_policy()
Return the shared mdspan/tensor preview policy used by this trace module.
Definition trace_impl.hpp:363
uni20::presentation::numeric_format_options numeric_format_policy() const
Build numeric formatting controls from trace precision settings.
Definition trace_impl.hpp:371
int fp_precision_float32
Floating-point precision for formatting float32 values.
Definition trace_impl.hpp:56
ColorOptions color
Definition trace_impl.hpp:86
std::string format_style(const std::string &str, const std::string &kind) const
Get or compute the terminal style for a given kind in this module.
Definition trace_impl.hpp:402
uni20::presentation::mdspan_format_options mdspanFormatPolicy
Presentation tensor-art policy used for mdspan and tensor-like values.
Definition trace_impl.hpp:136
static void set_errors_abort(bool b)
Enable or disable abort-on-error for this module.
Definition trace_impl.hpp:382
void set_sink(Sink s)
Set a custom sink function for this module.
Definition trace_impl.hpp:308
void append_glyph(uni20::presentation::styled_text &text, uni20::presentation::semantic_glyph glyph, std::string const &kind) const
Append a styled semantic glyph without rendering it immediately.
Definition trace_impl.hpp:453
ThreadIdOptions threadId
Definition trace_impl.hpp:115
Formatting controls for mdspan and tensor display art.
Definition presentation_mdspan.hpp:42
numeric_format_options numeric
Definition presentation_mdspan.hpp:46
Bounded preview controls for mdspan and tensor display.
Definition presentation_mdspan.hpp:51
mdspan_format_options format
Definition presentation_mdspan.hpp:52
Numeric scalar formatting controls shared by tensor-style renderers.
Definition presentation.hpp:105
int float32_precision
Definition presentation.hpp:106
Output policy shared by presentation renderers and layout helpers.
Definition presentation.hpp:186
color_mode color
Definition presentation.hpp:187
std::FILE * output_stream
Definition presentation.hpp:196
std::optional< std::size_t > wrap_width
Definition presentation.hpp:194