2016-04-01 6 views
3

Я пытаюсь написать функцию, которая может принимать все свои аргументы и печатать их как строку точно так же, как они были введены.Получение всех аргументов, переданных подпрограмме в виде строки в Perl

Например с помощью следующей функции:

test('arg1' => $arg1, 'arg2' => $arg2); 

Я хотел бы получить следующую строку внутри функции отформатированный ТОЧНО, как показано ниже:

"'arg1' => $arg1, 'arg2' => $arg2" 

Я хочу сделайте это, чтобы я мог распечатать все аргументы так же, как они были введены для целей отладки/тестирования.

+0

Другой пример: скажем, вы хотели выполнить 'test (time)'. Просто печать '@ _' будет печатать значение эпохи текущего времени и очень неясно (на первый взгляд), что вы на самом деле проверяете текущее время. – tjwrona1992

+2

Используйте [Devel :: Trace] (https://metacpan.org/pod/Devel::Trace) или просто используйте обычный отладчик и перерыв в строке, где вызывается функция. – ThisSuitIsBlackNot

+1

См. Также ['Debug :: Show'] (https://metacpan.org/pod/Debug::Show), [' PadWalker'] (https://metacpan.org/pod/PadWalker), ['Данные :: Dumper :: Names'] (https://metacpan.org/pod/Data::Dumper::Names), ['Data :: Dumper :: Lazy'] (https://metacpan.org/pod/ Data :: Dumper :: Lazy) и ['Debug :: ShowStuff :: ShowVar'] (https://metacpan.org/pod/Debug::ShowStuff::ShowVar) –

ответ

6

Perl предоставляет специальные debugging hooks, которые позволяют видеть исходные строки скомпилированных исходных файлов. Вы можете написать собственный отладчик, который печатает исходную строку каждый раз при вызове подпрограммы.

Ниже вы можете указать одну или несколько подпрограмм, которые вы хотите сопоставить; каждый раз, когда вызывается соответствующая подпрограмма, выводится соответствующая строка.

package Devel::ShowCalls; 

our %targets; 

sub import { 
    my $self = shift; 

    for (@_) { 
     # Prepend 'main::' for names without a package specifier 
     $_ = "main::$_" unless /::/; 
     $targets{$_} = 1;   
    } 
} 

package DB; 

sub DB { 
    ($package, $file, $line) = caller; 
} 

sub sub { 
    print ">> $file:$line: ", 
      ${ $main::{"_<$file"} }[$line] if $Devel::ShowCalls::targets{$sub}; 
    &$sub; 
} 

1; 

Для трассировки вызовов функций foo и Baz::qux в следующей программе:

sub foo {} 
sub bar {} 
sub Baz::qux {} 

foo(now => time); 
bar rand; 
Baz::qux(qw/unicorn pony waffles/); 

Run:

$ perl -d:ShowCalls=foo,Baz::qux myscript.pl 
>> myscript.pl:5: foo(now => time); 
>> myscript.pl:7: Baz::qux(qw/unicorn pony waffles/); 

Обратите внимание, что это будет печатать только первую строку из вызов, поэтому он не будет работать для звонков, таких как

foo(bar, 
    baz); 
+0

Единственная проблема заключается в том, что я хочу использовать это для написания сценария модульного тестирования. По сути, я хочу, чтобы мои тесты были названы после их ассоциированной функции и аргументов, поэтому мне не нужно вручную указывать каждый тест. Есть ли способ сделать это без явного запуска Perl в режиме отладки? или, может быть, вызывать режим отладки из самого скрипта тестирования? – tjwrona1992

+1

@ tjwrona1992 Этот подход не удастся, если вы хотите запустить несколько тестов с одной и той же функцией с одним и тем же входом. Например, с '$ foo-> connect(); $ Foo-> близко(); $ foo-> close(); ', вы, вероятно, хотите, чтобы первый« закрыть »закрыл соединение, но вы можете захотеть, чтобы второй« закрыть »предупредил. Различное поведение, но в вашей схеме у вас будет одинаковое имя теста для обоих. Имя теста должно быть прочитанной человеком строкой, описывающей ожидаемое поведение. Сделайте какие-либо будущие программы поддержки (включая себя!) В пользу и напишите описательные имена тестов. – ThisSuitIsBlackNot

0

Я знаю, что это, вероятно, не самое лучшее решение, но это работает:

sub test { 
    my (undef, $file_name, $line_number) = caller; 
    open my $fh, '<', $file_name or die $!; 
    my @lines = <$fh>; 
    close $fh; 

    my $line = $lines[$line_number - 1]; 
    trim($line); 

    print $line."\n"; 
} 

sub trim { 
    return map { $_ =~ s/^\s+|\s+$//g } @_; 
} 

Теперь, когда вы запустите это:

test(time); 

Вы получите это как выход:

тест (время);