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