Az DBase nyelv család

Kivételkezelés

Hibakezelés

Clipper:

Az 1997.ben bevezetett 5.0-s verziójától a Clipper nyelvben is volt lehetőség hibakezelése. Be lett vezetve az ERROR hibaobjektum osztály, amely nem valódi osztály, csak egy adattípus feldekorálva, melyek segítségével objektum jellegűen lehet kezelni az ilyen típusú változókat.

Az ERROR fontosabb adattagjai:

Args: a hibához köthető adatok egy tömbben.
Description: a leírása emberek számára feldolgozható módon (a hiba szöveges leírása).
FileName: a hibához köthető fájl neve.
GenCode a hiba leírása számítógép által feldolgozható módon (Clipper hibakód).
Operation: a hibát kiváltó művelet.
Severity: a hiba súlyossága. Lehetséges értékei és jelentésük:
0: nem definiált.
1: figyelmeztetés, nem kritikus hiba.
2: hiba.
3: végzetes hiba, ami azonnali kilépést eredményez.
SubCode: operációs rendszer hibakódja.

A hibakezelés felépítése

A Clipper hibakezelésnek a működése a mai programnyelek TRY-CATCH hibakezeléséhez hasonló felépítésű (nincs a FINALLY-val párhuzamba állítható nyelvi elem). Ez több nyelvi elem kombinálásával alakul ki:

A hibakezelés felépítése:

BEGIN SEQUENCE
  // egyéb CLIPPER utasítások
  ...
  BREAK [kifejezés]  //Ez történhet egy alprogramban is
  ...
  // egyéb CLIPPER utasítások
[RECOVER [USING oError]
  //hibakezelő utasítások]
END SEQUENCE>

A BEGIN SEQUENCE - [RECOVER] - END SEQUENCE utasításblokk konstrukcióban a RECOVER opcionális. Ha a szekvencia hibakezelés céllal van létrehozva, akkor kell, ha hiba elnyelési céllal, akkor nem kell.

A működési logikája:

Amennyiben a program egy utasításblokkban egy BREAK utasításhoz ért, akkor attól függően, hogy van-e RECOVER az utasításblokk hátralevő részében vagy nem, ha van, akkor a program vezérlése az adott utasításblokk RECOVER utasítástól folytatódik, ha nem, akkor az utasításblokk végétől. Ha a BREAK mögött meg van adva egy kifejezés, akkor a RECOVER USING használatával az egy változóban átvehető. Ha a RECOVER-re nem egy BREAK utasításról érkezett a vezérlés, akkor a RECOVER-nél a vezérlés az END SEQUENCE utasításra kerül, a köztük levő utasításokat figyelmen kívül hagyva.

A rendszerben tárolva van egy dedikált hibakezelő kódblokk, amire rákerült a vezérlés minden fellépő hiba esetén. Ez paraméterben megkapja a fellépett hibát leíró ERROR objektumot. Ezt a kódblokkot az ERROBLOCK függvénnyel lehet lekérni illetve beállítani is ezzel lehet új értéket. Ennek a szignatúrája:
ERRORBLOCK([bNewErrorBlock]) -> bLastErrorBlock

Hibakezeléshez be lehetett ennek a hibakezelő kódblokknak az értékét állítani a {|x| BREAK(x)} kódblokkra. Ekkor hiba esetén a kapott hiba objektummal azonnal elugrik a legközelebbi RECOVER-re vagy END SEQUENCE-re. Emiatt a viselkedés miatt fontos a hibakezelésnél a RECOVER használata: amennyiben nincsen RECOVER adva egy utasítás blokkban, akkor a BREAK hatására az END SEQUENCE-re ugik és a kiváltott hiba elnyelődik.

Az alapértelmezett hibakezelési kódblokk hatására a SEQUENCE nyelvi konstrukcióval elérhető volt, hogy a feladatot megvalósító programrész a BEGIN SEQUENCE és RECOVER közé kerüljön, a hibakezelés a RECOVER és az END SEQUENCE közé.

Kiegészítés az alapvető hibakezelési konstrukcióhoz

Az ERRORBLOCK függvénnyel át lehet állítani a hibakezelést tetszőleges kódblokkra. Megtehető, hogy akár egy saját függvényünk fusson le (pl. a hiba kinaplózása miatt, majd utána mi adjuk ki a BREAK utasítást).

Mivel a hibakezelő kódblokk a fentiek miatt bármire lehet állítva, ha speciálisan az alapértelmezett hibakezelő kódblokk funkcionalitását szeretnénk garantáltan használni, akkor szükséges beállítani azt. Azért, hogy csak az aktuális függvényre és az onnan hívottakra érvényesüljön ez a módosításunk, le kell menteni a BEGIN SEQUENCE előtt, majd az END SEQUENCE után visszaállítani a korábbi (általunk nem ismert funkcionalitású) hibakezelő kódblokkot. Ezzel elérhető, hogy egy függvényhívás ilyen szempontból a környezet viselkedését ne változtassa meg.
Ez a gyakorlatban így néz ki:

local bPrevErrorBlock

//A kívánt hibakezelés beállítása, korábbi lementése
bPrevErrorBlock := ERRORBLOCK({|oError| BREAK(oError)})

BEGIN SEQUENCE
  //Ide kerül a futtatott kód

RECOVER USING oError
  //Ide kerül a hibakezelés

END SEQUENCE

//Korábbi hibakezelés visszaállítása
ERRORBLOCK(bPrevErrorBlock)

FoxPro 6.0:

Bár nem deklarálhatunk kivételeket, ennek ellenére lehetoségünk van a hibák kezelésére. Ennek eszköze: ON ERROR és ON READERROR parancsok. Hiba jelentkezése esetén a paraméterként megadott utasítás vagy alprogram kerül végrehajtásra.

Ez paraméterként megkaphatja

A hibakezelõ függvénybõl RETRY-al visszatérve, a hibát kiváltó parancsot újra megpróbálja végrehajtani. (Használhatjuk például adatbekérésnél ellenõrzésre.)

A program futása során akárhányszor használhatjuk az ON ERROR és ON READERROR parancsokat, mindig az éppen aktuális hibakezelõ fog lefutni. (Paraméter nélkül írva az alapértelmezés lép életbe, azaz hiba esetén a program futása megszakad.)

Az ON READERROR segítségével az I/O mûveletekben közben fellépõ hibákat kezelhetjük.

FoxPro 8.0:

A 8.0-ás verziótól lehetõségünk van a kivételkezelés alkalmazására.

TRY
     [ tryCommands ]
[ CATCH [ TO VarName ] [ WHEN lExpression ]
     [ catchCommands ] ]
[ THROW [ eUserExpression ] ]
[ EXIT ]
[ FINALLY 
     [ finallyCommands ] ]
ENDTRY

A kivételkezelésre a fenti szerkezetet alkalmazhatjuk.
A TRY blokkban elhelyezett sorok által kiváltott kivételeket a CATCH kifejezés segítségével kezelhetjük le.
A THROW utasítás segítségével válthatunk ki kivételt. A parancs alkalmazható a CATCH blokkban is.
A blokk azonnali befejezését elérhetjük az EXIT utasítással, ami mindhárom részben alkalmazható.
A TRY és a CATCH részben kiadott EXIT utasítás hatására a vezérlés a FINALLY blokkhoz kerül.
Ha a FINALLY-ban használjuk, akkor az ENDTRY-hoz jutunk.
A FINALLY blokkban elhelyezett sorok minden esetben végrehajtódnak, kivéve a CANCEL és QUITalkalmazása esetén.

A kivételobjektum autómatikusan létrejön hiba esetén.

CATCH mûködése

WHEN után megadható feltétel teljesülése esetén a megadott változó egy a kivételobjektumra mutató referenciát fog tartalmazni.
Ezután a CATCH blokkban található utasítások hajtódnak végre. Ebben a blokkban a referencián keresztûl felhasználhatjuk a kivételobjektumban tárolt összes információt.

THROW mûködése

A Kivételt mi is kiválthatjuk a THROW utasítás segítségével, aminek paraméterként egy EXCEPTION osztályból, vagy leszármazottjából példányosított kivételobjektumot kell megadnunk.
A THROW által kiváltott kivételek ErrorNo attribútumát a rendszer utómatikusan 2071-re állítja, ami a felhasználó által dobott kivételek száma.

Példa:

LOCAL x AS Integer, y AS Integer, result AS Integer LOCAL oErr AS Exception, oErr1 AS Exception TRY x = 1 TRY USE nothing GO TOP y = nothing.col1 CATCH TO oErr oErr.UserValue = "Nested CATCH message: Unable to handle" ?[: Nested Catch! (Unhandled: Throw oErr Object Up) ] ?[ Inner Exception Object: ] ?[ Error: ] + STR(oErr.ErrorNo) ?[ LineNo: ] + STR(oErr.LineNo) ?[ Message: ] + oErr.Message ?[ Procedure: ] + oErr.Procedure ?[ Details: ] + oErr.Details ?[ StackLevel: ] + STR(oErr.StackLevel) ?[ LineContents: ] + oErr.LineContents ?[ UserValue: ] + oErr.UserValue THROW oErr FINALLY ?[: Nested FINALLY executed ] IF USED("nothing") USE IN nothing ENDIF ENDTRY result = x-y CATCH TO oErr1 ?[: Outer CATCH! ] ?[ Outer Exception Object: ] ?[ Error: ] + STR(oErr1.ErrorNo) ?[ LineNo: ] + STR(oErr1.LineNo) ?[ Message: ] + oErr1.Message ?[ Procedure: ] + oErr1.Procedure ?[ Details: ] + oErr1.Details ?[ StackLevel: ] + STR(oErr1.StackLevel) ?[ LineContents: ] + oErr1.LineContents ?[ ->UserValue becomes inner exception THROWn from nested TRY/CATCH ] ?[ Error: ] + STR(oErr1.UserValue.ErrorNo) ?[ Message: ] + oErr1.UserValue.Message ?[ Procedure: ] + oErr1.UserValue.Procedure ?[ Details: ] + oErr1.UserValue.Details ?[ StackLevel: ] + STR(oErr1.UserValue.StackLevel) ?[ LineContents: ] + oErr1.UserValue.LineContents ?[ UserValue: ] + oErr1.UserValue.UserValue result = 0 FINALLY ?[: FINALLY statement executed ] ENDTRY RETURN result>