Что повреждается об исключениях в Perl?

Обсуждение в другом вопросе получило меня удивление: тому, что системы исключения других языков программирования имеют тем Perl, недостает?

Встроенные исключения Perl являются немного специальными в этом, они были, как система объекта Perl 5, вид - соединенных болтом на машинально, и они перегружают другие ключевые слова (eval и die) которые не выделены конкретно исключениям.

Синтаксис может быть немного ужасным, по сравнению с языками со встроенным синтаксисом типа попытки/броска/выгоды. Я обычно делаю это как это:

eval { 
    do_something_that_might_barf();
};

if ( my $err = $@ ) { 
    # handle $err here
}

Существует несколько модулей CPAN, которые обеспечивают синтаксический сахар, чтобы добавить ключевые слова попытки/выгоды и позволить легкое объявление иерархий класса исключений и этажерки.

Основной проблемой, которую я вижу с системой исключения Perl, является использование глобального специального предложения $@ содержать текущую ошибку, а не специализированное catch- введите механизм, который мог бы быть более безопасным с точки зрения объема, хотя я лично никогда не сталкивался ни с какими проблемами с $@ быть портившимся.

29
задан Community 23 May 2017 в 12:18
поделиться

7 ответов

Некоторые классы исключений, например Ошибка , не удается обработать управление потоком из блоков 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
}

Выглядит разумно, не так ли? Этот код компилируется, но приводит к странным и непредсказуемым ошибкам. Проблемы:

  1. ошибка использования qw (: try) была опущена, поэтому блок try {} ... будет неправильно проанализирован (вы можете увидеть или не увидеть предупреждение, в зависимости от остальная часть вашего кода)
  2. отсутствует точка с запятой после блока catch! Неинтуитивно, поскольку блоки управления не используют точки с запятой, но на самом деле 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;
};
13
ответ дан 28 November 2019 в 01:03
поделиться

В C ++ и C # вы можете определить типы, которые можно бросить, с отдельными блоками Catch, которые управляют каждым типом. Системы типа Perl имеют определенные проблемы, связанные с RTTI и наследованием, согласно тому, что я читаю в блоге Chicomate.

Я не уверен, как другие динамические языки управлять исключениями; Оба C ++, так и C # являются статическими языками, и которые имеют с собой определенную мощность в системе типа.

Проблема , в том, что исключения Perl 5 скреплены; Они не созданы из начала языкового дизайна как что-то интегральное, насколько Perl написан.

1
ответ дан 28 November 2019 в 01:03
поделиться

С тех пор, как я использовал Perl, прошло очень много времени, так что моя память может быть нечеткой и/или Perl мог быть улучшен, но из того, что я помню (по сравнению с Python, который я использую ежедневно):

  1. так как исключения являются поздним дополнением, они не поддерживаются последовательно в библиотеках ядра

    (Неправда; они не поддерживаются последовательно в библиотеках ядра из-за того, что программисты, которые написали эти библиотеки, не любят исключений. )

  2. нет предопределенной иерархии исключений - вы не можете поймать связанную группу исключений, поймав базовый класс

  3. нет эквивалента попытки:... наконец:... определить код, который будет вызываться вне зависимости от того, было ли сделано исключение или нет, например, чтобы освободить ресурсы.

    (finally в Perl в основном лишнее -- деструкторы объектов запускаются сразу после выхода из scope; а не тогда, когда возникает давление на память. Таким образом, в деструкторе можно реально разместить любые ресурсы, не относящиеся к памяти, и он будет работать нормально.)

  4. (Насколько я могу судить) можно только бросать строки - нельзя бросать объекты, содержащие дополнительную информацию

    (Complete false. die $object работает так же хорошо как die $string.)

  5. вы не можете получить след стека, показывающий куда было брошено исключение - в python вы получаете подробную информацию, включая исходный код для каждой строки в стеке вызовов

    (False. perl -MCarp::Always и наслаждаетесь.)

  6. это прикольный кладж.

    (Субъективный. В Perl он реализован так же, как и везде. Он просто использует по-другому называемые ключевые слова.)

1
ответ дан 28 November 2019 в 01:03
поделиться

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

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 - это шаг в правильном направлении, но у меня все еще нет легкого класса исключения для использования.

24
ответ дан 28 November 2019 в 01:03
поделиться

с использованием перL, языка и письменных исключений: оба набора $ @ . На других языках языка исключения отделяются от пользовательских исключений и создают совершенно отдельный поток.

Вы можете поймать базу пользовательских записей исключения.

Если есть Мои :: исключение :: One и Мои :: Исключение :: Два

if ($@ and $@->isa('My::Exception'))

поймают оба.

Не забывайте уловить любые непользовательские исключения с помощью иначе .

elsif ($@)
    {
    print "Other Error $@\n";
    exit;
    }

Также приятно обернуть исключение в подзовов под названием Sub, чтобы бросить его.

2
ответ дан 28 November 2019 в 01:03
поделиться

Попробуйте::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 и продолжайте печатать быстрее ;)

25
ответ дан 28 November 2019 в 01:03
поделиться

Проблема, с которой я недавно столкнулся с механизмом исключения eval, связана с обработчиком $SIG{__DIEE__}. Я -- ошибочно -- предположил, что этот обработчик вызывается только при выходе из интерпретатора Perl через die() и хотел использовать этот обработчик для протоколирования фатальных событий. Затем оказалось, что я протоколировал исключения в библиотечном коде как фатальные ошибки, что явно было неправильно.

Решение заключалось в проверке состояния переменной $^S или $EXCEPTIONS_BEING_CAUGHT:

use English;
$SIG{__DIE__} = sub {
    if (!$EXCEPTION_BEING_CAUGHT) {
        # fatal logging code here
    }
};

Проблема, которую я вижу здесь, заключается в том, что __DIE__ обработчик используется в двух похожих, но разных ситуациях. Эта переменная $^S очень похожа на позднее дополнение. Не знаю, действительно ли это так.

9
ответ дан 28 November 2019 в 01:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: