Is there a way to reliably get the current line number during a Perl
multiline list assignment without explicitly using __LINE__
? I am
storing testcases in a list and would like to tag each with its line
number.* That way I can do (roughly)
ok($_->[1], 'line ' . $_->[0]) for @tests
.
And, of course, I would like to save typing compared to
putting __LINE__
at the beginning of each test case :) . I have
not been able to find a way to do so, and I have encountered some
confusing behaviour in the lines reported by caller
.
* Possible XY, but I can't find a module to do it.
Update I found a hack and posted it as an answer. Thanks to @zdim for helping me think about the problem a different way!
MCVE
A long one, because I've tried several different options. my_eval
,
L()
, and L2{}
are some I've tried so far — L()
was the one
I initially hoped would work. Jump down to my @testcases
to see how
I'm using these. When testing, do copy the shebang line.
Here's my non-MCVE use case, if you are interested.
#!perl
use strict; use warnings; use 5.010;
# Modified from https://www.effectiveperlprogramming.com/2011/06/set-the-line-number-and-filename-of-string-evals/#comment-155 by http://sites.google.com/site/shawnhcorey/
sub my_eval {
my ( $expr ) = @_;
my ( undef, $file, $line ) = caller;
my $code = "# line $line \"$file\"\n" . $expr;
unless(defined wantarray) {
eval $code; die $@ if $@;
} elsif(wantarray) {
my @retval = eval $code; die $@ if $@; return @retval;
} else {
my $retval = eval $code; die $@ if $@; return $retval;
}
}
sub L { # Prepend caller's line number
my (undef, undef, $line) = caller;
return ["$line", @_];
} #L
sub L2(&) { # Prepend caller's line number
my $fn = shift;
my (undef, undef, $line) = caller;
return ["$line", &$fn];
} #L2
# List of [line number, item index, expected line number, type]
my @testcases = (
([__LINE__,0,32,'LINE']),
([__LINE__,1,33,'LINE']),
(L(2,34,'L()')),
(L(3,35,'L()')),
(do { L(4,36,'do {L}') }),
(do { L(5,37,'do {L}') }),
(eval { L(6,38,'eval {L}') }),
(eval { L(7,39,'eval {L}') }),
(eval "L(8,40,'eval L')"),
(eval "L(9,41,'eval L')"),
(my_eval("L(10,42,'my_eval L')")),
(my_eval("L(11,43,'my_eval L')")),
(L2{12,44,'L2{}'}),
(L2{13,45,'L2{}'}),
);
foreach my $idx (0..$#testcases) {
printf "%2d %-10s line %2d expected %2d %s\n",
$idx, $testcases[$idx]->[3], $testcases[$idx]->[0],
$testcases[$idx]->[2],
($testcases[$idx]->[0] != $testcases[$idx]->[2]) && '*';
}
Output
With my comments added.
0 LINE line 32 expected 32
1 LINE line 33 expected 33
Using __LINE__
expressly works fine, but I'm looking for an
abbreviation.
2 L() line 45 expected 34 *
3 L() line 45 expected 35 *
L()
uses caller
to get the line number, and reports a line later
in the file (!).
4 do {L} line 36 expected 36
5 do {L} line 45 expected 37 *
When I wrap the L()
call in a do{}
, caller
returns the correct
line number — but only once (!).
6 eval {L} line 38 expected 38
7 eval {L} line 39 expected 39
Block eval
, interestingly, works fine. However, it's no shorter
than __LINE__
.
8 eval L line 1 expected 40 *
9 eval L line 1 expected 41 *
String eval
gives the line number inside the eval
(no surprise)
10 my_eval L line 45 expected 42 *
11 my_eval L line 45 expected 43 *
my_eval()
is a string eval
plus a #line
directive based on
caller
. It also gives a line number later in the file (!).
12 L2{} line 45 expected 44 *
13 L2{} line 45 expected 45
L2
is the same as L
, but it takes a block that returns a list,
rather than
the list itself. It also uses caller
for the line number. And it
is correct once, but not twice (!). (Possibly just because it's the last
item — my_eval
reported line 45 also.)
So, what is going on here? I have heard of Deparse and wonder if this is
optimization-related, but I don't know enough about the engine to know
where to start investigating. I also imagine this could be done with source
filters or Devel::Declare
, but that is well beyond my
level of experience.
@zdim's answer got me started thinking about fluent interfaces, e.g., as in my answer:
$testcases2 # line 26
->add(__LINE__,0,27,'LINE')
->add(__LINE__,1,28,'LINE')
->L(2,29,'L()')
->L(3,30,'L()')
->L(3,31,'L()')
;
However, even those don't work here — I get line 26 for each of the ->L()
calls. So it appears that caller
sees all of the chained calls as coming from the $testcases2->...
line. Oh well. I'm still interested in knowing why, if anyone can enlighten me!
Edit This answer is now wrapped in a CPAN module (GitHub)!
@zdim's answer got me thinking about fluent interfaces. Below are two hacks that work for my particular use case, but that don't help me understand the behaviour reported in the question. If you can help, please post another answer!
I think this one is very close to minimal. In perl, you can call a subroutine through a reference with $ref->()
, and you can leave out the second and subsequent ->
in a chain of arrows. That means, for example, that you can do:
my $foo; $foo=sub { say shift; return $foo; };
$foo->(1)
(2)
(3);
Looks good, right? So here's the MCVE:
#!perl
use strict; use warnings; use 5.010;
package FluentAutoIncList2 {
sub new { # call as $class->new(__LINE__); each element is one line
my $class = shift;
my $self = bless {lnum => shift // 0, arr => []}, $class;
# Make a loader that adds an item and returns itself --- not $self
$self->{loader} = sub { $self->L(@_); return $self->{loader} };
return $self;
}
sub size { return scalar @{ shift->{arr} }; }
sub last { return shift->size-1; } # $#
sub load { goto &{ shift->{loader} } } # kick off loading
sub L { # Push a new record with the next line number on the front
my $self = shift;
push @{ $self->{arr} }, [++$self->{lnum}, @_];
return $self;
} #L
sub add { # just add it
my $self = shift;
++$self->{lnum}; # keep it consistent
push @{ $self->{arr} }, [@_];
return $self;
} #add
} #FluentAutoIncList2
# List of [line number, item index, expected line number, type]
my $testcases = FluentAutoIncList2->new(__LINE__) # line 28
->add(__LINE__,0,36,'LINE')
->add(__LINE__,1,37,'LINE');
$testcases->load(2,38,'load')-> # <== Only need two arrows.
(3,39,'chain load') # <== After that, () are enough.
(4,40,'chain load')
(5,41,'chain load')
(6,42,'chain load')
(7,43,'chain load')
;
foreach my $idx (0..$testcases->last) {
printf "%2d %-10s line %2d expected %2d %s\n",
$idx, $testcases->{arr}->[$idx]->[3],
$testcases->{arr}->[$idx]->[0],
$testcases->{arr}->[$idx]->[2],
($testcases->{arr}->[$idx]->[0] !=
$testcases->{arr}->[$idx]->[2]) && '*';
}
Output:
0 LINE line 36 expected 36
1 LINE line 37 expected 37
2 load line 38 expected 38
3 chain load line 39 expected 39
4 chain load line 40 expected 40
5 chain load line 41 expected 41
6 chain load line 42 expected 42
7 chain load line 43 expected 43
All the chain load
lines were loaded with zero extra characters compared to the original [x, y]
approach. Some overhead, but not much!
Code:
By starting with __LINE__
and assuming a fixed number of lines per call, a counter will do the trick. This could probably be done more cleanly with a tie
.
#!perl
use strict; use warnings; use 5.010;
package FluentAutoIncList {
sub new { # call as $class->new(__LINE__); each element is one line
my $class = shift;
return bless {lnum => shift // 0, arr => []}, $class;
}
sub size { return scalar @{ shift->{arr} }; }
sub last { return shift->size-1; } # $#
sub L { # Push a new record with the next line number on the front
my $self = shift;
push @{ $self->{arr} }, [++$self->{lnum}, @_];
return $self;
} #L
sub add { # just add it
my $self = shift;
++$self->{lnum}; # keep it consistent
push @{ $self->{arr} }, [@_];
return $self;
} #add
} #FluentAutoIncList
# List of [line number, item index, expected line number, type]
my $testcases = FluentAutoIncList->new(__LINE__) # line 28
->add(__LINE__,0,29,'LINE')
->add(__LINE__,1,30,'LINE')
->L(2,31,'L()')
->L(3,32,'L()')
->L(4,33,'L()')
;
foreach my $idx (0..$testcases->last) {
printf "%2d %-10s line %2d expected %2d %s\n",
$idx, $testcases->{arr}->[$idx]->[3],
$testcases->{arr}->[$idx]->[0],
$testcases->{arr}->[$idx]->[2],
($testcases->{arr}->[$idx]->[0] !=
$testcases->{arr}->[$idx]->[2]) && '*';
}
Output:
0 LINE line 29 expected 29
1 LINE line 30 expected 30
2 L() line 31 expected 31
3 L() line 32 expected 32
4 L() line 33 expected 33