1 /** Various debug printing tools for debug printing in `@safe pure nothrow @nogc` code.
2  *
3  * Copyright: Per Nordlöw 2018-.
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 // version = show;
12 
13 @safe pure:
14 
15 /** Debug print `Names` all being compile-time strings that are mixed in.
16  *
17  * Similar to Rust's `dbg` macro introduced in version 1.32.
18  *
19  * See_Also: https://blog.rust-lang.org/2019/01/17/Rust-1.32.0.html#the-dbg-macro
20  * See_Also: https://forum.dlang.org/post/svjjawiezudnugdyriig@forum.dlang.org
21  */
22 mixin template dumpObseleted(Names...)
23 {
24     auto dumpObseleted =
25     {
26         import std.stdio : stderr, write;
27         debug write(__FILE__, ":", __LINE__, ": Info: ");
28         foreach (immutable i, name; Names)
29         {
30             debug write(typeof(name).stringof, " ", name, ": ", mixin(name), (i < Names.length-1) ? ", " : "\n");
31         }
32         return false;
33     }();
34 }
35 
36 ///
37 @safe pure unittest
38 {
39     const int x = 42;
40     int[2] y = [42, 43];
41     mixin dumpObseleted!("x", "y");
42 }
43 
44 /** Debug print `args` followed by a newline.
45  *
46  * Similar to Rust's `dbg` macro introduced in version 1.32.
47  *
48  * See_Also: https://blog.rust-lang.org/2019/01/17/Rust-1.32.0.html#the-dbg-macro
49  * See_Also: https://forum.dlang.org/post/svjjawiezudnugdyriig@forum.dlang.org
50  */
51 void dbg(string file = __FILE__,
52          uint line = __LINE__,
53          string fun = __FUNCTION__,
54          Args...)(Args args) @safe pure nothrow @nogc
55 {
56     try
57     {
58         import std.stdio : stderr, writeln;
59         debug stderr.writeln(file, ":", line, ":", " Info: ", args);
60     }
61     catch (Exception) { }
62 }
63 
64 ///
65 @safe pure nothrow @nogc unittest
66 {
67     // int x = 42;
68     // dbg("x: ", x);
69     static assert(__traits(compiles, { dbg(); })); // ok for dln to discard function qualifiers
70 }
71 
72 /** Show the symbol name and variable of $(D Args).
73  *
74  * See_Also: http://forum.dlang.org/thread/yczwqrbkxdiqijtiynrh@forum.dlang.org?page=1
75  *
76  * TODO use https://forum.dlang.org/post/ypxsqtddxvdxunsoluas@forum.dlang.org
77  *
78  * TODO is using this https://dlang.org/changelog/2.079.0.html#default_after_variadic preferred?
79  *
80  * TODO instead use
81  *
82  * void show_(Args...)(Args args,
83  * string file = __FILE__,
84  * uint line = __LINE__,
85  * string fun = __FUNCTION__)
86  *
87  */
88 template show(Args...)
89 if (Args.length >= 1)
90 {
91     void show(string file = __FILE__,
92               uint line = __LINE__,
93               string fun = __FUNCTION__)
94     {
95         import std.stdio: write, writeln;
96         try
97         {
98             debug write(file, ":",line, ":" /* , ": in ",fun */, " debug: ");
99             foreach (const i, Arg; Args)
100             {
101                 if (i) debug write(", "); // separator
102                 debug write(Args[i].stringof, ":", Arg);
103             }
104             debug writeln();
105         }
106         catch (Exception) { }
107     }
108 }
109 
110 version(show)
111 @safe pure unittest
112 {
113     const x = 11;
114     const y = 12;
115     const z = 13;
116     show!x;
117     show!y;
118     show!z;
119 }
120 
121 version(show) unittest
122 {
123     const x = 11, y = 12, z = 13;
124     show!(x, y, z);
125 }
126 
127 import std.stdio;
128 
129 /** Debug dump arguments `args` to standard error output (`stderr`).
130  *
131  * See_Also: https://forum.dlang.org/post/myxzyfgtcewixwbhvalp@forum.dlang.org
132  */
133 template dump(args...)
134 {
135     alias dbgImpl!(args).print dump;
136 }
137 
138 template dbgImpl(args...)
139 {
140     import std.traits : isBuiltinType, isAggregateType, FieldNameTuple;
141 
142     private void print(string file = __FILE__, uint line = __LINE__, string fun = __FUNCTION__)
143     {
144         static foreach (arg; args)
145         {
146             static if (isBuiltinType!(typeof(arg)))
147             {
148                 debug stderr.writefln("[%s:%s (%s)] %s = %s",
149                                       file, line, fun,
150                                       __traits(identifier, arg), arg);
151             }
152             else static if (isAggregateType!(typeof(arg)))
153             {
154                 debug stderr.writefln("[%s:%s (%s)] %s = %s",
155                                       file, line, fun,
156                                       __traits(identifier, arg),
157                                       toDbgString(arg));
158             }
159         }
160     }
161 
162     private string toDbgString(Arg)(Arg o)
163     {
164         string dbgstr = "(";
165         import std.format;
166         static foreach(f; FieldNameTuple!(typeof(o)))
167         {
168             static if (isBuiltinType!(typeof(__traits(getMember, o, f))))
169             {
170                 dbgstr ~= format("%s:%s, ", f, __traits(getMember, o, f));
171             }
172             else static if (isAggregateType!(typeof(__traits(getMember, o, f))))
173             {
174                 dbgstr ~= format("%s = %s, ", f, toDbgString(__traits(getMember, o, f)));
175             }
176         }
177         return dbgstr[0..$-2] ~ ")";
178     }
179 }
180 
181 ///
182 @safe pure unittest
183 {
184     struct Bar { auto c = 'c';}
185     struct Foo { int s = 2; bool b = false; Bar bar;}
186     class FooBar { int t; Foo f; }
187 
188     int i;
189     float f = 3.14;
190     string s = "some string";
191     Foo foo;
192     Bar bar;
193 
194     dump!(i, f, s, foo, 1+3, foo, bar);
195 
196     // prints:
197     // [dump.d:54 (dump.main)] i = 0
198     // [dump.d:54 (dump.main)] f = 3.14
199     // [dump.d:54 (dump.main)] s = some string
200     // [dump.d:54 (dump.main)] foo = (s:2, b:false, bar = (c:c))
201     // [dump.d:54 (dump.main)] _ = 4
202     // [dump.d:54 (dump.main)] foo = (s:2, b:false, bar = (c:c))
203     // [dump.d:54 (dump.main)] bar = (c:c)
204     // [dump.d:54 (dump.main)] fb = (t:0, f = (s:2, b:false, bar = (c:c)))
205 }