Обсуждение в другом вопросе получило меня удивление: тому, что системы исключения других языков программирования имеют тем Perl, недостает?
Встроенные исключения Perl являются немного специальными в этом, они были, как система объекта Perl 5, вид - соединенных болтом на машинально, и они перегружают другие ключевые слова (eval
и die
) которые не выделены конкретно исключениям.
Синтаксис может быть немного ужасным, по сравнению с языками со встроенным синтаксисом типа попытки/броска/выгоды. Я обычно делаю это как это:
eval {
do_something_that_might_barf();
};
if ( my $err = $@ ) {
# handle $err here
}
Существует несколько модулей CPAN, которые обеспечивают синтаксический сахар, чтобы добавить ключевые слова попытки/выгоды и позволить легкое объявление иерархий класса исключений и этажерки.
Основной проблемой, которую я вижу с системой исключения Perl, является использование глобального специального предложения $@
содержать текущую ошибку, а не специализированное catch
- введите механизм, который мог бы быть более безопасным с точки зрения объема, хотя я лично никогда не сталкивался ни с какими проблемами с $@
быть портившимся.
Некоторые классы исключений, например Ошибка , не удается обработать управление потоком из блоков try / catch. Это приводит к незаметным ошибкам:
use strict; use warnings;
use Error qw(:try);
foreach my $blah (@somelist)
{
try
{
somemethod($blah);
}
catch Error with
{
my $exception = shift;
warn "error while processing $blah: " . $exception->stacktrace();
next; # bzzt, this will not do what you want it to!!!
};
# do more stuff...
}
Обходной путь - использовать переменную состояния и проверять ее за пределами блока try / catch, что для меня ужасно похоже на вонючий код n00b.
Два других "подводных камня" в Error (оба из них причинили мне боль, поскольку их ужасно отлаживать, если вы не сталкивались с этим раньше):
use strict; use warnings;
try
{
# do something
}
catch Error with
{
# handle the exception
}
Выглядит разумно, не так ли? Этот код компилируется, но приводит к странным и непредсказуемым ошибкам. Проблемы:
ошибка использования qw (: try)
была опущена, поэтому блок try {} ...
будет неправильно проанализирован (вы можете увидеть или не увидеть предупреждение, в зависимости от остальная часть вашего кода) try
является прототипом вызова метода . Ах да, это также напоминает мне, что, поскольку try
, catch
и т. Д. Являются вызовами методов, это означает, что стек вызовов в этих блоках будет не таким, как вы ожидаете. (На самом деле есть два дополнительных уровня стека из-за внутреннего вызова внутри Error.pm.) Следовательно, у меня есть несколько модулей, заполненных шаблонным кодом, подобным этому, который просто добавляет беспорядка:
my $errorString;
try
{
$x->do_something();
if ($x->failure())
{
$errorString = 'some diagnostic string';
return; # break out of try block
}
do_more_stuff();
}
catch Error with
{
my $exception = shift;
$errorString = $exception->text();
}
finally
{
local $Carp::CarpLevel += 2;
croak "Could not perform action blah on " . $x->name() . ": " . $errorString if $errorString;
};
В C ++ и C # вы можете определить типы, которые можно бросить, с отдельными блоками Catch, которые управляют каждым типом. Системы типа Perl имеют определенные проблемы, связанные с RTTI и наследованием, согласно тому, что я читаю в блоге Chicomate.
Я не уверен, как другие динамические языки управлять исключениями; Оба C ++, так и C # являются статическими языками, и которые имеют с собой определенную мощность в системе типа.
Проблема , в том, что исключения Perl 5 скреплены; Они не созданы из начала языкового дизайна как что-то интегральное, насколько Perl написан.
С тех пор, как я использовал Perl, прошло очень много времени, так что моя память может быть нечеткой и/или Perl мог быть улучшен, но из того, что я помню (по сравнению с Python, который я использую ежедневно):
так как исключения являются поздним дополнением, они не поддерживаются последовательно в библиотеках ядра
(Неправда; они не поддерживаются последовательно в библиотеках ядра из-за того, что программисты, которые написали эти библиотеки, не любят исключений. )
нет предопределенной иерархии исключений - вы не можете поймать связанную группу исключений, поймав базовый класс
нет эквивалента попытки:... наконец:... определить код, который будет вызываться вне зависимости от того, было ли сделано исключение или нет, например, чтобы освободить ресурсы.
(finally
в Perl в основном лишнее -- деструкторы объектов запускаются сразу после выхода из scope; а не тогда, когда возникает давление на память. Таким образом, в деструкторе можно реально разместить любые ресурсы, не относящиеся к памяти, и он будет работать нормально.)
(Насколько я могу судить) можно только бросать строки - нельзя бросать объекты, содержащие дополнительную информацию
(Complete false. die $object
работает так же хорошо как die $string
.)
вы не можете получить след стека, показывающий куда было брошено исключение - в python вы получаете подробную информацию, включая исходный код для каждой строки в стеке вызовов
(False. perl -MCarp::Always
и наслаждаетесь.)
это прикольный кладж.
(Субъективный. В Perl он реализован так же, как и везде. Он просто использует по-другому называемые ключевые слова.)
Типичный метод, которые большинство людей научились обрабатывать исключения, уязвимы для отсутствия попадания в захваченные исключения:
eval { some code here };
if( $@ ) { handle exception here };
Вы можете сделать:
eval { some code here; 1 } or do { handle exception here };
Это защищает от отсутствия исключения из-за $ @
Быть наложенными, но оно все еще уязвимо, чтобы потерять ценность $ @
.
Убедитесь, что вы не складываете исключение, когда вы делаете свою EVAL, вы должны локализовать $ @
;
eval { local $@; some code here; 1 } or do { handle exception here };
Это все тонкие поломки, а предотвращение требует много эзотерической котельной Отказ
В большинстве случаев это не проблема. Но я был сожжен исключением, едящим предметом деструкторов в реальном коде. Отладка проблемы было ужасно.
Ситуация явно плохо. Посмотрите на все модули на CPAN, созданные построенные приличные процессоры исключения.
Подавляющие ответы в пользу Попытка :: Tiny В сочетании с тем фактом, что попробуйте :: Tiny не «слишком умно вдвое», убедил меня, чтобы попробовать это. Подобные вещи Trycatch и Исключение :: Class :: Trycatch , Ошибка , и включена и на слишком сложным для меня доверять. Попробуйте :: Tiny - это шаг в правильном направлении, но у меня все еще нет легкого класса исключения для использования.
с использованием перL, языка и письменных исключений: оба набора $ @
. На других языках языка исключения отделяются от пользовательских исключений и создают совершенно отдельный поток.
Вы можете поймать базу пользовательских записей исключения.
Если есть Мои :: исключение :: One
и Мои :: Исключение :: Два
if ($@ and $@->isa('My::Exception'))
поймают оба.
Не забывайте уловить любые непользовательские исключения с помощью иначе
.
elsif ($@)
{
print "Other Error $@\n";
exit;
}
Также приятно обернуть исключение в подзовов под названием Sub, чтобы бросить его.
Попробуйте::Tiny (или модули, построенные на нем) - единственный правильный способ работы с исключениями на Perl 5. Проблемы, связанные с этим, тонкие, но связанная с ними статья объясняет их в деталях.
Вот как это использовать:
use Try::Tiny;
try {
my $code = 'goes here';
succeed() or die 'with an error';
}
catch {
say "OH NOES, YOUR PROGRAM HAZ ERROR: $_";
};
eval
и $@
- это движущиеся части, о которых вам не нужно беспокоиться.
Некоторые люди думают, что это kludge, но прочитав реализации других языков (а также Perl 5), он ничем не отличается от других. Есть только $@
движущаяся часть, в которую можно зацепиться за руку... но как и в случае с другими частями техники с обнаженными движущимися частями... если не дотронуться до нее, она не оторвет вам пальцы. Так что используйте Try::Tiny и продолжайте печатать быстрее ;)
Проблема, с которой я недавно столкнулся с механизмом исключения eval
, связана с обработчиком $SIG{__DIEE__}
. Я -- ошибочно -- предположил, что этот обработчик вызывается только при выходе из интерпретатора Perl через die()
и хотел использовать этот обработчик для протоколирования фатальных событий. Затем оказалось, что я протоколировал исключения в библиотечном коде как фатальные ошибки, что явно было неправильно.
Решение заключалось в проверке состояния переменной $^S
или $EXCEPTIONS_BEING_CAUGHT
:
use English;
$SIG{__DIE__} = sub {
if (!$EXCEPTION_BEING_CAUGHT) {
# fatal logging code here
}
};
Проблема, которую я вижу здесь, заключается в том, что __DIE__
обработчик используется в двух похожих, но разных ситуациях. Эта переменная $^S
очень похожа на позднее дополнение. Не знаю, действительно ли это так.