Какие «хорошие» способы использовать longjmp / setjmp для обработки ошибок C? - PullRequest
19 голосов
/ 04 мая 2009

Я должен использовать C для одного проекта, и я думаю об использовании longjmp/setjmp для обработки ошибок, так как я думаю, что будет гораздо проще обрабатывать ошибки в одном центральном месте, чем коды возврата. Буду признателен, если есть какие-то советы о том, как это сделать.

Меня особенно беспокоит правильная очистка ресурса в случае возникновения такой ошибки.

Кроме того, как мне обрабатывать ошибки, которые приводят к их использованию в многопоточных программах?

Еще лучше, есть ли библиотека C, которая уже существует для обработки ошибок / исключений?

Ответы [ 6 ]

14 голосов
/ 04 мая 2009

Если вы беспокоитесь об очистке ресурса, вам нужно серьезно задуматься, являются ли longjmp () и setjmp () хорошей идеей.

Если вы спроектируете свою систему распределения ресурсов так, чтобы вы могли на самом деле точно очистить ее, тогда все в порядке - но такой дизайн бывает сложным и, как правило, неполным, если на самом деле стандартные библиотеки, используемые вашим кодом, сами выделяют ресурсы, которые должны быть освобождены. Это требует особой осторожности, и, поскольку оно не совсем надежно, оно не подходит для долго работающих систем, которым может понадобиться выдержать многократное использование вызовов setjmp () / longjmp () (они будут утекать, расширяться и в конечном итоге приводить к проблемы).

12 голосов
/ 04 мая 2009

Посмотрите на этот пример / учебник:
http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html

5 голосов
/ 04 мая 2009

В Symbian реализован механизм Leave в терминах longjmp(), и он служит хорошим обзором всего, что вам нужно сделать.

В Symbian есть глобальный «стек очистки», который вы нажимаете и высовываете вещи, которые хотите очистить в случае прыжка. Это ручная альтернатива автоматическому разматыванию стека, которое делает компилятор C ++ при возникновении исключения C ++.

У Symbian были «жгуты ловушек», в которые он мог бы выпрыгнуть; они могут быть вложенными.

(Symbian совсем недавно переопределил его в терминах исключений C ++, но интерфейс остается неизменным).

В целом, я думаю, что надлежащие исключения C ++ менее подвержены ошибкам кодирования и намного быстрее, чем переход на собственный эквивалент C.

(Современные компиляторы C ++ отлично справляются с исключениями «ноль накладных расходов», например, когда они не генерируются; longjmp() должен хранить состояние всех регистров и тому подобное, даже если переход не выполняется позднее, поэтому принципиально никогда не будет так быстро, как исключения.)

Использование C ++ в качестве лучшего C, где вы принимаете только исключения и RAII, было бы хорошим путем, если использование longjmp() для эмуляции исключений может быть заманчивым.

5 голосов
/ 04 мая 2009

Я только когда-либо нашел одно использование для setjmp()/longjmp(), и это не было связано с обработкой ошибок.

Там действительно нет необходимости использовать его для этого, так как он всегда может быть преобразован в нечто более простое для отслеживания. Использование setjmp()/longjmp() очень похоже на goto тем, что им легко злоупотреблять. Все, что делает ваш код менее читабельным, в целом является плохой идеей. Обратите внимание, что я не говорю, что они изначально плохие, просто они могут привести к плохому коду проще, чем альтернативы.

FWIW, единственное место, где они были неоценимы, был проект, который я делал в первые дни индустрии (период времени MS-DOS 6). Мне удалось собрать совместную многопоточную библиотеку с использованием Turbo C, которая использовала эти функции в функции yield() для переключения задач.

Я почти уверен, что не касался их (или не нуждался) с тех дней.

0 голосов
/ 12 августа 2014

Я использовал setjmp / longjmp достаточно аккуратно, чтобы убежать от обратного вызова, не согласовывая свой путь через различные другие уровни библиотеки.

В этом случае (если я правильно помню) был случай, когда код в синтаксическом анализаторе, сгенерированном yacc, мог обнаружить (не синтаксическую) проблему и хотел отказаться от синтаксического анализа, но предоставить вызывающему пользователю достаточно полезный отчет об ошибке другая сторона всего кода, сгенерированного YACC. Другой пример был внутри обратного вызова, вызванного из синтаксического анализатора Expat. В каждом случае были другие способы сделать это, но они казались более громоздкими и неясными, чем просто выручка таким образом.

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

Делаете это в контексте многопоточного программирования? Я уверен, что это не невозможно, но Ооо: достань свою семейную пачку аспирина сейчас. Вероятно, разумно держать пары setjmp / longjmp как можно ближе друг к другу. Пока подходящая пара setjmp / longjmp находится в одном потоке, я ожидаю, что вы будете в порядке, но ... будьте осторожны.

0 голосов
/ 04 мая 2009

Исключения намного лучше общего механизма, но в глубокие темные времена прошлого C я написал эмулятор процессора, который включал командную оболочку. Оболочка, используемая для setjmp / longjmp для обработки прерываний (т. Е. Процессор работает, и пользователь нажимает break / ctrl-c, код перехватывает SIGINT и longjmps обратно в оболочку).

...