Сегодня я думал о блоках try / catch, существующих в других языках. Некоторое время гуглил, но безрезультатно. Насколько я знаю, в C. нет такой вещи, как try / catch. Однако есть ли способ их «смоделировать»?
Конечно, есть assert и другие уловки, но ничего вроде try / catch, которые также перехватывают возникшее исключение. Спасибо
103
Ответы:
Сам C не поддерживает исключения , но вы можете моделировать их в определенной степени с
setjmp
иlongjmp
вызовов.На этом веб-сайте есть хороший учебник о том, как моделировать исключения с помощью
setjmp
иlongjmp
источник
try{ x = 7 / 0; } catch(divideByZeroException) {print('divided by zero')};
он не сработает, верно?Вы используете goto в C для аналогичных ситуаций обработки ошибок.
Это ближайший эквивалент исключений, которые вы можете получить в C.
источник
goto
, больше используется для обработки ошибок, ну и что? Вопрос не в обработке ошибок как таковой, а явно в эквивалентах try / catch.goto
не является эквивалентом try / catch, поскольку он ограничен той же функцией.goto
качестве механизма try / catch, используемого в современном, широко признанном, рецензируемом источнике. Найдитеgoto
эквивалент «бросить» и эквивалентfinish
«поймать».Хорошо, я не мог удержаться от ответа на это. Позвольте мне сначала сказать, что я не думаю, что моделировать это на C - хорошая идея, поскольку это действительно чуждое для C.
Мы можем
использоватьзлоупотребляйте препроцессор и локальный переменные стека , чтобы дать использовать ограниченный вариант C ++ Try / броска / улова.Версия 1 (выбрасывает локальный прицел)
Версия 1 - это только локальный выброс (не может покинуть область действия функции). Он действительно зависит от способности C99 объявлять переменные в коде (он должен работать в C89, если попытка является первым делом в функции).
Эта функция просто создает локальную переменную, чтобы она знала, произошла ли ошибка, и использует goto для перехода к блоку catch.
Например:
Это работает примерно так:
Версия 2 (скачок прицела)
Версия 2 намного сложнее, но в основном работает так же. Он использует длинный переход от текущей функции к блоку try. Затем блок try использует if / else, чтобы пропустить блок кода к блоку catch, который проверяет локальную переменную, чтобы увидеть, должна ли она перехватывать.
Пример снова расширился:
При этом используется глобальный указатель, поэтому longjmp () знает, какая попытка была выполнена последней. Мы
используемзлоупотребляя стек так дочерние функции могут также попробовать / поймать блок.Использование этого кода имеет ряд недостатков (но это забавное умственное упражнение):
источник
bool __ErrorCheck(bool &e){bool _e = e;e=false;return _e;}
. Но локальная переменная также будет переопределена, поэтому ситуация немного выйдет из-под контроля.В C99вы можете использоватьsetjmp
/longjmp
для нелокального потока управления.В рамках одной области используется общий структурированный шаблон кодирования для C при наличии нескольких выделений ресурсов и нескольких выходов
goto
, как в этом примере . Это похоже на то, как C ++ реализует вызовы деструкторов автоматических объектов под капотом, и если вы будете усердно придерживаться этого, это должно позволить вам определенную степень чистоты даже в сложных функциях.источник
В то время как некоторые другие ответы касались простых случаев использования
setjmp
иlongjmp
, в реальном приложении есть две проблемы, которые действительно имеют значение.jmp_buf
сделает их неработоспособными.jmp_buf
вызовет в этой ситуации все виды боли.Решением для них является поддержка локального стека потока
jmp_buf
который будет обновляться по мере вашего продвижения. (Я думаю, это то, что lua использует для внутренних целей).Итак, вместо этого (из потрясающего ответа JaredPar)
Вы бы использовали что-то вроде:
Опять же, более реалистичная версия этого могла бы включать некоторый способ сохранения информации об ошибках в
exception_state
более удобную обработкуMAX_EXCEPTION_DEPTH
(возможно, с использованием realloc для увеличения буфера или чего-то подобного).ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: приведенный выше код был написан без какого-либо тестирования. Это чисто для того, чтобы вы получили представление о том, как структурировать вещи. Разные системы и разные компиляторы должны будут по-разному реализовать локальное хранилище потока. Код, вероятно, содержит как ошибки компиляции, так и логические ошибки, поэтому, пока вы можете использовать его по своему усмотрению, ПРОВЕРЬТЕ его перед использованием;)
источник
Быстрый поиск в Google дает беспорядочные решения, такие как этот которые используют setjmp / longjmp, как упоминали другие. Нет ничего более простого и элегантного, чем try / catch в C ++ / Java. Я сам неравнодушен к обработке исключений в Аде.
Проверьте все с помощью операторов if :)
источник
Это можно сделать с помощью
setjmp/longjmp
C. P99 имеет довольно удобный набор инструментов для этого, который также совместим с новой моделью резьбы C11.источник
Это еще один способ обработки ошибок в C, который более эффективен, чем использование setjmp / longjmp. К сожалению, это не будет работать с MSVC, но если можно использовать только GCC / Clang, вы можете рассмотреть его. В частности, он использует расширение «метка как значение», которое позволяет вам взять адрес метки, сохранить его в значении и безоговорочно перейти к нему. Я представлю это на примере:
При желании вы можете реорганизовать общий код в определения, эффективно реализовав свою собственную систему обработки ошибок.
Тогда пример становится
источник
Предупреждение: это не очень хорошо, но работает.
Использование:
Вывод:
Имейте в виду, что здесь используются вложенные функции и
__COUNTER__
. Вы будете в безопасности, если используете gcc.источник
Redis использует goto для имитации try / catch, IMHO это очень чисто и элегантно:
источник
errno
должен использоваться только сразу после неудачного системного вызова, а не тремя вызовами позже.В C вы можете «имитировать» исключения вместе с автоматическим «восстановлением объекта» посредством ручного использования if + goto для явной обработки ошибок.
Я часто пишу C-код, подобный следующему (сводится к тому, чтобы выделить обработку ошибок):
Это полностью стандартный ANSI C, который отделяет обработку ошибок от вашего основного кода, позволяет (вручную) раскручивать инициализированные объекты, как это делает C ++, и совершенно очевидно, что здесь происходит. Поскольку вы явно проверяете на наличие сбоев в каждой точке, это действительно упрощает добавление определенного журнала или обработки ошибок в каждом месте, где может произойти ошибка.
Если вы не возражаете против небольшой магии макросов, вы можете сделать это более кратким, делая другие вещи, например, записывая ошибки с трассировкой стека. Например:
Конечно, это не так элегантно, как исключения + деструкторы C ++. Например, вложение нескольких стеков обработки ошибок в одну функцию таким способом не очень удобно. Вместо этого вы, вероятно, захотите разбить их на автономные подфункции, которые аналогичным образом обрабатывают ошибки, инициализировать + завершить явно, как это.
Это также работает только в рамках одной функции и не будет продолжать прыгать вверх по стеку, если вызывающие объекты более высокого уровня не реализуют аналогичную явную логику обработки ошибок, тогда как исключение C ++ будет просто продолжать прыгать вверх по стеку, пока не найдет подходящий обработчик. Также он не позволяет вам генерировать произвольный тип, а вместо этого только код ошибки.
Систематическое кодирование таким образом (т.е. с одной точкой входа и одной точкой выхода) также упрощает вставку логики до и после («наконец»), которая будет выполняться несмотря ни на что. Вы просто помещаете логику "finally" после метки END.
источник
Если вы используете C с Win32, вы можете использовать его структурированную обработку исключений (SEH) для имитации попытки / отлова.
Если вы используете C на платформах, которые не поддерживают,
setjmp()
иlongjmp()
взгляните на эту обработку исключений библиотеки pjsip, она предоставляет свою собственную реализацию.источник
Возможно, это не основной язык (к сожалению), но в APL есть операция ⎕EA (расшифровывается как Execute Alternate).
Использование: 'Y' ⎕EA 'X', где X и Y - либо фрагменты кода, представленные в виде строк, либо имен функций.
Если X обнаруживает ошибку, вместо этого будет выполняться Y (обычно обработка ошибок).
источник