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__, in uint line = __LINE__) { 26 import nxt.stdio : pwriteln; 27 debug pwriteln(Format.debugging, file, "(", line, "):", " Debug: ", args, ""); 28 debug _fflush(); // most often what we want before a potentially crash happens 29 } 30 31 void pdbg(Args...)(in Format fmt, scope auto ref Args args, in string file = __FILE_FULL_PATH__, in uint line = __LINE__) { 32 import nxt.stdio : pwriteln; 33 debug pwriteln(fmt, file, "(", line, "):", " Debug: ", args, ""); 34 debug _fflush(); // most often what we want before a potentially crash happens 35 } 36 37 void dbgf(Args...)(scope Args args, const string file = __FILE_FULL_PATH__, in uint line = __LINE__, const string fun = __FUNCTION__) { 38 import nxt.stdio : pwriteln; 39 debug pwriteln(Format.debugging, file, "(", line, "): ", fun, ": Debug: ", args, ""); 40 debug _fflush(); // most often what we want before a potentially crash happens 41 } 42 43 private void _fflush() @trusted { 44 import core.stdc.stdio : stdout, fflush; 45 debug fflush(stdout); 46 } 47 48 /// 49 pure nothrow @safe @nogc unittest { 50 static assert(__traits(compiles, { dbg(); })); // ok for dln to discard function qualifiers 51 } 52 53 enum DumpFormat { 54 dmd, 55 rust, 56 /+ TODO: gcc, +/ 57 } 58 59 /** Debug dump arguments `args` to standard error (`stderr`). 60 * 61 * See_Also: https://forum.dlang.org/post/myxzyfgtcewixwbhvalp@forum.dlang.org 62 */ 63 template dump(args...) { 64 enum fmt = DumpFormat.dmd; 65 static if (fmt == DumpFormat.dmd) 66 static immutable header = "%s(%s,1): Debug: [%s]: "; 67 else static if (fmt == DumpFormat.rust) 68 static immutable header = "[%s:%s (%s)] "; 69 import std.traits : isBuiltinType, isAggregateType, FieldNameTuple, isSomeString, isSomeChar; 70 private void dump(string file = __FILE__, uint line = __LINE__, string fun = __FUNCTION__) { 71 debug static foreach (arg; args) {{ 72 alias Arg = typeof(arg); 73 static if (isBuiltinType!(Arg)) { 74 static immutable isString = isSomeString!Arg; 75 static immutable isChar = isSomeChar!Arg; 76 static immutable wrap = isString ? `"` : isChar ? `'` : ``; 77 stderr.writefln(header ~ "%s: %s%s%s [%s]", 78 file, line, fun, 79 __traits(identifier, arg), 80 wrap, arg, wrap, 81 Arg.stringof); 82 } 83 else static if (isAggregateType!(Arg)) { 84 /+ TODO: alias this? +/ 85 stderr.writefln(header ~ "%s: { %s } [%s]", 86 file, line, fun, 87 __traits(identifier, arg), 88 toDbgString(arg), 89 Arg.stringof); 90 } 91 }} 92 } 93 private string toDbgString(Arg)(Arg o) { 94 string result; 95 import std.format; 96 static foreach (f; FieldNameTuple!(typeof(o))) { 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 pure @safe unittest { 116 struct Bar { auto c = 'c';} 117 struct Foo { int s = 2; bool b = false; Bar bar;} 118 class FooBar { int t; Foo f; } 119 120 int i; 121 float f = 3.14; 122 char c = 'D'; 123 string s = "some string"; 124 Foo foo; 125 Bar bar; 126 127 dump!(i, c, f, s, foo, 1+3, foo, bar); /+ TODO: don’t print variable name for `1+3` +/ 128 }