ANSI C эквивалент попробовать / поймать?

У меня есть некоторый код 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 ответов

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

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

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

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

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

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

Есть классическая раскрутка gotoшаблон:

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 */
  }
 /* ... */
}

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

Один полезный стиль кодирования, который мне нравится использовать, заключается в следующем. Я не знаю, есть ли у него конкретное имя, но я столкнулся с ним, когда я занимался обратным проектированием некоторого ассемблерного кода в эквивалентный 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;
}

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

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

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

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

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

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

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

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

Другие вопросы по тегам