Níže uvedený text pochází z prvního vydání. Nad tímto textem se nachází aktuální stav po revizi směřující k druhému vydání.
O čem si budeme povídat?
Při tradičním přístupu může programátor po provedení nějaké akce — dejme tomu po volání funkce — otestovat korektnost výsledku. Když se například pokusíme otevřít soubor, který neexistuje, může se vracet hodnota NULL. K ošetření takových situací se používají dva obecné přístupy:
Odpovědnost za kontrolu (zda nastala chyba) a provedení příslušných akcí leží v obou případech na programátorovi.
V jazyce BASIC to může vypadat takto:
OPEN "A:\DATA.TXT" FOR INPUT AS #1
IF ERR = 53 THEN
CALL ChybaSouborNenalezen
ELSE
REM Zde pokračujeme v práci se souborem.
END IF
Při tvorbě kvalitních programů to může vést k situaci, kdy více než polovina kódu připadá na testování úspěšnosti jednotlivých akcí. Je to poněkud nešikovné a snižuje to čitelnost kódu (ale v současnosti je takto psána většina programů). Pokud se chceme vyhnout některým hloupým chybám, musíme tento postup důsledně dodržovat.
V modernějších programátorských prostředích se vyvinul alternativní způsob práce s chybami. Je znám pod pojmem ošetření výjimek a je založen na tom, že funkce vrhají (throw) nebo, jinými slovy, vyvolávají (raise) výjimky. Systém si pak zajistí vyskočení z aktuálního bloku kódu na nejbližší blok pro ošetření výjimky. V systému se nachází také blok kódu, který zachytí (catch) všechny neošetřené výjimky a obvykle zobrazí chybové hlášení a poté ukončí běh aplikace.
Blok kódu pro ošetření výjimek se trochu podobá bloku
if...then...else:
try: # Zde se umístí hlavní část kódu programu. except TypVyjimky: # Zde se bude zpracovávat jmenovaná výjimka. except JinyTypVyjimky: # Zde se ošetřuje jiná výjimka. else: # Zde umístíme úklidový kód, který se provede # v případě, že nenastane žádná výjimka.
Existuje ještě jeden typ bloku, který souvisí s výjimkami. Umožňuje
zapsat kód pro úklid prováděný i poté, co nastala chyba. Nazývá se
try...finally (try [tray] = zkus, pokus se vykonat; finally [fajnly] = nakonec) a
typicky se používá pro zavírání souborů, vyprazdňování vyrovnávacích pamětí
(buffer) na disk a podobně. Blok finally je proveden vždy jako
poslední nezávisle na tom, co se stane v sekci try. Pokud nenastane výjimka, prostě
se provede. Pokud nastane výjimka, zapamatuje se, kód bloku
finally se provede a zapamatovaná výjimka se znovu
vyvolá.
try: # Kód, související s účelem programu. finally: # V této části provádíme úklidové akce nezávisle # na tom, zda v bloku try nastala chyba či nikoliv.
V Tcl se používá podobný mechanismus, který používá klíčové slovo
catch ([keč]
= chytit, zachytit):
set chybovyKod [catch {
unset x
} zprava ]
if {$chybovyKod != 0} {
# Tady ošetříme chybu.
}
V uvedeném případě proměnná x neexistuje, takže nemůžeme
zrušit její hodnotu. Tcl vyvolá výjimku, ale catch zajistí, že
program nezhavaruje. Místo toho umístí chybovou zprávu do proměnné
zprava a vrátí nenulovou hodnotu (kterou může programátor
určit). Proto můžeme později otestovat návratovou hodnotu části
catch, která byla uložena do proměnné chybovyKod.
Pokud je nenulová, došlo k chybě a podrobnosti se můžeme dozvědět z proměnné
zprava.
BASIC výjimky v pravém slova smyslu nepodporuje, ale nabízí konstrukci, která nám pomůže dodržet čistotu (přehlednost) kódu:
100 OPEN "A:\Temp.dat" FOR INPUT AS #1
110 ON ERROR GOTO 10010
120 REM Tady umístíme kód programu...
130 ...
10000 REM Vstupní body pro ošetření jednotlivých chyb:
10010 IF ERR = 54 THEN....
Povšimněte si použití čísel řádků. Ta se ve starších jazycích používala běžně. První verze jazyka BASIC nebyly výjimkou. Stejného efektu můžete v současné době dosáhnout při využití návěští (label):
ON ERROR GOTO Osetreni
REM Nyní vytvoříme chybu dělení nulou
x = 5/0
Osetreni:
IF ERR = 23 THEN
PRINT "Nelze dělit nulou!"
x = 0
RESUME NEXT
END IF
Povšimněte si příkazu RESUME NEXT, který způsobí návrat
do místa programu těsně za příkaz, ve kterém došlo k chybě. Od tohoto místa
provádění programu pokračuje.
Jakým způsobem můžeme generovat výjimky — dejme tomu uvnitř modulu
—, které má zachytit někdo jiný? V jazyce Python je pro tento případ
vyhrazeno klíčové slovo raise:
delenec = 42
delitel = input("Jakou hodnotou chcete dělit číslo 42?")
if delitel == 0:
raise "nulový dělitel"
Tento kód vygeneruje výjimku (v podobě objektu typu string), která může
být zachycena v bloku try/except.
V Tcl může být u příkazu return uveden nepovinný příznak
-code, který bude zachycen libovolnou z obklopujících funkcí
catch:
proc spam {val} {
set x $val
return -code 3 [expr $x]
}
set err [catch {
set foo [spam 7]
} msg]
V proměnné err by se měla objevit hodnota 3 a v proměnné
msg hodnota 7. Je to další případ toho, kdy je syntaxe jazyka
Tcl méně intuitivní, než by mohla být.
V jazyce BASIC můžeme proměnnou ERR nastavit příkazem
ERROR:
ON ERROR GOTO CHYBY
INPUT "Zadej kód chyby"; E
ERROR E
CHYBY:
IF ERR = 142 THEN
PRINT "Nastala chyba číslo 142"
STOP
ELSE
PRINT "Neznámá chyba"
STOP
END IF
Zapamatujte si
if.except.raise.Pokud vás napadne, co by se dalo na překladu této kapitoly vylepšit, zašlete e-mail odklepnutím Tím budou do dopisu automaticky vloženy informace o tomto HTML dokumentu.
$Id: cztuterrors.html,v 1.5 2004/08/31 11:55:13 prikryl Exp $