ChrisBeall has asked for the wisdom of the Perl Monks concerning the following question:

Hi - I'm new to Perl.
In C, I had a nice simple macro to print a trace message with the script name, variable name, variable contents, and line number:

#define DEBUG(fmt,expr) \ fprintf(stderr, "DEBUG: " __FILE__ ":%d - " #expr " = [" #fmt "]\n", _ +_LINE__, expr);

Example: debug statement in my_script.c, line 32

10: char szMyString[] = "ContentsOfMyString"; 31: ... 32: DEBUG(%s, szMyString); 33: ... 34: ... ... output => DEBUG: my_script.c:32: - szMyString = [ContentsOfMyString]

What I really like about this is (a) it automatically prints the variable name "szMyString" (via the "#expr" value), and (b) the line number. But in Perl, I find myself always re-typing the variable name (Dumper tends to just print "$VAR1" etc):

print "\$mystring: [", $mystring, "]\n";

Any suggestions on doing something similar in Perl, which automatically prints the variable name, would be greatly appreciated. Thanks a lot.

Replies are listed 'Best First'.
Re: How to get variable name in trace message
by tobyink (Canon) on Jun 15, 2012 at 06:05 UTC

    Perl doesn't make this sort of thing especially easy. Devel::Assert kinda pulls off this trick...

    use Devel::Assert -all; my $x = 2; assert($x > 3);

    The above will die with the message: Assertion ' $x > 3 ' failed at assert.pl line 5.

    How does Devel::Assert do it? With a bit of Devel::Declare magic. Devel::Declare hooks into the Perl compiler, rewriting code before it gets compiled. Using Devel::Declare you could define your own Dumper function which rewrites this:

    Dumper($foo)

    to this:

    Data::Dumper->Dump([$foo], [qw[$foo]])

    Not impossible, but probably more effort than it's worth.

    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'

      While I still stand by the "more effort than it's worth" comment, I've been meaning to get more stuck into Devel::Declare lately, so I had a go, and this is what I came up with:

      Data::Dumper::Declare

      It gives you a Dumper function just like Data::Dumper (in fact, it uses Data::Dumper internally, so respects things like $Data::Dumper::Sortkeys), but figures out the variable names being dumped. So you can do this:

      use Data::Dumper::Declare; my $foo = 1; my $bar = 'Hello World'; my @baz = qw(1 2 3); print Dumper($foo, substr($bar, 0, 5), @baz);

      ... and you get:

      $foo = 1;
      $EXPR = 'Hello';
      \@baz = [
        1,
        2,
        3
      ];
      

      (Note that unlike Data::Dumper, you can't just eval the output. An assignment like \@bar = [...] is illegal.)

      Data::Dumper::Declare isn't on CPAN yet. Do people think it useful enough to upload?

      Update: just pushed a change which allows arrays and hashes to be dumped more nicely. e.g.

      @baz = (
        1,
        2,
        3
      );
      

      Update 2: Data::Dumper::Declare is now on CPAN.

      Update 3: Given this:

      use Data::Dumper::Declare; my @foo = qw(Hello World); print Dumper( @foo, $foo[0], $foo[1], join('::', @foo), );

      The output is:

      @foo = (
        'Hello',
        'World'
      );
      $foo[0] = 'Hello';
      $foo[1] = 'World';
      $EXPR = 'Hello::World';
      

      Can anyone suggest something better than $EXPR in the output? It's certainly possible to output join('::', @foo) = 'Hello::World'; but I'm not especially fond of that. What do other people think?

      perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
        Data::Dumper::Declare isn't on CPAN yet. Do people think it useful enough to upload?
        Definitely! This is something I always wanted to have!!!

        -- 
        Ronald Fischer <ynnor@mm.st>

        There is no variable name associated with the expression, so undef could work, and it is ok to assign to undef.

        Quoting the expression might also be a nice option if possible.

        $foo = 1; undef = 'StringResultOfExpression'; $str . result('expression') = 'StringResultOfExpression'; @bar = [ 1, 2, 3, ];
        That's great - you've put a lot of work into it. Personally, I do like the explicit join('::', @foo) in the output.
      You guys are amazing! Thanks for such a thoughtful response. I'll have a play around with Devel::Declare.
Re: How to get variable name in trace message
by tinita (Parson) on Jun 15, 2012 at 09:09 UTC
    just as an addition: I've been using a vim mapping for a very long time now.
    imap DUMPER <ESC>^iwarn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\<ESC>llyw$a], ['<ESC>pa']);<ESC>

    type $varnameDUMPER or %varnameDUMPER or @varnameDUMPER. result:
    warn __PACKAGE__.':'.__LINE__.$".Data::Dumper->Dump([\$varname], ['varname']);

    ok, the resulting perl code doesn't look nice, but I render it grey with this code:
    2match comments /^ *warn __PACKAGE__.*/ highlight comments ctermfg=darkgrey
      Very nifty! I love Vim, so I'll give this a go too. Thanks.
Re: How to get variable name in trace message
by stevieb (Canon) on Jun 15, 2012 at 05:45 UTC

    Does caller() appeal to you in this case?

    ps. I don't know much about C, so this is just a guess.

    Update: caller() won't print out the var & contents inherently, but it does of course produce a stack trace.

      Hi stevieb - caller() is very impressive, but, as you say, it seems to relate more to doing a stack trace about function calls etc. I'm after a simple function (or "macro" equivalent) to just plonk out trace values of the form "<line_no>: <var_name> = <var_value>", using a command like "DEBUG(<var_name>)". I guess I could use the debugger, but this would be simpler for little tasks. The trick is I can't see how to find the <var_name> itself.

        I regretted posting originally, because I spoke before I fully comprehended what you were after.

        I've written such tools before, but they aren't worthy of public consumption. Hopefully someone will be able to finger a CPAN module that will get you on your way.

        Cheers,

        Steve