Как выполнить обработку исключений на процедурном языке, таком как C или Perl? (Я знаю, что Perl также занимается объектно-ориентированным программированием.) Как лучше всего обрабатывать исключения в процедурном коде Perl?
Как обрабатывать исключения на процедурном языке?
Ответы (6)
В Perl 5 обработка исключений выполняется с помощью eval и die. Вы просто оцениваете тело кода, и если оно умирает, вы можете проверить $@ на наличие ошибки. Это не так просто, если вы хотите сделать это правильно, поэтому существуют различные модули try/catch. Вас может заинтересовать Try::Tiny, который не имеет зависимостей и описывает все ловушки, с которыми вам приходится сталкиваться при использовании наивной обработки исключений eval. (См. также эту запись в блоге автора Try::Tiny.)
'with eval' => &with_eval нужен `` перед подименем. В моей системе perl 5.8 два слоя eval дают 44% штраф по сравнению с отсутствием eval, а один слой дает 24% штраф. Однако я думаю, что это может быть несправедливым критерием. Обертывание запроса к базе данных или доступа к файлу в eval не будет иметь таких накладных расходов, поскольку эти операции вряд ли будут привязаны к процессору. Я думаю, что урок, который нужно усвоить, заключается в том, что вы можете зайти слишком далеко со своими оценками. Оберните куски кода разумного размера, и вы не будете тратить все свое время на создание и удаление контекстов eval.
- person daotoad; 16.09.2009
Вот пример того, как я делаю исключения в Perl (без использования одного из модулей Try):
use Carp;
use English qw( -no_match_vars );
do_something_needing_rollback_if_failed();
eval {
do_something_dangerous();
} or do {
# Exception was thrown by dangerous method
# Save the error:
my $error = $EVAL_ERROR;
# Try to rollback
eval { rollback(); }
or do { confess qq{Couldn't rollback: $EVAL_ERROR. Original error $error}; }
# Let's rethrow:
confess qq{Rolled back! Error was $error};
}
Одна из наиболее раздражающих частей обработки исключений Perl заключается в том, что она использует одну переменную для хранения любой ошибки исключения, и она может быть случайно перезаписана, поэтому требуется некоторое защитное кодирование.
Это скорее зависит от того, что вы подразумеваете под «обработкой исключений».
Некоторые ОС имеют механизмы обработки исключений — см. процедуры обработки исключений Windows или обработчики сигналов Linux (некоторые из них являются исключениями).
Если вы имеете в виду идиому в пользовательском коде, чтобы сигнализировать об ошибке и раскрутить стек, очищая все выделенные объекты, тогда код C не вызывает деструкторы для выделенных объектов, когда стек раскручивается (он просто освобождает память), так что это нормально в C для функций, возвращающих значение состояния, и для вызывающего кода, выполняющего любую необходимую очистку перед возвратом.
Я не очень хорошо знаю Perl, но поиск в Google «обработки исключений Perl» показывает, что он имеет как встроенные механизмы, эквивалентные try/catch, так и модули, которые обеспечивают обработку исключений в «стиле OO».
В C были setjmp и longjump; по сути, setjmp() сохраняет текущий контекст в стеке, а longjmp() может вернуться к точке, сохраненной setjmp(). Запись в Википедии об этом прекрасно описывает детали.
В Perl, как обычно, есть несколько способов сделать это. Однако сообщество разработчиков в целом установило правильный способ делать большинство вещей.
Я сам становлюсь поклонником Exception::Base.
Если вам нужна более легкая реализация, просто используйте Carp.
Вы перехватываете исключения, используя конструкцию eval { ... }; if ($@) { ... }.
С помощью Exception::Base он предлагает вам способ абстрагироваться от конструкции if ($@) { ... }. Однако вам все равно нужно будет использовать eval { ... }.
Традиционный подход состоит в том, чтобы... ну... не :)
Как правило, люди просто проверяют статус после попытки что-то сделать, и если что-то пошло не так, они очищаются и возвращают код ошибки, а не продолжают. Эта проверка и возврат ошибок распространяется вниз по стеку вызовов функций до тех пор, пока они не вернутся к чему-то более высокому уровню в вашей программе, где вы хотите проинформировать пользователя, а не просто возвращать больше ошибок:
int try_it() {
if (!do_something(...)) {
return TRYIT_FAILURE;
}
}
void my_gui() {
rc = try_it()
if (rc == TRYIT_FAILURE) {
message_box("failed when trying it.", MB_ABORT|MB_RETRY);
}
...
}
Вы также можете сделать это в большой функции, используя вложенные ifs, если вы хотите что-то вроде конструкции try...except:
if (stage1()) {
if (stage2()) {
if(stage3()) {
printf("success!\n");
} else {
// error handling for stage 3
}
} else {
// error handling for stage 2
}
} else {
// error handling for stage 1
}
И вы можете сделать то же самое с gotos, если вы немного злитесь.
Тем не менее, вы можете создавать настоящие исключения, по крайней мере, в C. C имеет два стандартных библиотечных вызова для такого рода вещей: setjmp и longjmp. С их помощью вы можете вернуться через несколько вызовов функций к заранее определенному месту, где, как вы знаете, произошло исключение (переход). Подробнее об этом см. Setjmp.h#exception_handling в Википедии.
Кажется, в Perl есть способ сделать это, хотя мне он кажется далеко не интуитивным. Опять же, я не пишу код на Perl :)
Часто задаваемые вопросы по Perl Q4.8
C— это процедурный язык, и самое близкое, что вы можете получить, — это практиковать и применять методологию абстрактных типов данных. - person Axeman   schedule 15.09.2009