1 /** Various debug printing tools for debug printing in `@safe pure nothrow @nogc` code.
2  *
3  * Copyright: Per Nordlöw 2022-.
4  * License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
5  * Authors: $(WEB Per Nordlöw)
6  *
7  * See_Also: https://forum.dlang.org/post/svjjawiezudnugdyriig@forum.dlang.org
8  */
9 module nxt.dbgio;
10 
11 @safe pure:
12 
13 /** Debug print `args` followed by a newline.
14  *
15  * Similar to Rust's `dbg` macro introduced in version 1.32.
16  *
17  * See_Also: https://blog.rust-lang.org/2019/01/17/Rust-1.32.0.html#the-dbg-macro
18  * See_Also: https://forum.dlang.org/post/svjjawiezudnugdyriig@forum.dlang.org
19  * See_Also: http://forum.dlang.org/thread/yczwqrbkxdiqijtiynrh@forum.dlang.org?page=1
20  * TODO: use https://forum.dlang.org/post/ypxsqtddxvdxunsoluas@forum.dlang.org
21  * TODO: is using this https://dlang.org/changelog/2.079.0.html#default_after_variadic preferred?
22  */
23 void dbg(Args...)(scope Args args,
24                   const string file = __FILE_FULL_PATH__,
25                   const uint line = __LINE__,
26                   const string fun = __FUNCTION__) @trusted pure nothrow @nogc
27 {
28     import std.stdio : stderr, writeln;
29     try
30         debug stderr.writeln(file, "(", line, ",1):", " Debug: \"", args, "\"");
31     catch (Exception) { }
32 }
33 
34 ///
35 version(none)
36 @safe pure nothrow @nogc unittest
37 {
38     int x = 42;
39     dbg("x: ", x);
40     static assert(__traits(compiles, { dbg(); })); // ok for dln to discard function qualifiers
41 }
42 
43 import std.stdio;
44 
45 enum DumpFormat
46 {
47 	dmd,
48 	rust,
49 	// TODO: gcc,
50 }
51 
52 /** Debug dump arguments `args` to standard error (`stderr`).
53  *
54  * See_Also: https://forum.dlang.org/post/myxzyfgtcewixwbhvalp@forum.dlang.org
55  */
56 template dump(args...)
57 {
58 	enum fmt = DumpFormat.dmd;
59 	static if (fmt == DumpFormat.dmd)
60 		static immutable header = "%s(%s,1): Debug: [%s]: ";
61 	else static if (fmt == DumpFormat.rust)
62 		static immutable header = "[%s:%s (%s)] ";
63     import std.traits : isBuiltinType, isAggregateType, FieldNameTuple, isSomeString, isSomeChar;
64     private void dump(string file = __FILE__, uint line = __LINE__, string fun = __FUNCTION__)
65     {
66 		debug static foreach (arg; args)
67         {{
68 			alias Arg = typeof(arg);
69             static if (isBuiltinType!(Arg))
70             {
71 				static immutable isString = isSomeString!Arg;
72 				static immutable isChar = isSomeChar!Arg;
73 				static immutable wrap = isString ? `"` : isChar ? `'` : ``;
74 				stderr.writefln(header ~ "%s: %s%s%s [%s]",
75 								file, line, fun,
76 								__traits(identifier, arg),
77 								wrap, arg, wrap,
78 								Arg.stringof);
79             }
80             else static if (isAggregateType!(Arg))
81             {
82 				// TODO: alias this?
83 				stderr.writefln(header ~ "%s: { %s } [%s]",
84 								file, line, fun,
85 								__traits(identifier, arg),
86 								toDbgString(arg),
87 								Arg.stringof);
88             }
89 		}}
90     }
91     private string toDbgString(Arg)(Arg o)
92     {
93         string result;
94         import std.format;
95         static foreach (f; FieldNameTuple!(typeof(o)))
96         {
97 			{
98 				alias Member = typeof(__traits(getMember, o, f));
99 				static if (isBuiltinType!(Member))
100 					result ~= format("%s:%s [%s], ",
101 									 f, __traits(getMember, o, f),
102 									 Member.stringof); // TODO: avoid ~
103 				else static if (isAggregateType!(Member))
104 					result ~= format("%s = %s [%s], ",
105 									 f, toDbgString(__traits(getMember, o, f)),
106 									 Member.stringof); // TOOD: avoid ~
107 			}
108         }
109         return result[0..$-2];
110     }
111 }
112 
113 ///
114 version(none)
115 @safe pure unittest
116 {
117     struct Bar { auto c = 'c';}
118     struct Foo { int s = 2; bool b = false; Bar bar;}
119     class FooBar { int t; Foo f; }
120 
121     int i;
122     float f = 3.14;
123 	char c = 'D';
124     string s = "some string";
125     Foo foo;
126     Bar bar;
127 
128     dump!(i, c, f, s, foo, 1+3, foo, bar); // TODO: don’t print variable name for `1+3`
129 }