Есть ли способ надежно получить текущий номер строки во время назначения многострочного списка Perl без явного использования __LINE__
?Я храню тестовые случаи в списке и хотел бы пометить каждый из них своим номером строки. * Таким образом, я могу сделать (примерно) ok($_->[1], 'line ' . $_->[0]) for @tests
.И, конечно же, я хотел бы сэкономить набор текста по сравнению с указанием __LINE__
в начале каждого теста :).Я не смог найти способ сделать это, и я столкнулся с некоторым запутанным поведением в строках, сообщенных caller
.
* Возможно XY, но я не могу найти модульчтобы сделать это.
Обновление Я нашел хак и опубликовал его как ответ .Спасибо @zdim за помощь в рассмотрении проблемы по-другому!
MCVE
Длинный, потому что я пробовал несколько разных вариантов.my_eval
, L()
и L2{}
- это те, которые я пробовал до сих пор - L()
был тем, который я изначально надеялся, сработает.Спрыгните на my @testcases
, чтобы увидеть, как я их использую.При тестировании скопируйте строку Шебанга.
Вот мой вариант использования без MCVE , если вам интересно.
#!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
возвращает правильный номер строки - но только один раз (!).
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
, но для него требуется блок, который возвращает список, а не сам список.Также используется 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()')
;
Однако даже они здесь не работают - я получаю строку 26 для каждого из вызовов ->L()
,Похоже, что caller
видит все цепочки вызовов, поступающие из линии $testcases2->...
.Ну что ж.Мне все еще интересно знать, почему, если кто-нибудь может меня просветить!