Программисты C++, Java, C#… при написании кода на «чистом» C часто сталкиваются с проблемой отсутствия механизма исключений. Классический способ обработки ошибок в C — проверка кодов возврата. В сложных алгоритмах при глубокой вложенности это весьма неудобно. Приведенный ниже способ не нов, но, к сожалению, многие с ним не знакомы.
Итак, в POSIX есть пара полезных функций:
setjmp и
longjmp
#include <setjmp.h>
int setjmp(jmp_buf env);
void longjmp(jmp_buf evn, int val);
Рассмотрим пример:
#include <setjmp.h>
jmp_buf jbuf;
/* ...... */
/* Ставим обработчик */
int result = setjmp(jbuf);
switch(result) {
case 0: /* это блок try */
case 1: /* Один из блоков catch */
case 2: /* Еще один catch */
};
/* .... */
/* Здесь мы хотим кинуть исключение */
longjmp(jbuf, КОД_ИСКЛЮЧЕНИЯ);
Вызов setjmp сохраняет состояние стека в переменную. При сохранении setjmp возвращает 0. Эту переменную необходимо сделать доступной для кода, который будет инициировать переход. После вывода longjmp всё будет выглядеть так, как будто setjmp вернул отличное от 0 значение.
Что стоит отметить:
1) longjmp никогда не возвращает управление. Либо будет откат к setjmp, либо, если jmp_buf битый — segmentation fault.
2) Второй параметр longjmp — то значение, которое вернет setjmp по восстановлении стека. 0 передать не получится (в этом случае вернется 1).
3) Описанный выше механизм ломает «нормальную логику» работы программы на C. При этом в некоторых случая может сделать программу более читаемой. К этому стоит относиться как к goto.
UPDATE-замечание от mraleph:
Строго говоря setjmp не сохраняет «состояние стека». Он сохраняет лишь значения регистров, поэтому в частности jmp_buf'ом нельзя пользоваться, если функция, в которой он был создан завершилась…