Реализация механизма исключений средствами языка C

    Программисты 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'ом нельзя пользоваться, если функция, в которой он был создан завершилась…

    Комментарии 8

      +1
      *вздохнул*
      где бы найти программиста на чистом С, так чтоб ещё и плюсы знал…

      (ищу)
        +2
        К сожалению, это не решит проблему не-вызова free для всех malloc'ов.
          0
          Не решит. В++ динамическая память при обработке исключений тоже не чистится. Разве что оборачивать указатели.
          +1
          Чем то это мне напоминает вырывание гланд через ж… :-)
          Если пишем на С, то надо сжать зубы, и писать нормально, мерясь со всеми ограничениями :-)
            +2
            Вызов setjmp сохраняет состояние стека


            Строго говоря setjmp не сохраняет «состояние стека». Он сохраняет лишь значения регистров, поэтому в частности jmp_buf'ом нельзя пользоваться, если функция, в которой он был создан завершилась…
              0
              Вы правы, это важно
              0
              из той же оперы sourceforge.net/projects/cexception/
                0
                ай-ай-ай :) какие setjmp/longjmp для эксепшенов? После этого ++ники и говорят, что у сишников код тормознее…
                libunwind и вперёд.

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                Самое читаемое