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 }