Strukture podataka i algoritmi
2.7.6 Kako manipulisati greskama u C-u?

Sta mozemo da uradimo u C-u?

Bez I/O funkcija

Ne stavljajte I/O komande kao printf u visestruko iskoristivom kodu niskog nivoa. Ako kod treba da se koristi vise puta, tada zelimo da funkcionise ispravno u svakom okruzenju: I/O komande su, u principu, najmanje prenosiv aspekt bilo kog programa. Njih treba ostaviti "visim" nivoima programa gde se sa sigurnoscu moze znati koje je operativno okruzenje programa.

Koristite vrednost funkcije za vracanje greske

Ako biste hteli da specificirate void funkciju, specificirajte je tako da vraca int ili jos bolje enum.

Cesto postoji vrednost koju funkcija moze da vrati, a koja se moze iskoristiti za oznacavanje greske. Konstruktori vracaju pointere na blokove memorije alocirane za objekat: NULL pointer oznacava gresku.

int f( ... ) {
    X a;
    a = ConsX( ... );
    if ( a != NULL ) {
        /* Nema greske */
        ....
        return 1;
        }
    else
        {
        /* vrati kod za gresku na visi nivo */
        return 0;
        }
    }

Nema slobodnih vrednosti funkcije?

Jedno moguce resenje je dodavanje pointera na kod greske za svaki metod:
/* vector.h */
typedef struct vector_t *vector;
typedef enum( BezGreske, PogresnaDimenzija, NemaMemorije, LosVektor )
                                 vector_error;

double AddVector( vector a, vector b, vector_error *error );

Implementacija:
#include "vector.h"
double DotProduct( vector a, vector b, vector_error *error ) {
    if ( LengthVector(a) == LengthVector(b) )
        {
        ....
        }
    else
        {
        *error = PogresnaDimenzija;
        return 0.0;
        }
  }
Ovo resenje bi se verovatno smatralo glomaznim, iako se njime postize cilj robustnog koda. Dodatni argument takodje povecava vreme izvrsenja.

Simulirajte obradjivac gresaka

setjmp/longjmp
U C-u postoji setjmp/longjmp mehanizam koji dozvoljava skok sa proizvoljnog mesta na proceduru. Doduse, iako bi bilo moguce koristiti ih, oni predstavljaju potencijalne probleme za prenosivost, pa ih je bolje izbegavati.
hvatanje signala
Unix programi mogu da postave hvatace signala. Ovaj mehanizam je napravljen tako da dozvoli roditeljskom procesu u Unix-u da nadgleda svoje potprocese, i on verovatno predstavlja suvisan trud kojim se upravlja necim sto i korisnik moze trivijalno da sredi - kao sto je suvise mnogo otvorenih prozora ili iskoriscenje sve raspolozive memorije!

Prava simulacija obradjivaca gresaka

Pristup koji prilicno dobro modelira obradjivace gresaka (exception handlers) u Adi, Javi ili C++ dodaje jos jednu funkciju svakoj klasi. Kao i u ovim jezicima, izuzetak moze da se "uhvati" na bilo kom odgovarajucem nivou u programu (gde je poznato dovoljno o okruzenju da bi se znalo kako treba odreagovati na greske) jednostavnim pozivanjem funkcije greske.
Dodajte funkciju greske u svaku klasu
Sa ovim pristupom, svakoj klasi dodajemo funkciju koja se moze pozvati sa odgovarajuceg nivoa da bi se utvrdilo da li je doslo do greske.
/* vector.h */
typedef struct vector_t *vector;
typedef enum( BezGreske, PogresnaDimenzija, NemaMemorije ) vector_error;

vector ConsVector( int dimension );
vector_error DeleteVector( vector a );
vector_error VectorError();

double DotProduct( vector a, vector b );
Implementacija je jednostavna:
#include "vector.h"
static vector_error ve = BezGreske;

vector_error VectorError() {
    vector_error err;
    err = ve;
    ve = BezGreske;
    return err;
    }

char *VectorErrorString( vector_error ve ) {
    switch( ve ) {
    	case BezGreske: return "Bez greske";
    	case PogresnaDimenzija: return "Pogresna dimenzija vektora";
        case NemaMemorije: return "Nedovoljno memorije za vektor";
    	default: return "Nepoznata greska za vektor";
    	}
    }

double DotProduct( vector a, vector b ) {
    if ( LengthVector(a) == LengthVector(b) ) {
        ....
        }
    else {
        ve = PogresnaDimenzija;
        return 0.0;
        }
    }
Primetite dodatni (opcioni) VectorErrorString metod koji daje razumljiv string sa objasnjenjem greske. Ovo je slicno perror() metodu koji na izlazu daje poslednju I/O gresku u obliku stringa. (Ali primetite da perror() krsi pravilo o visestrukoj iskoristivosti koje predlazemo ovde, s obzirom da se string stampa na standardnom izlazu, pa je stoga beskorisna u okruzenjima u kojima pojam stdout moze biti besmislen, npr. vecina GUI okruzenja.)

U ovom slucaju, metod VectorError() moze da se pozove sa bilo kog mesta u programu, nakon sto je pozvan neki vektorski metod, u trenutku kada je okruzenje dovoljno poznato da moze da se odredi kakvu akciju treba sprovesti kao odgovor na gresku.

double dp;
...
dp = DotProduct( a, b );
if( (ve = VectorError()) != Bez greske ) {
    printf("Greska sa skalarnim proizvodom: %s\n", VectorErrorString( ve ) );
    }
Dalje na Par napomena o C-u
Nazad na Sadrzaj
© John Morris, 1998. Prevod sa engleskog, Dragan Stevanovic, 2002.