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 }