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