V programovacích jazycích se pro detekci chyb obvykle používá jeden ze tří způsobů:

  • testování návratové hodnoty
  • testování přes globální proměnnou či funkci
  • výjimky

Výjimky se považují za nejpokročilejší ze všech metod. Mají úzkou vazbu na jazyk a jejich užití vypadá velmi elegantně.

Na základě delších teoretických úvah si však troufám tvrdit, že elegantně skutečně jen vypadají. Než však budu moci toto tvrzení obhájit, musíme trošku zabřednout do teorie.

Co jsou to chyby?

Abychom se vůbec byli schopni domluvit, určeme si některé pojmy. Jde o ryze soukromé definice, tedy nezaměňujte význam zde uvedených termínů chyba a varování za ten, který mají třeba v PHP nebo jiném jazyku.

S pojmem chyba velmi úzce souvisí pojem funkce. Funkce je základní úsek programu, nad kterým budeme nadále uvažovat. Za chybu považujme takový stav, kdy funkce není schopna dokončit svou úlohu. Ukončí se předčasně a nějakým způsobem volající funkci tuto skutečnost sdělí.

Během vykonávání funkce může dojít i k výjimečnému stavu, který je třeba oznámit programátorovi, ale nebrání funkci v dalším běhu. Tomuto říkejme varování.

Mezi varováním a chybou je zásadní rozdíl. Zatímco chyba se oznamuje volající funkci (tedy program sděluje programu), tak varování je adresováno programátorovi. Proto je v případě chyb klíčový její kód (ať už vyjádřen jakkoliv), kdežto u varování spíš vysvětlující popis.

Životnost chyby

Všimněte si, že z výše uvedených definic vyplývá, že „nepříjemné“ stavy, které si funkce umí sama ošetřit a zpracovat, neoznačujeme za chyby. A dále, že chyba končí společně s ukončením funkce. Nadřazená funkce má totiž vlastní hodnotový žebříček chyb a proto ji třeba vůbec nemusí zajímat chyby vzniklé v podfunkcích.

Příklad:

function test() {
  ...
  $s = file_get_contents('cache.dat');
  if ($s === false) ...
  ...
}

Funkce se pokusí načíst obsah keše ze souboru. Pokud soubor neexistuje, dojde k chybě uvnitř file_get_contents. Pro jednu funkci je to chyba, pro druhou naopak záležitost, se kterou počítá.

Narážíme na první úskalí

V uvedeném příkladě jsme se dopustili vážného pochybení. Předpokládáme totiž, že pokud se soubor nepodařilo načíst, tak je to z důvodu, že neexistuje. Jenže co když je to tak, že nemáme ke čtení dostatečná práva? Nebo cache.dat je adresářem? Nebo došlo k úplně jiné neočekávané chybě?

Funkce test() by potřebovala rozlišit tři stavy:

  1. soubor se povedlo načíst
  2. soubor se nepovedlo načíst, protože zatím neexistuje (a pak pokračovat dle plánu)
  3. soubor se nepovedlo načíst z jiného důvodu (a pozor – je jen na funkci test(), jestli z toho vyvodí chybu, varování nebo situaci vyřeší jinak!)

K rozlišení případů je třeba návratový kód ve strojově zpracovatelné podobě (např. číslo). V případě třetí varianty by bylo vhodné informaci doplnit o textový popis a také jména souborů a čísla řádků (callstack), kde k chybě došlo (pomiňme nyní, že file_get_contents je interní funkce).

Vedle toho má návratová hodnota false poněkud malou vypovídací hodnotu…

Dobrá, dejme tomu, že v případě chyby nevrátíme false, ale strukturu, která tyto informace bude obsahovat. To je záležitost zcela snadno realizovatelná v netypových jazycích (PHP) a naprosto neřešitelná v jazycích typových (Delphi, C++, Java).

Optikou výjimek

Záchranou by mohly být výjimky. Především dokáží zmíněnou strukturu velmi snadno vygenerovat:

  • kód je představován třídou výjimky a případně číslem chyby
  • textový popis lze také doplnit
  • automaticky zaznamená jméno souboru a pozici, kde k ní došlo

Navíc výjimky umí obejít mechanismus návratových hodnot, takže je možné je předávat i v typových jazycích.

Ovšem – je tu jeden velký rozdíl od nastíněného způsobu chápání chyb. Zatímco naše chyby končí s ukončením funkce, výjimky končí až s jejich ošetřením.

Tedy objevivší se problém zanořené funkce se automaticky stává kritickým problémem i funkcí nadřazených. To přináší docela jiný náhled na věc. A není nakonec takovéto chápání lepší?

Odpověď si nechám na příště 🙂