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šetření chyb

O čem si budeme povídat?

Tradiční způsob

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:

  1. Chybový kód zahrneme do výsledku funkce nebo
  2. do vyhrazené globální proměnné přiřadíme informaci o chybovém stavu.

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.

Využití výjimek

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.

Generování chyb

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.

Chybový mechanismus jazyka Tcl

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.

Zpracování chyb v jazyce BASIC

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

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 $