Каковы общие ошибки при использовании Perl eval?

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

Ответы

Ответ 1

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

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

При каждом вызове eval он изменит значение [email protected]. Это будет либо '', если eval преуспел, либо ошибка, улавливаемая eval.

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

Другая ошибка связана с использованием [email protected] в качестве проверки, чтобы определить, удалось ли eval. Общий шаблон:

eval {...};
if ([email protected]) {
   # deal with error here
}

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

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

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

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

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

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

Ответ 3

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

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

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

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

и это:

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

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

Ответ 4

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

Код похож на

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

script будет выведен из

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