Výjimka (programování)

koncept v programování

Výjimka (anglicky exception) je v programování výjimečná situace, která může nastat za běhu programu. Jedná se o zobecnění vnitřního přerušení vyvolaného chybou při provádění programu. Ve vyšších programovacích jazycích je obvykle výjimka spojena s objektem nesoucím informace o chybovém stavu.

Historie a současnost výjimek

editovat

Prvním rozšířeným jazykem, který disponoval zpracováním výjimek, je jazyk PL/I společnosti IBM z roku 1964, který implementoval výjimky jako mechanismus pro zpracování neobvyklých (on endfile) nebo chybových (on zerodivide) stavů. Dalším z 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). Mechanismus zpracování výjimek používá Ada, C++, Eiffel, Python, Ruby, Java a mnohé další.

Zpracování výjimek

editovat

Ve většině vyšších programovacích jazyků, které podporují zpracování výjimek, je implicitně (blok) nebo explicitně (např. klíčovým slovem try) vymezeno, v jaké části kódu se má hlídat vznik výjimek. Výskyt výjimky se ošetřuje („zachycení chybového stavu“) zvláštním blokem kódu, obvykle uvozeným klíčovým slovem catch nebo except, ve kterém je k dispozici datová struktura nesoucí informace o chybovém stavu. Výjimky mohou být způsobeny chybovým stavem při provádění kódu (dělení nulou, přetečení, nedostatek paměti, vstupně-výstupní chyba) nebo mohou být vyvolány úmyslně, pak mluvíme o „vyhození výjimky“ (throw), což je signalizace, že čá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 jazyka Java více než 1300 chyb ve zpracování výjimek.[2]

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

editovat

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.[5]

Podpora výjimek v programovacích jazycích

editovat

Mezi programovací jazyky s podporou zpracování výjimek patří Ada, C++, C# a jiné programovací jazyky platformy .NET, Eiffel, Java, Object Pascal (např. Delphi), PHP verze 5a vyšší, Prolog, Python, Ruby. Zpracování výjimek ve většině těchto programovacích jazyků neobnovuje zpracování v místě, kde k výjimce došlo; běžně se zpětně prochází zásobník, až je nalezeno zpracování příslušné výjimky. 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. Zpracování výjimek (v pseudokódu podobném jazyku Java) může 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

editovat

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í:[6]

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

editovat

V tomto článku byl použit překlad textu z článku Exception handling na anglické Wikipedii.

  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. J. L. Lions. ARIANE 5 Flight 501 Failure [online]. Paříž: Inquiry Board, 1996-07-19 [cit. 2020-11-13]. Dostupné online. (anglicky) 
  6. Ukázka převzata z: http://www.cs.cmu.edu/~rwh/introsml/core/exceptions.htm