Я работаю над тестовым фреймворком на Perl. В рамках тестов мне может потребоваться добавить проверки предусловия или постусловия для любого заданного теста, но не обязательно для всех из них. Пока что у меня есть что-то вроде:
eval "&verify_precondition_TEST$n";
print $@ if $@;
К сожалению, это выводит «Неопределенная подпрограмма & verify_precondition_TEST1, вызванная в ...», если функция не существует.
Как я могу заранее определить, существует ли функция, прежде чем пытаться ее вызвать?





Package::Name->can('function')
или же
*Package::Name::function{CODE}
# or no strict; *{ "Package::Name::$function" }{CODE}
или просто жить с исключением. Если вы вызываете функцию в eval и установлен $ @, вы не можете вызвать функцию.
Наконец, похоже, что вы можете захотеть Test :: Class вместо того, чтобы писать это самостоятельно.
Обновлено: defined &function_name (или вариант no strict; defined &{ $function_name }), как упоминалось в других ответах, выглядит лучшим способом. UNIVERSAL :: can лучше всего подходит для того, что вы собираетесь вызывать как метод (стилистически), и зачем возиться с таблицей символов, когда Perl дает вам синтаксис для выполнения того, что вы хотите.
Обучение ++ :)
Ого, 5.002? Думаю, это появилось еще до моего рождения :)
Разве 5.002 - не тот, у которого все проблемы с переполнением буфера? :)
Согласно perlhist, 5.003 - это 5.002 с исправлениями безопасности.
(И, по-видимому, тогда у них не было UNIVERSAL ::. Вот почему main-> может не работать; он отлично работает под 5.10.)
@Greg Hewgill, вы никогда не выходите за пределы пакета. Попробуйте main-> can ("функция")
@GregHewgill - мне интересно ваше окружение. Не могли бы вы сказать несколько слов об этом? Звучит как большой пример использования «древних» платформ до сегодняшнего дня ...
@ jm666: Я думаю, что в то время это была разновидность OSF / 1 или AIX. Установленный Perl поставлялся вместе с ОС, и попытки его обновления не стоили усилий.
С определением:
if (eval "defined(&verify_precondition_TEST$n)") {
eval "&verify_precondition_TEST$n";
print $@ if $@;
}
else {
print "verify_precondition_TEST$n does not exist\n";
}
Обновлено: хм, я думал только об eval, поскольку он был в вопросе, но с символическими ссылками, поднятыми с Леоном Тиммермансом, не могли бы вы сделать
if (defined(&{"verify_precondition_TEST$n"}) {
&{"verify_precondition_TEST$n"};
print $@ if $@;
}
else {
print "verify_precondition_TEST$n does not exist\n";
}
даже со строгим?
Это неправильный eval. Вам нужен блок eval, а не строковый eval. Плюс тестирование, существует ли функция, в любом случае можно выполнить с помощью eval.
Мне нужно использовать строку eval, потому что $ n неизвестно во время компиляции.
Нет, не знаешь. Вам нужно использовать символические ссылки. См. Мой ответ как пример того, как к ним подойти.
Вы правы, определенное освобождается от строгих рекомендаций. Однако вызов субмарины - нет. Вы должны либо использовать «no strict», либо использовать eval, первый будет более чистым IMNSHO.
Кроме того, вам нужно использовать имя пакета, если функция определена в каком-то пакете ... например, use CGI; my $q=CGI->new; my @users = defined (&CGI::multi_param) ? $q->multi_param('u') : $q->param('u');
sub function_exists {
no strict 'refs';
my $funcname = shift;
return \&{$funcname} if defined &{$funcname};
return;
}
if (my $subref = function_exists("verify_precondition_TEST$n") {
...
}
+1 (отсутствие строгого всегда лучше, чем строка eval) Кстати, крошечная опечатка: s / $ functname / $ funcname /
Я бы написал это как no strict 'refs', чтобы заметить читателю, что вы собираетесь совершить некоторую магию символических ссылок. :)
Я использовал подход Леона, но когда у меня было несколько пакетов, это не удалось. Я не уверен, почему именно; Я думаю, это связано с распространением области видимости между пространствами имен. Это решение, которое я придумал.
my %symbols = ();
my $package =__PACKAGE__; #bring it in at run-time
{
no strict;
%symbols = %{$package . "::"}; #See Symbol Tables on perlmod
}
print "$funcname not defined\n" if (! defined($symbols{$funcname});
Ссылки:
Ссылка __УПАКОВКА__ на странице perlmod.
Пакеты / __ ПАКЕТ__ ссылка на обучение Perl в Австралии.
Проблема может быть в том, что вы пытались сделать напрямую, например: if ( defined &{$package . '::subname'} ), но вроде этого должно быть хорошо: my $subname = $package.'::sub_name'; if ( defined &{$subname} ) ...
К сожалению, я делаю это вне любого пакета, поэтому получаю: Не могу найти метод объекта "can" через пакет "main". Кроме того, я использую старую версию Perl (5.002) в среде, где у меня абсолютно не установлены модули Perl.