www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Convenient debug printing

reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
Rust just added a standard macro `dbg` for debug printing

https://blog.rust-lang.org/2019/01/17/Rust-1.32.0.html#the-dbg-macro

What's the closest match for this macro we can get with D?


I have put together a similar solution at

     
https://github.com/nordlow/phobos-next/blob/master/src/dbgio.d#L51

used as

     int x = 42;
     dbg("x: ", x);

printing

     dbgio.d:68: Info: x: 42

but it only works with a cumbersome writeln-syntax.


I also have

     
https://github.com/nordlow/phobos-next/blob/master/src/dbgio.d#L12

copied from Wilzbach's effort and used as

     int x = 42;
     int[] y = [42, 43];
     mixin dump!("x", "y");

printing

     dbgio.d:17: Info: x: 42, y: [42, 43]

but, of course, cumbersome in another syntactic way.


Is this as good as it gets in D with regards to syntactic 
sugarness?
Feb 01 2019
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2019-02-01 16:49, Per Nordlöw wrote:

 Is this as good as it gets in D with regards to syntactic sugarness?
No, not if you want it to print the variable name. Yet another case for AST macros. -- /Jacob Carlborg
Feb 01 2019
parent reply 12345swordy <alexanderheistermann gmail.com> writes:
On Friday, 1 February 2019 at 19:34:30 UTC, Jacob Carlborg wrote:
 On 2019-02-01 16:49, Per Nordlöw wrote:

 Is this as good as it gets in D with regards to syntactic 
 sugarness?
No, not if you want it to print the variable name. Yet another case for AST macros.
I am pretty sure that the universe will freeze-over, before Walter consider AST macros. -Alex
Feb 01 2019
next sibling parent reply Meta <jared771 gmail.com> writes:
On Friday, 1 February 2019 at 20:32:54 UTC, 12345swordy wrote:
 I am pretty sure that the universe will freeze-over, before 
 Walter consider AST macros.

 -Alex
Meaning no offense to Walter, his recent statements on the AST macros makes me think that he is mistaking what Jacob has proposed before (Lisp or Rust-style hygienic AST macros) for C-style text macros (which are, of course, supremely terrible). His main argument, about not wanting people to be able to invent their own syntax and encourage fragmentation within the language community, does not ring true. All you have to do is look at Rust to see that it is possible to support a limited form of AST macro without any adverse effects.
Feb 01 2019
next sibling parent reply JN <666total wp.pl> writes:
On Friday, 1 February 2019 at 21:40:29 UTC, Meta wrote:
 C-style text macros (which are, of course, supremely terrible).
What's the difference between C #define and stitching strings with ~ all over the place?
Feb 01 2019
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 1 February 2019 at 21:48:18 UTC, JN wrote:
 What's the difference between C #define and stitching strings 
 with ~ all over the place?
those ~ strings can only be stitched to other strings, not to the rest of the program. mixin operates as complete blocks (it inserts AST nodes, not code per se). of course you could say the same thing about ast macros lol
Feb 01 2019
prev sibling next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Friday, 1 February 2019 at 21:40:29 UTC, Meta wrote:
 [snip]

 Meaning no offense to Walter, his recent statements on the AST 
 macros makes me think that he is mistaking what Jacob has 
 proposed before (Lisp or Rust-style hygienic AST macros) for 
 C-style text macros (which are, of course, supremely terrible).

 His main argument, about not wanting people to be able to 
 invent their own syntax and encourage fragmentation within the 
 language community, does not ring true. All you have to do is 
 look at Rust to see that it is possible to support a limited 
 form of AST macro without any adverse effects.
In general, the rvalue DIP makes pretty clear that Walter and Andrei have a high bar for the quality of a DIP, particularly one that could have a big impact on the language. I don't think the old AST macro DIP is probably up to the standard that would be needed at this point regardless. It would need a re-work and definitely need to be written from the perspective of convincing W&A, which is a pretty high hurdle given their biases. Two of the interesting things about AST macros to me are 1) that it seems pretty straightforward to implement C++'s metaclass proposal using them, 2) not only could a lot of D's other features like foreach be written in terms of them but a lot of the attributes could too. This could enable a lot of customization, which could be good or bad...
Feb 01 2019
next sibling parent Olivier FAURE <couteaubleu gmail.com> writes:
On Friday, 1 February 2019 at 23:50:04 UTC, jmh530 wrote:
 In general, the rvalue DIP makes pretty clear that Walter and 
 Andrei have a high bar for the quality of a DIP, particularly 
 one that could have a big impact on the language. I don't think 
 the old AST macro DIP is probably up to the standard that would 
 be needed at this point regardless. It would need a re-work and 
 definitely need to be written from the perspective of 
 convincing W&A, which is a pretty high hurdle given their 
 biases.
While you're being more charitable than most people I've seen on the forum, I think this isn't a constructive approach to dealing with language maintainers. A lot of people on the forum are essentially treating the maintainers as living obstacles to be worked around, not people to work with, which: - Is kind of disrespectful in the first place. - Excludes them from the discussion, instead of trying to improve communication. - Asks the wrong question: "How can we convince them?" instead of "What is best for the language, and how does that overlap with their opinion?" I'm not saying they're always right, mind you. But to get back to AST macros, I think it would be more constructive to look at the positive aspects of macros, the intended use cases, examples from other languages where macros are useful, etc... and *also* look at their drawbacks, investigate the problems they can create, probably ask Walter and Andrei for an exhaustive list of objections, ideally examples of problematic code... Then, when it comes to writing a DIP, try to propose something that covers the intended use cases in an elegant way *without* hitting the drawbacks they're worried about. The DIP shouldn't be "That thing you already refused except this time it's going to convince you despite your biases!", because that's not an intellectually honest way to proceed. I don't think I'm saying anything crazy either. Most use cases for AST macros in other languages are already covered by D's template system. It might be possible to get the most popular use cases (eg smart assertions) by just giving templates more power.
Feb 02 2019
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2019-02-02 00:50, jmh530 wrote:
 It would need a re-work and definitely need to be written 
 from the perspective of convincing W&A, which is a pretty high hurdle 
 given their biases.
All of the old DIPs (on the wiki) are written in a completely different style. Back then the requirements to get a DIP passed was a lot less then it is now. Those DIP are not really written like a spec. On the other hand the D spec is not written like a spec either :).
 Two of the interesting things about AST macros to me are 1) that it 
 seems pretty straightforward to implement C++'s metaclass proposal using 
 them
Yes, I think so.
 2) not only could a lot of D's other features like foreach be 
 written in terms of them but a lot of the attributes could too. This 
 could enable a lot of customization, which could be good or bad...
Yes, exactly. -- /Jacob Carlborg
Feb 02 2019
prev sibling parent reply Neia Neutuladh <neia ikeran.org> writes:
On Fri, 01 Feb 2019 21:40:29 +0000, Meta wrote:
 His main argument, about not wanting people to be able to invent their
 own syntax and encourage fragmentation within the language community,
 does not ring true. All you have to do is look at Rust to see that it is
 possible to support a limited form of AST macro without any adverse
 effects.
From what I've read, Rust macros allow you to define new syntax: let fib = recurrence![ a[n]: u64 = 0, 1 ... a[n-1] + a[n-2] ]; credit: https://danielkeep.github.io/practical-intro-to-macros.html That "..." isn't a Rust language construct; it's defined by the macro (and only works within the macro's arguments, so you can't do a Pascal-in-C style `#define begin {`). like database libraries that take normal lambdas. They're sufficient for writing an assertion tool that can take an expression, evaluate it, and print out every part of it for analysis if something went wrong. An old DIP <https://wiki.dlang.org/DIP50> for AST macros in D suggests this strategy, along with implicit stringification of block statements belonging to a macro invocation and access to the context of the invocation. That probably shouldn't be necessary to do a lot of weird and wonderful things. But it's not going to happen.
Feb 01 2019
parent reply Dmitry <dmitry indiedev.ru> writes:
On Saturday, 2 February 2019 at 05:20:50 UTC, Neia Neutuladh 
wrote:
 An old DIP <https://wiki.dlang.org/DIP50> for AST macros in D
It looks interesting. Why didn't it go?
Feb 01 2019
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2019-02-02 08:24, Dmitry wrote:
 On Saturday, 2 February 2019 at 05:20:50 UTC, Neia Neutuladh wrote:
 An old DIP <https://wiki.dlang.org/DIP50> for AST macros in D
It looks interesting. Why didn't it go?
It was never formally put through a process. Not sure if we had a process back then for DIPs. Originally it was just me writing down what I though AST macros would be like in a single place, to avoid having to repeat long explanations on the forums. -- /Jacob Carlborg
Feb 02 2019
prev sibling parent reply Neia Neutuladh <neia ikeran.org> writes:
On Sat, 02 Feb 2019 07:24:45 +0000, Dmitry wrote:
 On Saturday, 2 February 2019 at 05:20:50 UTC, Neia Neutuladh wrote:
 An old DIP <https://wiki.dlang.org/DIP50> for AST macros in D
It looks interesting. Why didn't it go?
It looks incomplete. It doesn't even begin to explain what sort of object model the compiler should expose, and it has a block-to-string feature that it doesn't really talk about in any detail.
Feb 02 2019
parent Jacob Carlborg <doob me.com> writes:
On 2019-02-02 18:23, Neia Neutuladh wrote:

 It looks incomplete. It doesn't even begin to explain what sort of object
 model the compiler should expose, and it has a block-to-string feature
 that it doesn't really talk about in any detail.
It's far from complete. As I mentioned elsewhere, the DIPs on the wiki are from a different time with not as high standards. It was also never formally reviewed, it was more that I wrote down what I though AST macros should be like to avoiding having to repeat myself on the forums. -- /Jacob Carlborg
Feb 04 2019
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2019-02-01 21:32, 12345swordy wrote:

 I am pretty sure that the universe will freeze-over, before Walter 
 consider AST macros.
Yeah, I know :(. -- /Jacob Carlborg
Feb 02 2019
prev sibling next sibling parent reply Rubn <where is.this> writes:
On Friday, 1 February 2019 at 15:49:24 UTC, Per Nordlöw wrote:
 Rust just added a standard macro `dbg` for debug printing

 https://blog.rust-lang.org/2019/01/17/Rust-1.32.0.html#the-dbg-macro

 What's the closest match for this macro we can get with D?


 I have put together a similar solution at

     
 https://github.com/nordlow/phobos-next/blob/master/src/dbgio.d#L51

 used as

     int x = 42;
     dbg("x: ", x);

 printing

     dbgio.d:68: Info: x: 42

 but it only works with a cumbersome writeln-syntax.


 I also have

     
 https://github.com/nordlow/phobos-next/blob/master/src/dbgio.d#L12

 copied from Wilzbach's effort and used as

     int x = 42;
     int[] y = [42, 43];
     mixin dump!("x", "y");

 printing

     dbgio.d:17: Info: x: 42, y: [42, 43]

 but, of course, cumbersome in another syntactic way.


 Is this as good as it gets in D with regards to syntactic 
 sugarness?
import std.stdio; void dbg(alias a)(string file = __FILE__, int line = __LINE__) { writeln( file, "(", line, "): ", __traits(identifier, a), " = ", a ); } int value = 10; dbg!value; // filename.d(12): value = 10
Feb 01 2019
next sibling parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Saturday, 2 February 2019 at 00:06:58 UTC, Rubn wrote:
 void dbg(alias a)(string file = __FILE__, int line = __LINE__) {
 	writeln( file, "(", line, "): ", __traits(identifier, a), " = 
 ", a );
 }
Nice! I wasn't aware of `__traits(identifier, a)`. What role does it play here? Further, can we make this variadic with respect to `alias a`?
Feb 01 2019
next sibling parent Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Saturday, 2 February 2019 at 00:25:02 UTC, Per Nordlöw wrote:
 I wasn't aware of `__traits(identifier, a)`. What role does it 
 play here?
Ahh, of course, I returns the symbol name as a `string`.
Feb 01 2019
prev sibling parent ezneh <petitv.isat gmail.com> writes:
On Saturday, 2 February 2019 at 00:25:02 UTC, Per Nordlöw wrote:
 On Saturday, 2 February 2019 at 00:06:58 UTC, Rubn wrote:
 void dbg(alias a)(string file = __FILE__, int line = __LINE__) 
 {
 	writeln( file, "(", line, "): ", __traits(identifier, a), " = 
 ", a );
 }
Nice! I wasn't aware of `__traits(identifier, a)`. What role does it play here? Further, can we make this variadic with respect to `alias a`?
I got this working without using the alias a, but working with variadic: ``` template dbg(args...) { alias dbgImpl!(args).print dbg; } template dbgImpl(args...) { void print(string file = __FILE__, uint line = __LINE__, string fun = __FUNCTION__) { static foreach (int i, a; args) { debug stderr.writefln("[%s:%s (%s)] %s = %s", file, line, fun, __traits(identifier, args[i]), args[i]); } } } void main() { int i; float f = 3.14; string s = "some string"; dbg!(i, f, s); } ``` prints: [dbg.d:28 (dbg.main)] i = 0 [dbg.d:28 (dbg.main)] f = 3.14 [dbg.d:28 (dbg.main)] s = some string
Feb 01 2019
prev sibling parent reply Olivier FAURE <couteaubleu gmail.com> writes:
On Saturday, 2 February 2019 at 00:06:58 UTC, Rubn wrote:
 import std.stdio;

 void dbg(alias a)(string file = __FILE__, int line = __LINE__) {
 	writeln( file, "(", line, "): ", __traits(identifier, a), " = 
 ", a );
 }

 int value = 10;
 dbg!value;      // filename.d(12): value = 10
I did think it was weird that a similar result couldn't be produced using __FILE__ and __LINE__, given that even C++ has them. Look like something that could be added to std.stdio. Only problem: dbg!(2 + 1); // Prints "a = 3" (could be mitigated by naming the alias "_")
Feb 02 2019
parent Olivier FAURE <couteaubleu gmail.com> writes:
On Saturday, 2 February 2019 at 10:42:27 UTC, Olivier FAURE wrote:
 (could be mitigated by naming the alias "_")
Or adding a new "expressionString" trait applicable to aliases and variadic templates. A trait like that might be useful for smart assertions.
Feb 02 2019
prev sibling next sibling parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Friday, 1 February 2019 at 15:49:24 UTC, Per Nordlöw wrote:
 Rust just added a standard macro `dbg` for debug printing

 https://blog.rust-lang.org/2019/01/17/Rust-1.32.0.html#the-dbg-macro

 [...]
What's wrong with `debug log(<whatever>);` from `std.experimental.logger;`? Works like a charm in my project. The message is prepended with a time stamp and location in file/function/line.
Feb 02 2019
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2019-02-02 13:08, Patrick Schluter wrote:

 What's wrong with `debug log(<whatever>);` from `std.experimental.logger;`?
 
 Works like a charm in my project. The message is prepended with a time 
 stamp and location in file/function/line.
I think what's missing is the variable name not being automatically added to the message printed. -- /Jacob Carlborg
Feb 02 2019
prev sibling parent Gary Willoughby <dev nomad.uk.net> writes:
On Saturday, 2 February 2019 at 12:08:02 UTC, Patrick Schluter 
wrote:
 On Friday, 1 February 2019 at 15:49:24 UTC, Per Nordlöw wrote:
 Rust just added a standard macro `dbg` for debug printing

 https://blog.rust-lang.org/2019/01/17/Rust-1.32.0.html#the-dbg-macro

 [...]
What's wrong with `debug log(<whatever>);` from `std.experimental.logger;`? Works like a charm in my project. The message is prepended with a time stamp and location in file/function/line.
This! I think everyone here has forgotten about the debug keyword and compiler switch.
Feb 02 2019
prev sibling parent reply ezneh <petitv.isat gmail.com> writes:
On Friday, 1 February 2019 at 15:49:24 UTC, Per Nordlöw wrote:
 [...]
Alright, after quite some work I finally managed to do the following code printing also nested variable names from classes & structs. I am not sure it's quite the same as the Rust AST macro for that, but I guess it's still interesting: import std.stdio; template dbg(args...) { alias dbgImpl!(args).print dbg; } template dbgImpl(args...) { import std.traits; void print(string file = __FILE__, uint line = __LINE__, string fun = __FUNCTION__) { static foreach (_; args) { static if(isBuiltinType!(typeof(_))) debug stderr.writefln("[%s:%s (%s)] %s = %s", file, line, fun, __traits(identifier, _), _); static if(isAggregateType!(typeof(_))) { debug stderr.writefln("[%s:%s (%s)] %s = %s", file, line, fun, __traits(identifier, _), toDbgString(_)); } } } string toDbgString(Arg)(Arg o) { string dbgstr = "("; import std.format; static foreach(f; FieldNameTuple!(typeof(o))) { static if(isBuiltinType!(typeof(__traits(getMember, o, f)))) { dbgstr ~= format("%s:%s, ", f, __traits(getMember, o, f)); } static if(isAggregateType!(typeof(__traits(getMember, o, f)))) { dbgstr ~= format("%s = %s, ", f, toDbgString(__traits(getMember, o, f))); } } return dbgstr[0..$-2] ~ ")"; } } struct Foo{ int s = 2; bool b = false; Bar bar;} struct Bar{ auto c = 'c';} class FooBar{ int t; Foo f; } void main() { int i; float f = 3.14; string s = "some string"; Foo foo; Bar bar; FooBar fb = new FooBar; dbg!(i, f, s, foo, 1+3, foo, bar, fb); // prints: // [dbg.d:54 (dbg.main)] i = 0 // [dbg.d:54 (dbg.main)] f = 3.14 // [dbg.d:54 (dbg.main)] s = some string // [dbg.d:54 (dbg.main)] foo = (s:2, b:false, bar = (c:c)) // [dbg.d:54 (dbg.main)] _ = 4 // [dbg.d:54 (dbg.main)] foo = (s:2, b:false, bar = (c:c)) // [dbg.d:54 (dbg.main)] bar = (c:c) // [dbg.d:54 (dbg.main)] fb = (t:0, f = (s:2, b:false, bar = (c:c))) }
Feb 02 2019
next sibling parent Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Saturday, 2 February 2019 at 14:58:42 UTC, ezneh wrote:
 import std.stdio;

 template dbg(args...)
 {
 	alias dbgImpl!(args).print dbg;
 ...
Great! Close enough for me! Thanks!
Feb 03 2019
prev sibling parent =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2019-02-02 14:58:42 +0000, ezneh said:

 On Friday, 1 February 2019 at 15:49:24 UTC, Per Nordlöw wrote:
 [...]
Alright, after quite some work I finally managed to do the following code printing also nested variable names from classes & structs. I am not sure it's quite the same as the Rust AST macro for that, but I guess it's still interesting: ...
This looks great, but I get: Error: value of this is not known at compile time for the static foreach() line in dbgImpl. So, wondering what I'm doing wrong... -- Robert M. Münch http://www.saphirion.com smarter | better | faster
Nov 17 2019