Каковы распространенные подводные камни при использовании Perl Eval? - PullRequest
15 голосов
/ 29 сентября 2011

Каковы распространенные подводные камни, связанные с eval в Perl, которые могут заставить вас выбрать такой модуль, как Try::Tiny?

Ответы [ 4 ]

25 голосов
/ 29 сентября 2011

Perl's eval выпускается в двух вариантах: струнный и блочный. String eval вызывает компилятор для выполнения исходного кода. Блок eval окружает уже скомпилированный код в оболочке, которая перехватит исключение die. (строка eval также перехватывает исключение die и все ошибки компиляции).

Try :: Tiny применяется только к блочной форме eval, но следующее относится к обеим формам.

Каждый раз, когда вы звоните eval, это изменит значение $@. Это будет либо '', если eval завершился успешно, либо ошибка, обнаруженная eval.

Это означает, что каждый раз, когда вы вызываете eval, вы удаляете все предыдущие сообщения об ошибках. Try::Tiny локализует переменную $@ для вас, так что успешное вычисление не удалит сообщение предыдущего неудачного вычисления.

Другая ловушка возникает из-за использования $@ в качестве проверки, чтобы определить, удалось ли eval. Общий шаблон:

eval {...};
if ($@) {
   # deal with error here
}

Это основывается на двух предположениях: во-первых, любое сообщение об ошибке $@ может содержать истинное значение (обычно true) и что между блоком eval и оператором if нет кода.

Визуально, конечно, последнее верно, но если блок eval создал объект, и этот объект вышел из области видимости после сбоя eval, то метод DESTROY объекта будет вызван перед оператором if. Если DESTROY случайно вызовет eval без локализации $@, и это произойдет, то к тому времени, когда будет запущен оператор if, переменная $@ будет очищена.

Решение этих проблем:

my $return = do {
    local $@;
    my $ret;
    eval {$ret = this_could_fail(); 1} or die "eval failed: $@";
    $ret
};

разрывая эту строку за строкой, local $@ создает новый $@ для блока do, который предотвращает слипание предыдущих значений. my $ret будет возвращаемым значением оцененного кода. В блоке eval присваивается $ret, а затем блок возвращает 1. Таким образом, несмотря ни на что, если eval преуспеет, он вернет true, а если потерпит неудачу, вернет false Вам решать, что делать в случае неудачи. Приведенный выше код просто умирает, но вы можете легко использовать возвращаемое значение блока eval, чтобы принять решение запустить другой код.

Поскольку вышеприведенное заклинание немного утомительно, оно подвержено ошибкам. Использование такого модуля, как Try::Tiny, защищает вас от этих потенциальных ошибок, за счет еще нескольких вызовов функций за один eval. Важно знать, как правильно использовать eval, потому что Try::Tiny не поможет вам, если вам придется использовать строку eval.

11 голосов
/ 29 сентября 2011
6 голосов
/ 30 сентября 2011

В дополнение к ответам выше, я бы добавил ...

  • eval влияет глобальный обработчик $SIG{__DIE__}, вызывающий действие на расстоянии.
  • Новичку легко перепутать eval BLOCK и eval STRING, поскольку они, похоже, делают одно и то же, но одна из них - дыра в безопасности.

Try :: У Tiny есть свои подводные камни, самая большая из которых заключается в том, что, хотя он выглядит как блок, он на самом деле является вызовом подпрограммы. Это означает, что это:

eval {
    ...blah blah...
    return $foo;
};

и это:

try {
    ...blah blah...
    return $foo;
};

не делайте то же самое. Они изложены в разделе CAVEATS в Try :: Tiny docs . Тем не менее, я бы порекомендовал его более eval.

0 голосов
/ 05 марта 2016

Использование функции eval на X11 может по-прежнему не поддерживать работу.

Код похож на

eval {    
    @win_arrays = GetWindowsFromPid($pid);
};

Сценарий будет завершен с

XОшибка неудачного запроса: ...

...