2010-09-21 17 views
25

Mam trochę kodu C, nad którym pracuję, i znajduję błędy, gdy kod jest uruchomiony, ale mam mało informacji o tym, jak zrobić prawidłowy try/catch (jak w C# lub C++).ANSI C odpowiednik try/catch?

Na przykład w C++ chcę tylko zrobić:

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

ale w ANSI C jestem nieco zagubiony. Próbowałem wyszukiwać w Internecie, ale nie widzę wystarczająco dużo informacji o tym, jak to zrobić. Pomyślałem, że zapytam, czy ktoś może wskazać mi właściwy kierunek.

Oto kod, nad którym pracuję (dość prosta, metoda rekursywna) i chciałbym zawrzeć z try/catch (lub równoważną strukturą obsługi błędów).

Jednak moim głównym pytaniem jest po prostu jak spróbować/catch w ANSI C ... implementacja/przykład nie musi być rekursywna.

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/ coś, co może być przydatne – anijhaw

+23

Ten kod nie jest C, ansi lub w inny sposób. C nie ma operatora zasięgu '::'. –

+4

C nie ma mechanizmu obsługi wyjątków. Wszystkie operacje na błędach są zwykle wykonywane z wartościami zwracanymi i zmienną errnum. Przy okazji, dobrze byłoby uzyskać szczegółowe komentarze ekspertów na temat prawidłowego postępowania z błędami w języku C :) – Kel

Odpowiedz

25

Generalnie nie.

Możliwe jest użycie setjmp i longjmp do zbudowania czegoś dość podobnego do try/catch, chociaż nie ma czegoś takiego w C jak destruktory lub rozwijanie stosu, więc RAII nie wchodzi w grę. Możesz nawet zbliżyć RAI do tak zwanego "stosu oczyszczania" (patrz na przykład Symbian/C++), chociaż nie jest to bardzo bliskie przybliżenie i to dużo pracy.

Najczęstszym sposobem wskazywania błędów lub niepowodzenia w C jest zwracanie wartości wskazującej status sukcesu. Dzwoniący sprawdzają wartość zwracaną i podejmują odpowiednie działania. Zobacz na przykład standardowe funkcje języka C: printf, read, open, w celu uzyskania pomysłów na określenie funkcji.

Podczas mieszania kodu C i C++ należy upewnić się, że wyjątek C++ nigdy nie osiągnie kodu C. Podczas pisania funkcji w C++, które będą wywoływane z C, przechwytuj wszystko.

+19

IME Najczęstszym sposobem wskazywania błędów lub niepowodzenia w C jest zwracanie wartości ignorowanej przez dzwoniącego. ': (' – sbi

+2

@sbi: IME, niektórzy ludzie nadal uważają, że "zwracanie wartości" jest sposobem na przejście w C++, Java i C#, too ... ': (' – paercebal

+0

@paercebal: Wiem. ':(' – sbi

1

Jeśli chcesz wykonać skok na wielu poziomach, wyszukaj wartości setjmp() i longjmp(). Mogą być używane jako prymitywny rzut wyjątków. Funkcja setjmp() ustawia powrót do miejsca i zwraca wartość statusu. Funkcja longjmp() przechodzi do miejsca powrotu i podaje wartość statusu. Możesz utworzyć funkcję catch, wywołując ją po setjmp() w zależności od wartości statusu.

Nie, z jakiegokolwiek powodu, używaj ich w C++. Nie stosują odwijania stosu lub wywoływania destruktorów.

17

C nie obsługuje obsługi wyjątków.

Dostępne są informacje na temat jednego podejścia do tego problemu: here. To pokazuje proste podejście setjmp/longjmp, ale zapewnia także bardziej wyrafinowaną alternatywę, która jest szczegółowo omawiana.

+0

Hmm, trzecia dzisiejsza dezaprobata, czy jest jakiś szczególny powód tego ?, myślałem, że to wygląda obiecująco –

+0

Czy ktoś cię pochwalił? –

+2

+1, ponieważ głosowanie w dół było błędne ... Idealne łącze do pytania – Hogan

0

Ponieważ C++ był pierwotnie zaimplementowany jako pre-procesor C i miał funkcję Try/Catch, można było ponownie wykonać pracę Bjarne Stroustrup i napisać do tego procesor wstępny.

+1

Do czasu wprowadzenia wyjątków do C++, implementacja preprocesora C była historią: –

+0

@Nemanja: To prawda w przypadku oryginału cfront. [Comeau C++] (http://www.comeaucomputing.com) jednak nadal generuje kod C. (Ponieważ to powoduje dużą przenośność.) Ale wątpię, czy jest to już użyteczne, niż wyjście asemblera jakiegokolwiek innego kompilatora. : jest to kod wygenerowany maszynowo i jako taki nie jest przeznaczony do spożycia przez ludzi. – sbi

+1

Sądzę, że wyjątkami była słoma, która przełamała grzbiet wielbłąda, gdzie wielbłąd, o którym mowa, był kompilatorem C++. Zobacz Stroupstrup "Design & Evolution of C++". –

4

Jest to klasyczny odwijania goto s wzór:

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; 

Alternatywnie można udawać w ograniczony sposób przez izolowanie „Try” kod w innej funkcji

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

ale ani podejście jest w pełni równoważny. Masz do zarządzania wszystkimi zasobami siebie, nie dostaniesz automatycznego wycofywania aż do obsługi zostanie znaleziony, i tak dalej ...

+0

Eek!To wygląda na naprawdę nieporządne. Wolę moją metodę, którą zasugerowałem poniżej. Nie ma potrzeby goto! – James

+0

'goto' jest drogą do przebycia (przepraszam za kalambur) w C. Posiadanie wielu etykiet wyjścia wydaje się jednak przesadą. Jest czystszy (choć nieco mniej efektywny), aby mieć etykietę wyjścia i (jeśli to konieczne), aby sprawdzić, które zasoby zostały przydzielone. – jamesdlin

+0

@jamesdlin: wiele etykiet kupuje coś konkretnego: każdy odpowiada zasobowi, który może wymagać zwolnienia. Możesz ich uniknąć warunkowymi wersjami, ale w niektórych przypadkach (nie tym), które wymagają dodatkowych flag. Sprawa smaku, o ile wiem. – dmckee

4

Jeden przydatny styl kodowania, który lubię używać, jest następujący. Nie wiem, czy ma konkretną nazwę, ale natknąłem się na nią, gdy byłem inżynierią wsteczną, tworząc jakiś kod zespołu w równoważnym kodzie C. Tracisz poziom wcięcia, ale nie jest to dla mnie wielka sprawa. Uważaj na recenzenta, który wskaże nieskończoną pętlę! :)

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

Nice. Możesz potrzebować zagnieżdżonych instancji, jeśli chcesz się zrelaksować w sposób warunkowy. – dmckee

4

To jest moja implementacja systemu obsługi wyjątków w C: exceptions4c.

Jest zasilany przez makr, zbudowany na szczycie setjmp i longjmp i to jest 100% przenośny ANSI C.

There można również znaleźć listę wszystkich różnych implementacjach znam.