2010-03-09 11 views
11

Czasami muszę napisać kod, który zmienia naprzemiennie między robieniem rzeczy a sprawdzaniem warunków błędu (np. Wywołanie funkcji biblioteki, sprawdzenie jej wartości zwracanej, kontynuowanie pracy). Prowadzi to często do długich, gdzie rzeczywista praca się dzieje w warunkach if, jakdobry styl w przypadku sprawdzania wielu wartości zwracanych

if(! (data = (big_struct *) malloc(sizeof(*data)))){ 
    //report allocation error 
} else if(init_big_struct(data)){ 
    //handle initialization error 
} else ... 

Jak macie pisać tego rodzaju kodu? Sprawdziłem kilka przewodników stylu, ale wydają się bardziej zainteresowani zmiennym nazywaniem i białymi znakami.

Linki do przewodników po stylach są mile widziane.

Edycja: w przypadku, gdy nie jest to jasne, jestem niezadowolony z czytelności tego stylu i szukam czegoś lepszego.

+1

Będąc przy tym, osobiście porzuciłbym obsadę '(big_struct *)', chyba że do kompilacji tego kodu wymagane jest użycie kompilatora C++. Jest to problem sporny, ale jeśli nie masz żadnych wymagań C++, myślę, że najlepiej go opuścić. –

Odpowiedz

15

Chociaż trudno mi to powiedzieć, może to być przypadek dla nie-popularnego goto. Oto jeden link, który znalazłem na ten temat: http://eli.thegreenplace.net/2009/04/27/using-goto-for-error-handling-in-c/

+0

Jeśli nie chcesz widzieć 'goto' w swoim kodzie, zawsze możesz je ukryć. http://stackoverflow.com/questions/2314066/do-whilefalse – jschmier

+1

+1 Używanie goto jest nadal najlepszą metodą w tym okropnym języku. – Tronic

+1

Tak - jest to dobra praktyka dla kodu C (w wyjątkach użycia kodu C++ i RAII). ustaw zasoby ptrs na NULL; ustaw wtedy, gdy otrzymasz i uzyskasz wspólny punkt wyjścia, który zwalnia jeśli! NULL. Jedynym minusem jest to, że naprawdę umieszczasz wszystkie swoje zmienne zasobów w najwyższym zakresie funkcji (wolę deklarować jak lokalnie, jak to możliwe). – pm100

13

zazwyczaj piszę ten kod w ten sposób:

data = (big_struct *) malloc(sizeof(*data)); 
if(!data){ 
    //report allocation error 
    return ...; 
} 

err = init_big_struct(data); 
if(err){ 
    //handle initialization error 
    return ...; 
} 

... 

w ten sposób uniknąć wywoływania funkcji wewnątrz jeśli i debugowania jest łatwiejsze, ponieważ można sprawdzić wartości zwracanych.

3

Nie używaj assert w kodzie produkcyjnym.
W trybie debugowania, assert nigdy nie powinny być wykorzystywane do czegoś, co w rzeczywistości może się zdarzyć (jak malloc powrocie NULL), a to powinno być stosowane w nieprawdopodobnych wypadkach (jak indeks tablicy jest poza zakresem w C)

Read this post for more.

2

Jedną z metod, z której miałem wielki wpływ, jest metoda używana przez W. Richarda Stevensa w Unix Network Programming (kod jest do pobrania here. W przypadku typowych funkcji, których oczekuje się, że cały czas się powiedzie, i nie ma możliwości odwołania się do awarii, zawija je , używając dużej litery (kod skompresowany pionowo):

void * Malloc(size_t size) { 
    void *ptr; 
    if ((ptr = malloc(size)) == NULL) 
     err_sys("malloc error"); 
    return(ptr); 
} 

err_sys tutaj wyświetla błąd, a następnie wykonuje exit(1). W ten sposób możesz po prostu zadzwonić do Malloc i wiedzieć, że wystąpi błąd, jeśli wystąpi problem.

UNP jest nadal jedyną książką, w której myślę, że autor ma kod, który sprawdza wartości zwracane przez wszystkie funkcje, które mogą zawieść. Każda inna książka mówi "powinieneś sprawdzić wartości zwracane, ale zostawimy to, abyś zrobił to później".

+2

Należy zauważyć, że chociaż takie rozwiązanie jest często dobre dla samodzielnych programów, jest to bardzo zły pomysł radzić sobie z błędami w ten sposób w bibliotekach - nigdy nie powinny one niespodziewanie przerwać. –

0

bywam

  • sprawdzanie błędów Delegat do wrapper funkcje (jak Stevens)
  • Po błędzie symulować wyjątki używając longjmp. (I faktycznie korzysta Dave Hansona C Interfaces and Implementations symulować wyjątki.)

Inną opcją jest użycie Don Knuth na literate programming zarządzać kod błędu obsługi lub jakiś inny rodzaj preprocesora.Ta opcja jest dostępna tylko w przypadku ustawiania reguł dotyczących twojego sklepu :-)

0

Jedyną właściwością grupującą takiego kodu jest to, że po prostu jest narzucona zewnętrznie sekwencja, którą musi przestrzegać. Właśnie dlatego przypisujesz te alokacje do jednej funkcji, ale jest to bardzo słaba wspólność. Dlaczego niektórzy ludzie zalecają porzucić zakres korzyści zagnieżdżonych, jeśli jest poza moim zrozumieniem. Skutecznie próbujesz umieścić szminkę na świni (nie ma zamiaru zniewagi) - natura kodu nigdy nie da niczego czystego, najlepiej, jak możesz użyć pomocy kompilatora do złapania błędów (konserwacji). Przyklej z if's IMHO.

PS: jeśli jeszcze cię nie przekonałem: jak wygląda goto-rozwiązanie, jeśli musisz podejmować trójstronne decyzje? Jeśli na pewno zrobi się brzydsza, ale goto ???

Powiązane problemy