Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Nervously....How do I access the Name of a Perl variable?

by Anonymous Monk
on Jun 11, 2002 at 01:09 UTC ( [id://173358]=perlquestion: print w/replies, xml ) Need Help??

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

After reading this thread println() I am a little nervous about asking this but:

In C, I use a macro TRACE() (who's definition is pretty standard) that when passed a variable prints a single line giving the variables name and its current value (plus __FILE__ and __LINE__ info).

What I would like is something that when called like this:  trace( $fred, $bill );

would result in:

/path/to/test.pl (11): $fred:'12' /path/to/test.pl (11): $bill:' The almighty greenback '

I guess there are two main problems here:

1) Obtaining the text for the variable name - though I am fully expecting the answer "no problem",but I haven't found it yet.

2) Having the line number available for output within the subroutine without having to manually supply it each time. Given this is an interpreter, this ought to be possible. Again, I am not quite sure where to start looking for this? Thanks.

How would this be done in Perl?


The worst part of not knowing, is not knowing that you do not know what you do not know!

Replies are listed 'Best First'.
Re: Nervously....How do I access the Name of a Perl variable?
by wog (Curate) on Jun 11, 2002 at 01:35 UTC

    Getting the line number and filename is relatively easy:

    sub trace { my($package, $filename, $line) = caller; # ... }

    You can also get the subroutine its in. See the docs for caller.

    Getting the names of the relevant variables is much harder, because perl does not such a convinent mechanism for compile-time expanded macros as C. However there are things called source filters, and the Filter::Simple module provides a nice interface to them. So, we can use that to make a module that adds a TRACE() macro:

    package TraceMacro; use Filter::Simple; FILTER_ONLY code => sub { s/TRACE\(\s*([^)]+)\s*\)/__PACKAGE__."::_trace(q($1),$1)"/eg; }; # replace each TRACE(...) with <this package>::_trace(q(...),...) sub _trace { my($names,@values) = @_; my @names = split /\s*,\s*/, $names; # extract varnames my($package,$file,$line) = caller; foreach my $i(0..$#names) { print STDERR "$file ($line): $names[$i]:'$values[$i]'"; } }

    (The module just needs to be saved appropriately to a file called by the name of Package.pm where Package is whatever's specified by the package declaration. (You could change that.))

    This has the side-effect of allowing arbitrary expressions instead of just variable names.

    update: Usage would be like:

    use TraceMacro; # ... TRACE( $foo, $bar );
    If you want to change TRACE to trace (I prefer that compile-time-special things be uppercased), then it shouldn't be hard to fiquire that out.

      Outstanding! Everything I wanted and an object lesson in creating my first package, which I didnt really think I was up for yet but this looks simple enough that even I can do it! Thankyou. Anyone with a spare vote handy, give it to this man.

      Of course, it will start out as cut&paste for now, but you gave me enough hints for possibilities that I can add (like sub name) to encourage me to investigate the references.

      To other responders:

      I am aware of the debugger and use it if I get something that needs it, but while im just cranking code out, sticking a TRACE in various places and running the prog is a nice quick way of checking things are doing what I expect or not.

      Sorry for the second post here but reading the code I thought of one more feature that may be possible.and will try to work out for myself, but I am unsure of the side effects.

      If a SINGLE value is passed, how hard would it be to have it eval the passed expression and then return that from _trace()?

      That way, the TRACE macro could be used in-situ in the code, and by a suitable test of some flag (possibly the systems debug settings) either Trace or not trace and the program would still fuction (possibly inefficiently).

      Does this thought have any merit?

Re: Nervously....How do I access the Name of a Perl variable?
by chromatic (Archbishop) on Jun 11, 2002 at 01:33 UTC
    I'm not aware of any pure Perl way of doing #1. Sorry. For #2, see caller() or __LINE__.
      Well, perl does have the -p option which will throw your Perl program through the C preprocessor....

      You have to be careful though, as the # character also starts a Perl comment.

      Abigail


        Here is a simple example using -P. The program should be run as follows perl -P program.pl and it requires a pre-processor on the target system:
        #!/usr/bin/perl -w use strict; #define TRACE(x) print "$0 line ". __LINE__ .":\t". x ." = ". eval +(x). "\n"; my $foo = 7; TRACE('$foo'); $foo *= $foo; TRACE('$foo'); __END__ Prints: /tmp/test.pl line 8: $foo = 7 /tmp/test.pl line 11: $foo = 49

        --
        John.

Re: Nervously....How do I access the Name of a Perl variable?
by Zaxo (Archbishop) on Jun 11, 2002 at 03:06 UTC

    The TRACE() macro is very like warn $fred, $bill, $!;, except that printing the names of the variables doesn't happen. Package globals do have names that are kept in the symbol table, but not lexicals. You can fiddle with symrefs if you must have the names, but that is a bad habit. Better to just give them explicitly as string arguments to warn

    $ perl -e 'my $foo="bar"; warn q/$foo is /, $foo, $!' $foo is bar at -e line 1.
    The __FILE__ and __LINE__ handling is done by $! warn.

    I've been playing with the -D flag to perl, which requires a debugging build. You can get lots of very explicit info about a program that way, but maybe more than you are asking for.

    After Compline,
    Zaxo

Re: Nervously....How do I access the Name of a Perl variable?
by talexb (Chancellor) on Jun 11, 2002 at 02:39 UTC
    It's possible to write something tremendously clever that does it all in a subroutine, but if you're debugging like that, just create a line like
    print __FILE__ . " (" . __LINE__ . "): " . "(your variable names and values here)\n";
    and copy it willy nilly, wherever needed. When you're tired of it, just comment it out .. you may end up needing it again.

    This presumes that you can't use the Perl debugger to track down your problem. The debugger is a little intimidating, but it's a little cleaner and neater (not to mention way more flexible) to use when tracking down problems.

    --t. alex

    "Nyahhh (munch, munch) What's up, Doc?" --Bugs Bunny

    Update Abigail's suggestion is much cooler -- it gets Perl to take care of the file name and line numbering detail.

    It's really a preference between commenting out code that's no longer needed and just disabling it with the $debug flag. Your choice.

      Or you would just use:
      warn "Your variable names and values here" if $debug;
      and Perl will supply the file name and line number for you.

      By toggling the $debug variable, you can control whether the program prints debugging messages or not.

      Abigail

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://173358]
Approved by ehdonhon
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (2)
As of 2024-04-20 04:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found