2010-09-21 2 views
25

У меня есть код C, с которым я работаю, и я нахожу ошибки, когда код работает, но мало информации о том, как сделать правильный try/catch (как на C# или C++).ANSI 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(); 
} 
+2

http://www.nicemice.net/cexcept/ что-то, что может быть полезно – anijhaw

+23

Этот код не является C, ansi или иным. C не имеет оператора '::' scope. –

+4

C не имеет механизма обработки исключений. Вся обработка ошибок обычно выполняется с возвращаемыми значениями и переменной errnum. Кстати, было бы неплохо получить некоторые подробные комментарии экспертов о том, как обработка ошибок выполняется правильно в C :) – Kel

ответ

25

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

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

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

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

+19

IME - обычный способ указания ошибок или сбоев в C - это возврат значения, которое игнорируется вызывающим. ': (' – sbi

+2

@sbi: IME, некоторые люди по-прежнему верят, что «вернуть значение» - это путь к C++, Java и C# тоже ... ': (' – paercebal

+0

@paercebal: Я знаю. ':(' – sbi

1

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

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

17

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

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

+0

Хмм, третий снизу сегодня. Какая-то конкретная причина для этого? Я думал, что это выглядит многообещающе. –

+0

Неужели кто-то тебя достал? –

+2

+1, потому что проголосовало неправильно ... Идеальная ссылка на вопрос – Hogan

0

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

+1

К тому времени, когда были введены исключения для C++, реализация C-препроцессора была историей. –

+0

@Nemanja: Это действительно так для оригинала cfront. [Comeau C++] (http://www.comeaucomputing.com), однако, все еще выводит код C. (Потому что это обеспечивает высокую переносимость.) Но я сомневаюсь, что это более полезно, чем сборщик любого другого компилятора : это машинный код, и как таковой не для потребления человеком. – sbi

+1

Я считаю, что исключения были соломой, которая сломала спину верблюда, где рассматриваемый верблюд был компилятором C++ cfront. См. Stroupstrup «Дизайн и эволюция C++». –

4

Существует классическая размотки 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; 

В качестве альтернативы вы можете поддельной его в ограниченной форме путем выделения кода «попробовать» в другой функции

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

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

+0

Eek!Это выглядит очень грязно для меня. Я предпочитаю метод, предложенный ниже. Не нужно переходить! – James

+0

'goto' - это способ пойти (извините за каламбур) в C. Однако, если несколько ярлыков выхода выглядят как overkill. Это чище (хотя и немного менее эффективно) иметь единую метку выхода и (если необходимо), чтобы проверить, какие ресурсы были выделены. – jamesdlin

+0

@jamesdlin: несколько ярлыков покупают вам что-то конкретное: каждый соответствует ресурсу, который может потребоваться освободить. Вы можете избежать их с условностями на выпусках, но в некоторых случаях (не для этого), которые требуют дополнительных флагов. Насколько я могу судить о вкусе. – dmckee

4

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

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

4

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

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

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