ANSI C эквивалент попробовать / поймать? - PullRequest
25 голосов
/ 21 сентября 2010

У меня есть некоторый код C, с которым я работаю, и я нахожу ошибки, когда код выполняется, но мало информации о том, как правильно выполнить попытку / перехват (как в C # или C ++).

Например, в C ++ я бы просто сделал:

try{
//some stuff
}
catch(...)
{
//handle error
}

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

Вот код, с которым я работаю (довольно простой, рекурсивный метод) и который я хотел бы обернуть при помощи try / catch (или эквивалентной структуры обработки ошибок).

Однако мой главный вопрос - просто как сделать попытку / поймать в ANSI C ... реализация / пример не должны быть рекурсивными.

void getInfo( int offset, myfile::MyItem * item )
{
    ll::String myOtherInfo = item->getOtherInfo();
    if( myOtherInfo.isNull() )
        myOtherInfo = "";
    ll::String getOne = "";
    myfile::Abc * abc = item->getOrig();
    if( abc != NULL )
    {
        getOne = abc->getOne();
    }
    for( int i = 0 ; i < offset ; i++ )
    {
             printf("found: %d", i);
    }
    if( abc != NULL )
        abc->release();
    int childCount = item->getChildCount();
    offset++;
    for( int i = 0 ; i < childCount ; i++ )
        getInfo( offset, item->getChild(i) );
    item->release();
}

Ответы [ 8 ]

25 голосов
/ 21 сентября 2010

Как правило, нет.

Можно использовать setjmp и longjmp для создания чего-то похожего на try / catch, хотя в C нет такого понятия, как деструкторы или разматывание стека, поэтому о RAII не может быть и речи. Вы могли бы даже приблизить RAII с помощью так называемого «стека очистки» (см., Например, Symbian / C ++), хотя это не очень близкое приближение и много работы.

Обычный способ указать на ошибки или ошибки в C - это вернуть значение, указывающее статус успеха. Вызывающие стороны проверяют возвращаемое значение и действуют соответственно. Посмотрите, например, стандартные функции C: printf, read, open, чтобы узнать, как определить ваши функции.

При смешивании кода C и C ++ вы должны убедиться, что исключение C ++ никогда не достигнет кода C. При написании функций C ++, которые будут вызываться из C, ловите все.

17 голосов
/ 21 сентября 2010

C не поддерживает обработку исключений.

Информация об одном подходе к этой проблеме здесь .Это показывает простой setjmp/longjmp подход, но также предоставляет более сложную альтернативу, подробно рассмотренную.

4 голосов
/ 28 апреля 2011

Это моя реализация системы обработки исключений в C: exceptions4c .

Он работает на макросах, построен на основе setjmp и longjmp и является 100% переносимым ANSI C.

Там вы также можете найти список всех различных реализаций, которые я знаю.

4 голосов
/ 21 сентября 2010

Один из полезных стилей кодирования, который мне нравится использовать, заключается в следующем. Я не знаю, есть ли у него конкретное имя, но я столкнулся с ним, когда я занимался обратным проектированием некоторого ассемблерного кода в эквивалентный C-код. Вы теряете уровень отступа, но для меня это не так уж важно. Не упустите рецензента, который укажет на бесконечный цикл! :)

int SomeFunction() {
    int err = SUCCESS;

    do {
        err = DoSomethingThatMayFail();
        if (err != SUCCESS) {
            printf("DoSomethingThatMayFail() failed with %d", err);
            break;
        }

        err = DoSomethingElse();
        if (err != SUCCESS) {
            printf("DoSomethingElse() failed with %d", err);
            break;
        }

        // ... call as many functions as needed.

        // If execution gets there everything succeeded!
        return SUCCESS;
    while (false);

    // Something went wrong!
    // Close handles or free memory that may have been allocated successfully.

    return err;
}
4 голосов
/ 21 сентября 2010

Классическая раскрутка goto s:

FILE *if = fopen(...);
FILE *of = NULL;
if (if == NULL) return; 

of = fopen(...);
if (of == NULL) goto close_if;

/* ...code... */
if (something_is_wrong) goto close_of;

/* ... other code... */

close_of:
  fclose(of);
close_if:
  fclose(if);

return state;

В качестве альтернативы вы можете подделать его ограниченным способом, изолировав код "try" в другой функции

int try_code(type *var_we_must_write, othertype var_we_only_read /*, ... */){
  /* ...code... */
  if (!some_condition) return 1;
  /* ...code... */
  if (!another_condition) return 2;
  /* ...code... */
  if (last_way_to_fail) return 4;
  return 0;
}

void calling_routine(){
  /* ... */
  if (try_code(&x,y/*, other state */) ) {
     /* do your finally here */
  }
 /* ... */
}

но ни один из подходов не является полностью эквивалентным. Вы должны управлять всеми ресурсами самостоятельно, вы не получите автоматический откат до тех пор, пока не будет найден обработчик, и так далее ...

2 голосов
/ 21 сентября 2010

Вы можете найти возможную реализацию с longjmp в этой книге: Интерфейсы и реализации C: методы создания программного обеспечения многократного использования - Дэвид Хансон

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

1 голос
/ 21 сентября 2010

Если вы хотите сделать многоуровневый прыжок, посмотрите setjmp() и longjmp().Их можно использовать как примитивное исключение.Функция setjmp() устанавливает место возврата и возвращает значение состояния.Функция longjmp() отправляется на место возврата и предоставляет значение состояния.Вы можете создать функцию catch, вызвав ее после setjmp() в зависимости от значения состояния.

По какой-либо причине не используйте их в C ++.Они не выполняют разматывание стека и не вызывают деструкторы.

0 голосов
/ 21 сентября 2010

Поскольку C ++ изначально был реализован как препроцессор C и имел Try / Catch, вы можете переделать работу Бьярна Страуструпа и написать препроцессор для этого.

...