Výjimka (programování)

Z Wikipedie, otevřené encyklopedie

Výjimka (z angličtiny Exception) je v informatice chápána jako výjimečná situace, která může nastat za běhu programu, případně ve volané metodě nebo funkci. Jedná se o speciální případ softwarového přerušení vyvolaného následkem výjimečné situace při zpracování v procesoru. Obecně jsou jako výjimky označovány objekty nesoucí informace o chybovém stavu. V případě objektově orientovaného programování jsou jako výjimky označovány objekty nesoucí tyto informace.

Historie a současnost výjimek

První historické záznamy o výjimkách pochází z roku 1964 v souvislosti s programovacím jazykem PL/I vyvíjeným společností IBM. Původně byly výjimky používány pro ladění programu, v současné době jsou však plnohodnotnou součástí většiny programovacích jazyků jakožto mechanismus pro zacházení s chybovými stavy. Jedním z prvních programovacích jazyků, které mechanismy zpracování výjimek implementovaly a uvedly do praxe je jazyk ML vyvíjený v 70. letech (1. vydání v roce 1973). Mezi další programy, které využívají výjimek, patří Ada, C++, Eiffel, Python, Ruby, Java a mnohé další.

Zpracování výjimek

Pojmem výjimka je obecně označena datová struktura nesoucí informace o chybovém stavu. V případě, že k takovému stavu dojde, mluvíme o „zachycení chybového stavu“ (catch). Ošetření tohoto stavu probíhá pomocí tzv. „vyhození výjimky“ (throw). Vyhození výjimky je způsob signalizace, že probíhající část zpracovávaného kódu nemohla být provedena normálně. Důvodem může být např. nedostupnost použitého zdroje (neexistující soubor, chyba média, nedostatek paměti) nebo chybný vstupní argument (zadaná hodnota se nenachází v definičním oboru použité funkce). V systémech nevyužívajících výjimky je v těchto situacích uživateli vrácen příslušný kód chyby. V případě použití výjimek jsou zprávy uživateli programu (příp. programátorovi) ve většině případů jasnější a je tedy jednodušší pochopit, proč k chybě došlo a jak se ji vyvarovat, potažmo jak ji ošetřit. V některých případech výjimek je také možno chod programu obnovit. Zde vždy závisí na konkrétním použitém mechanismu ošetření výjimky a také na druhu výjimky. Příkladem relativně snadno obnovitelného chybového stavu je dělení desetinného čísla (float) nulou. Naopak velmi obtížně se ošetřují stavy s nedostatkem paměti (out-of-memory). Pro většinu současných programovacích jazyků platí, že jsou používány výjimky, které neobnovují chod programu (non-resumable). K tomuto trendu došlo postupným vývojem, který se dočkal podpory odborníků, mimo jiné Bjarna Stroustrupa [1], tvůrce programovacího jazyka C++. Zpracování výjimek je velmi často implementováno nesprávně, především ve složitějších programech, kde je více možných původců výjimek. Studie z roku 2008 objevila v 5 milionech řádcích kódu jazyku Java více než 1300 chyb ve zpracování výjimek.[2]

Přístupy k použití výjimek a jejich zpracování

Využití výjimek a jejich zpracování není pro všechny programovací jazyky vždy stejné a přístup k nim se liší i mezi jednotlivými programátory. Typickým přístupem prosazujícím použití výjimek je tzv. Defenzivní programování, které se vyznačuje snahou o ošetření všech neočekávaných stavů, ke kterým by mohlo za běhu programu dojít. V opozici k defenzivnímu programování stojí do značné míry kontraktové programování, které se zakládá na předpokladu, že je nutné pouze zajistit stavy, které nejsou ošetřeny kontraktem nebo zadáním. Jako příklad můžeme použít např. algoritmus pro výpočet odmocniny, který předpokládá, že mu nikdy nebude předáno v parametru záporné číslo. V případě defenzivního programování by toto bylo ošetřeno v kódu, v případě programování podle kontraktu by tento předpoklad byl zakotven v zadání. Používání výjimek má ve světě i své odpůrce, jedním z nich je např. Sir Charles Antony Richard Hoare, který označuje používání výjimek za potenciálně nebezpečné[3]. Svůj názor vyslovil v roce 1980 v rámci Turing Award Lecutre a podpořil jej výrokem „Příští raketa, která se odchýlí z kurzu v důsledku chyby programu, nemusí být výzkumná vesmírná raketa směřující k Venuši. Může jít o jadernou střelu vybuchující nad jedním z našich měst.[4]“ Pozdější pád rakety Ariane 5 (let 501) v roce 1996 v důsledku chyby programu po startu jeho obavy potvrdil. Nehoda nastala v důsledku nedostatečného pochopení a přílišného spoléhání se na ošetření výjimek v programovacím jazyku Ada, který byl použit v navigačním software rakety.

Podpora výjimek v programovacích jazycích

Programovací jazyky s implicitně zabudovanou podporou zpracování výjimek zahrnují, ale neomezují se jen na: Ada, C++, C# a jiné programovací jazyky platformy .NET, Eiffel, Java, Object Pascal (např. Delphi), PHP verze 5a vyšší, Prolog, Python, Ruby a mnohé další. Zpracování výjimek v těchto programovacích jazycích téměř výhradně neobnovuje chod programu a ve většině případů dochází k postupnému zpětnému procházení zásobníku, než je nalezen příslušný objekt zpracovávající výjimku. Abstrahujeme-li od drobných syntaktických rozdílů, existuje velmi málo přístupů ke zpracování výjimek na úrovni zdrojového kódu. Nejrozšířeněji je proces zpracování výjimky spuštěn klauzulí „throw“ nebo „raise“, případně objektem výjimky. Blok s výjimkou obvykle začíná klauzulí „try“ nebo „begin“, jednotlivé chybové stavy jsou poté označeny bloky „catch“ nebo „except“. Celý blok zpracování výjimky je ukončen sekcí „finally“ nebo „ensure“, která slouží pro uvolnění systémových zdrojů použitých při zpracování výjimky. Jako celek může deklarace výjimky (v pseudokódu podobném jazyku Java) vypadat např. takto:

 try {
  line = console.readLine();
  if (line.length() == 0) {
    throw new EmptyLineException("The line read from console was empty!");
  }
  console.printLine("Hello %s!" % line);
  console.printLine("The program ran successfully");
 } catch (EmptyLineException e) {
  console.printLine("Hello!");
 } catch (Exception e) {
  console.printLine("Error: " + e.message());
 } finally {
  console.printLine("The program terminates now");
 }

Příklad použití výjimky

Následující kód ukazuje deklaraci výjimky v jazyce ML a její následné použití v kódu a ošetření[5]:

exception Factorial

local
      fun fact 0 = 1
        | fact n = n * fact (n-1)
in
      fun checked_factorial n =
          if n >= 0 then
             fact n
          else
             raise Factorial
end   
fun factorial_driver () =
    let
        val input = read_integer ()
        val result = makestring (checked_factorial input)
    in
        print result
    end
    handle Factorial => print "Out of range.\n"

Jak můžeme vidět, výjimka řeší situaci, kdy je funkci faktoriálu zadáno jako parametr záporné číslo (faktoriál záporného čísla neexistuje). V tomto případě program zobrazí chybovou hlášku „Out of range“ ve výstupním okně, které je implicitně zvoleno (obvykle se jedná o konzolový výstup).

Reference

  1. Bjarne Stroustrup (April 8, 1994). Design and Evolution of C++. Addison-Wesley.
  2. Weimer, W and Necula, G.C. (2008). "ACM Transactions on Programming Languages and Systems, vol 30 (2)".
  3. C.A.R. Hoare. "The Emperor's Old Clothes". 1980 Turing Award Lecture
  4. Parafráze původního citátu. Původní znění: "The next rocket to go astray as a result of a programming language error may not be an exploratory space rocket on a harmless trip to Venus: It may be a nuclear warhead exploding over one of our own cities."
  5. Ukázka převzata z: http://www.cs.cmu.edu/~rwh/introsml/core/exceptions.htm