如何在测试用例的多行列表初始值设定项中获取当前行号?
How to get the current line number in a multi-line list initializer of testcases?
有没有办法在 Perl 中可靠地获取当前行号
没有显式使用 __LINE__
的多行列表分配?我是
将测试用例存储在列表中,并希望用它的行标记每个
number.* 这样我就可以做到(大致)
ok($_->[1], 'line ' . $_->[0]) for @tests
。
而且,当然,与
将 __LINE__
放在每个测试用例的开头 :) 。我有
无法找到这样做的方法,我遇到了一些
caller
.
报告的行中的混淆行为
* 可能的 XY,但我找不到模块来做。
更新 我发现了一个 hack 并将其发布为 。感谢@zdim 帮助我以不同的方式思考问题!
MCVE
很长,因为我尝试了几种不同的选择。 my_eval
,
L()
和 L2{}
是我目前尝试过的一些方法 — L()
就是那个
我最初希望能奏效。跳转到 my @testcases
看看如何
我正在使用这些。测试时,请复制 shebang 行。
这是我的non-MCVE use case,如果你有兴趣的话。
#!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]) && '*';
}
输出
加上我的评论。
0 LINE line 32 expected 32
1 LINE line 33 expected 33
使用 __LINE__
明确工作正常,但我正在寻找
缩写。
2 L() line 45 expected 34 *
3 L() line 45 expected 35 *
L()
用caller
获取行号,后报一行
在文件中 (!).
4 do {L} line 36 expected 36
5 do {L} line 45 expected 37 *
当我将 L()
调用包装在 do{}
中时,caller
returns 正确
行号 — 但只有一次 (!).
6 eval {L} line 38 expected 38
7 eval {L} line 39 expected 39
块eval
,有趣的是,工作正常。然而,它并不短
比 __LINE__
.
8 eval L line 1 expected 40 *
9 eval L line 1 expected 41 *
String eval
给出eval
里面的行号(不出意外)
10 my_eval L line 45 expected 42 *
11 my_eval L line 45 expected 43 *
my_eval()
是一个字符串 eval
加上一个 #line
指令基于
caller
。它还在文件后面给出了行号 (!)。
12 L2{} line 45 expected 44 *
13 L2{} line 45 expected 45
L2
和L
一样,但是returns一个list需要一个block,
而不是
列表本身。它还使用 caller
作为行号。而它
一次是正确的,但不是两次(!)。 (可能只是因为这是最后一次
项目 — my_eval
也报告了第 45 行。)
那么,这是怎么回事?我听说过 Deparse,想知道这是不是
优化相关,但我对引擎了解不够了解
从哪里开始调查。我也想象这可以用源来完成
过滤器或 Devel::Declare
,但这远远超出了我的范围
经验水平。
拍摄 2
@zdim 的回答让我开始思考流畅的界面,例如 :
$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()')
;
但是,即使是那些在这里也不起作用 — 每个 ->L()
调用我都会得到第 26 行。因此,caller
似乎将所有链接的调用都视为来自 $testcases2->...
行。那好吧。我还是很想知道为什么,如果有人能赐教一下!
caller
只能获取语句的行号,由编译决定。
当我把代码改成
my @testcases;
push @testcases, ([__LINE__,0,32,'LINE']);
push @testcases, ([__LINE__,1,33,'LINE']);
push @testcases, (L(2,34,'L()'));
push @testcases, (L(3,35,'L()'));
...
维护行号,有效(字符串评估除外)。
因此,在实践方面,使用 caller
可以使用单独的调用语句。
Perl 内部结构
行号在编译时被烘焙到 op-tree 和(我的重点)
At run-time, only the line numbers of statements are available [...]
来自 ikegami's post on permonks。
我们可以通过 运行ning perl -MO=Concise script.pl
看到这一点,其中行
2 nextstate(main 25 line_nos.pl:45) v:*,&,{,x*,x&,x$,$,67108864 ->3
用于 nextstate
操作,它设置 caller
(和警告)的行号。请参阅 和下面的 nextstate
示例。
解决这个问题的方法是尝试欺骗编译(以某种方式),或者更好的当然是,不要 assemble 列表中的信息。一种这样的方法是在 .
有关相关案例和更多详细信息,请参阅 。
nextstate
例子
这是一个多行函数调用链 运行 到 Deparse
(注释):
$ perl -MO=Concise -e '$x
->foo()
->bar()
->bat()'
d <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 1 -e:1) v:{ ->3 <=== the only nextstate
c <1> entersub[t4] vKRS/TARG ->d
3 <0> pushmark s ->4
a <1> entersub[t3] sKRMS/LVINTRO,TARG,INARGS ->b
4 <0> pushmark s ->5
8 <1> entersub[t2] sKRMS/LVINTRO,TARG,INARGS ->9
5 <0> pushmark s ->6
- <1> ex-rv2sv sKM/1 ->7
6 <#> gvsv[*x] s ->7
7 <.> method_named[PV "foo"] s ->8
9 <.> method_named[PV "bar"] s ->a
b <.> method_named[PV "bat"] ->c
-e syntax OK
即使连续的调用在不同的行上,它们也是同一个语句的一部分,所以都附加到同一个 nextstate
。
编辑 这个答案现在包含在 CPAN module (GitHub)!
让我想到了流畅的界面。以下是适用于我的特定用例的 两个技巧 ,但它们无法帮助我理解问题中报告的行为。如果您能帮忙,请post另一个答案!
Hack 2(较新)(现在在 CPAN 上)
我认为这个非常接近最小。在 perl 中,您可以通过带有 $ref->()
的引用来调用子例程,并且可以省略箭头链中的第二个和后续 ->
。这意味着,例如,您可以:
my $foo; $foo=sub { say shift; return $foo; };
$foo->(1)
(2)
(3);
看起来不错吧?所以这是 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]) && '*';
}
输出:
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
与原始 [x, y]
方法相比,所有 chain load
行都加载了零个额外字符。一些开销,但不多!
技巧 1
代码:
从 __LINE__
开始并假设每次调用的行数固定,计数器就可以解决问题。使用 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]) && '*';
}
输出:
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
有没有办法在 Perl 中可靠地获取当前行号
没有显式使用 __LINE__
的多行列表分配?我是
将测试用例存储在列表中,并希望用它的行标记每个
number.* 这样我就可以做到(大致)
ok($_->[1], 'line ' . $_->[0]) for @tests
。
而且,当然,与
将 __LINE__
放在每个测试用例的开头 :) 。我有
无法找到这样做的方法,我遇到了一些
caller
.
* 可能的 XY,但我找不到模块来做。
更新 我发现了一个 hack 并将其发布为
MCVE
很长,因为我尝试了几种不同的选择。 my_eval
,
L()
和 L2{}
是我目前尝试过的一些方法 — L()
就是那个
我最初希望能奏效。跳转到 my @testcases
看看如何
我正在使用这些。测试时,请复制 shebang 行。
这是我的non-MCVE use case,如果你有兴趣的话。
#!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]) && '*';
}
输出
加上我的评论。
0 LINE line 32 expected 32
1 LINE line 33 expected 33
使用 __LINE__
明确工作正常,但我正在寻找
缩写。
2 L() line 45 expected 34 *
3 L() line 45 expected 35 *
L()
用caller
获取行号,后报一行
在文件中 (!).
4 do {L} line 36 expected 36
5 do {L} line 45 expected 37 *
当我将 L()
调用包装在 do{}
中时,caller
returns 正确
行号 — 但只有一次 (!).
6 eval {L} line 38 expected 38
7 eval {L} line 39 expected 39
块eval
,有趣的是,工作正常。然而,它并不短
比 __LINE__
.
8 eval L line 1 expected 40 *
9 eval L line 1 expected 41 *
String eval
给出eval
里面的行号(不出意外)
10 my_eval L line 45 expected 42 *
11 my_eval L line 45 expected 43 *
my_eval()
是一个字符串 eval
加上一个 #line
指令基于
caller
。它还在文件后面给出了行号 (!)。
12 L2{} line 45 expected 44 *
13 L2{} line 45 expected 45
L2
和L
一样,但是returns一个list需要一个block,
而不是
列表本身。它还使用 caller
作为行号。而它
一次是正确的,但不是两次(!)。 (可能只是因为这是最后一次
项目 — my_eval
也报告了第 45 行。)
那么,这是怎么回事?我听说过 Deparse,想知道这是不是
优化相关,但我对引擎了解不够了解
从哪里开始调查。我也想象这可以用源来完成
过滤器或 Devel::Declare
,但这远远超出了我的范围
经验水平。
拍摄 2
@zdim 的回答让我开始思考流畅的界面,例如
$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()')
;
但是,即使是那些在这里也不起作用 — 每个 ->L()
调用我都会得到第 26 行。因此,caller
似乎将所有链接的调用都视为来自 $testcases2->...
行。那好吧。我还是很想知道为什么,如果有人能赐教一下!
caller
只能获取语句的行号,由编译决定。
当我把代码改成
my @testcases;
push @testcases, ([__LINE__,0,32,'LINE']);
push @testcases, ([__LINE__,1,33,'LINE']);
push @testcases, (L(2,34,'L()'));
push @testcases, (L(3,35,'L()'));
...
维护行号,有效(字符串评估除外)。
因此,在实践方面,使用 caller
可以使用单独的调用语句。
Perl 内部结构
行号在编译时被烘焙到 op-tree 和(我的重点)
At run-time, only the line numbers of statements are available [...]
来自 ikegami's post on permonks。
我们可以通过 运行ning perl -MO=Concise script.pl
看到这一点,其中行
2 nextstate(main 25 line_nos.pl:45) v:*,&,{,x*,x&,x$,$,67108864 ->3
用于 nextstate
操作,它设置 caller
(和警告)的行号。请参阅 nextstate
示例。
解决这个问题的方法是尝试欺骗编译(以某种方式),或者更好的当然是,不要 assemble 列表中的信息。一种这样的方法是在
有关相关案例和更多详细信息,请参阅
nextstate
例子
这是一个多行函数调用链 运行 到 Deparse
(注释):
$ perl -MO=Concise -e '$x
->foo()
->bar()
->bat()'
d <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 1 -e:1) v:{ ->3 <=== the only nextstate
c <1> entersub[t4] vKRS/TARG ->d
3 <0> pushmark s ->4
a <1> entersub[t3] sKRMS/LVINTRO,TARG,INARGS ->b
4 <0> pushmark s ->5
8 <1> entersub[t2] sKRMS/LVINTRO,TARG,INARGS ->9
5 <0> pushmark s ->6
- <1> ex-rv2sv sKM/1 ->7
6 <#> gvsv[*x] s ->7
7 <.> method_named[PV "foo"] s ->8
9 <.> method_named[PV "bar"] s ->a
b <.> method_named[PV "bat"] ->c
-e syntax OK
即使连续的调用在不同的行上,它们也是同一个语句的一部分,所以都附加到同一个 nextstate
。
编辑 这个答案现在包含在 CPAN module (GitHub)!
Hack 2(较新)(现在在 CPAN 上)
我认为这个非常接近最小。在 perl 中,您可以通过带有 $ref->()
的引用来调用子例程,并且可以省略箭头链中的第二个和后续 ->
。这意味着,例如,您可以:
my $foo; $foo=sub { say shift; return $foo; };
$foo->(1)
(2)
(3);
看起来不错吧?所以这是 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]) && '*';
}
输出:
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
与原始 [x, y]
方法相比,所有 chain load
行都加载了零个额外字符。一些开销,但不多!
技巧 1
代码:
从 __LINE__
开始并假设每次调用的行数固定,计数器就可以解决问题。使用 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]) && '*';
}
输出:
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