Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

is it possible to access eval() counter?

by Anonymous Monk
on Jun 29, 2006 at 10:41 UTC ( [id://558280]=perlquestion: print w/replies, xml ) Need Help??

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

String eval has some sort of counter associated with it:
C:\>perl -le "eval q!die!;print $@"
output:
Died at (eval 1) line 1.
another example:
C:\>perl -le "eval q!print $^O! for 1..10; eval q!die!;print $@"
output:
MSWin32 MSWin32 MSWin32 MSWin32 MSWin32 MSWin32 MSWin32 MSWin32 MSWin32 MSWin32 Died at (eval 11) line 1.
The question is, can I access that counter somehow? For example, I may do some evals myself, but some other code can also do that, so I can't use a simple $counter++ before eval()ing.

Replies are listed 'Best First'.
Re: is it possible to access eval() counter?
by Corion (Patriarch) on Jun 29, 2006 at 11:54 UTC

    There is no way I know of to (re)set the eval counter, but the usual trick I use to associate evals with the evalled code is to embed a #line comment in your eval() string:

    perl -le "eval q!print $^O! for 1..10; eval qq!#line 99 foo.cfg\ndie!; +print $@"
    gives
    MSWin32 MSWin32 MSWin32 MSWin32 MSWin32 MSWin32 MSWin32 MSWin32 MSWin32 MSWin32 Died at foo.cfg line 99.

      Note that in older versions of Perl you'll need a newline in front of the #line directive.

      Note also that you can make the line number and file name actually match where the eval'd code is. For example, Algorithm::Loops' t/assert.t evals a bunch of code after __END__ but makes any fatal errors report the correct line number via:

      # ... my $lineNum; sub SetLineNum { $lineNum= 1+(caller(0))[2]; } while( <DATA> ) { $lineNum++; # ... eval "\n#line $lineNum $0\n$_\n; 1" or die $@; # ... } # ... BEGIN { SetLineNum } __END__ # ...

      More typical tricks just use __LINE__ (plus a constant) and __FILE__:

      my $file= '"' . __FILE__ . '"'; eval join " ", "\n#line", 1+__LINE__, $file, "\n", <<END, "; 1" or die + $@; ... END

      Though, you might want to do a bit extra work if you are on an operating system that allows obnoxious things like file names that contain double quotes.

      - tye        

Re: is it possible to access eval() counter?
by Sidhekin (Priest) on Jun 29, 2006 at 10:48 UTC

    Well ... if you really need to know the number of the immediately following eval (why?) ... this will do it:

    $_++ for my ($counter) = do {eval 'die'; $@ =~ /eval (\d+)/};

    Edit: It will do it, but the style is poor ... try this:

    my $counter = do {eval "die"; $@ =~ /eval (\d+)/; $1+1};

    print "Just another Perl ${\(trickster and hacker)},"
    The Sidhekin proves Sidhe did it!

Re: is it possible to access eval() counter?
by radiantmatrix (Parson) on Jun 29, 2006 at 16:31 UTC

    I feel your pain. "Died at (eval 11) line 1" is not particularly useful for debugging, and can easily wind up prompting a trip to 'perl -d'. There are two workarounds I've used:

    eval q!die!; if ($@) { die $@ }

    Or:

    use strict; use warnings; sub caught () { # caught ( ) my $cl = 1; my ($pack, $file, $sub); # occasionally $sub will end up undef; this is OK, but we don't wa +nt a # warning about it. So, we're lexically disabling it. no warnings 'uninitialized'; do { ($pack,$file, undef, $sub) = caller($cl++); } until ($sub ne '(eval)'); $cl--; (my $exc = $@) =~ s/(.*) at .*$/$1/s; #trim of 'at file line ##' m +sg. $exc=~ s/[\r\n]/\x1F/gs; #replace line endings. return 'Caught exception ['.$exc.'] ' .($cl>0 ? "$cl evals deep " : '').'in ' .($sub ? $sub : 'main'); } eval q!die "This is a test"!; die caught if ($@);

    This provides the very helpful "Caught exception [This is a test] 1 evals deep in main at D:\test.pl line 26.". This tells you the *depth* of the eval, rather than which eval, and gives you the line context nicely (and even tells you what subroutine you were ultimately in).

    Other than for context, what would you use this for?

    <radiant.matrix>
    A collection of thoughts and links from the minds of geeks
    The Code that can be seen is not the true Code
    I haven't found a problem yet that can't be solved by a well-placed trebuchet
Re: is it possible to access eval() counter?
by hv (Prior) on Jun 30, 2006 at 11:48 UTC

    Here is a simpler approach, if you can use Inline::C:

    #!/usr/bin/perl -w use strict; use Inline C => q{ int evalseq() { return PL_evalseq; }}; for (1 .. 10) { eval q{ print "in eval (", evalseq(), ")\n" }; print "outside eval (", evalseq(), ")\n"; }

    Of course this delves into undocumented internals, so no guarantee that it will work with every past and future version of perl. (It may also need a pTHX or something for threaded builds, but I don't have one handy to check.)

    Hugo

      Thanks hv :) it works :)
A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (5)
As of 2024-04-24 09:21 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found