8. díl - SDL - Errory a logování

C++ SDL SDL - Errory a logování

V dnešním díle se podíváme na zachytávání errorů, které SDL může vyvolat a na jejich následné logování. Stejným způsobem můžeme samozřejmě logovat i jakékoliv další informace.

Errory

Protože je SDL určeno pro jazky C, nepracuje s výjimkami, jak to známe z C++. To se změní, jakmile použijeme objektový wrapper pro C++. Do té doby se ale budeme muset naučit, jak pracovat s errory v samotném SDL.

Konvence je relativně jednoduchá. Pokud se jedná o funkci, která něco vytváří (SDL_CreateRGBSurface, SDL_CreateWindow a jiné), SDL vrátí NULL. Všechny ostatní funkce, včetně například SDL_Init, vrací 0 při úspěšném provedení funkce, v jiných případech vrací zápornou hodnotu. Je potřeba po každé takové operaci ověřit, zda funkce proběhla v pořádku. Pokud si nejsme jisti, co bude funkce vracet, zjistíme to v dokumentaci.

K samotnému získání informace o problému, který nastal, slouží funkce SDL_GetError. Ta vrací const char *, tedy řetězec obsahující bližší určení chyby. Tento text je vždy v angličtině. Po vyřešení erroru by měla následovat funkce SDL_ClearError, která vyčistí vložený error . SDL_GetError po zavolání této metody (nebo když není žádný error) vrátí prázdný řetězec. Není to ovšem nutné, protože při dalším erroru SDL přemaže text původního.

Poslední funkce, která se u práce s errory vyskytne, je SDL_SetError. Ta nastaví chybovou hlášku na řetězec, který předáme v parametru. Vzhledem k tomu, že pracujeme v C++, nebudeme tuto funkci využívat, protože by tyto stavy měly být vyřešeny výjimkami. Jak už jsem zmínil, přidáním dalšího erroru se předcházející smaže, totéž platí i pro tuto funkci. Také pozor na návratovou hodnotu, ta je vždy -1.

Logy

Při logování už je situace o něco pestřejší. Logování pracuje s různými prioritami a kategoriemi. Nejdřív si popíšeme jednotlivé kategorie a následně funkce, které SDL pro logování poskytuje. Logování se také na každém OS chová odlišně. Na systémech Windows se bude logovat do debug výstupu (není jej tedy možné použít pro produkci). Na systémech Android bude výstup do klasického logovacího výstupu. Na všech zbývajících systémech se budou logy zapisovat to standartního error výstupu. Pro C++ je to tedy sderr. Také je potřeba dát pozor na délku zprávy. Zpráva, která má více než 4096 bajtů (4096 znaků), bude automaticky oříznuta.

Kategorie

Jednotlivé kategorie logů jsou vypsány v dokumentaci.

  • Aplication log je log týkající se aplikace. Můžeme u něj vypisovat logy týkající se startu a vypnutí aplikace, načtení konfigurace a podobné věci.
  • Error log je log týkající se problémů v aplikaci. Zpravidla se sem zařazují zprávy, které jsme dostali z SDL_GetError.
  • Do systémového logu zapisujeme informace, které se týkají přímo systému. Můžeme zde například zapsat informace o hardwaru počítače a informace pocházející z operačního systému. Proč se nám tyto informace hodí? Pokud někdo bude naši aplikaci používat na jeho stroji a něco nebude fungovat, je vhodné mít alespoň nějaké informace o stroji, na kterém aplikace běží.
  • Audio a Video logy slouží k informování o funkci videa a audia. Můžeme zde například vypsat, v jakém rozlišení byla vytvořena textura nebo okno.
  • Dalším logem je render log. Ten bude zpravidla poblíž renderovací funkce v nekonečné smyčce (viz dřívější kapitoly). Ideálně by měl být tento log za každou funkcí, která něco vykresluje (SDL_RenderCopy, SDL_RenderClear a jiné).
  • Poslední přichystanou kategorií použitelnou v aplikaci je input log, který loguje informace proudící do aplikace. Můžeme zde například vypisovat, kterou akci uživatel provedl.
  • Kromě toho má SDL další dvě kategorie, které využijeme především při debugování. Je to test a assert log. Typickým příkladem jsou testy aplikace.

Poslední kategorií je custom log, kterému můžeme přiřadit cokoliv. Zpravidla si vytváříme vlastní kategorie, které aplikace potřebuje. Pokud chceme tyto kategorie vytvořit, napíšeme si vlastní enumerátor, ale první hodnota musí být stejná s SDL_LOG_CATEGORY_CUSTOM, aby nenastala kolize s ostatními kategoriemi. Jak vytvořit vlastní kategorii vidíme na následujícím kódu.

enum
{
      MOJEAPLIKACE_CATEGORY_KATEGORIE1 = SDL_LOG_CATEGORY_CUSTOM,
      MOJEAPLIKACE _CATEGORY_KATEGORIE2,
      MOJEAPLIKACE _CATEGORY_KATEGORIE3,
      ...
};

Není nutně pravidlem, že musí být použita vždy přesná kategorie. SDL při logování jednotlivé kategorie nerozlišuje, je tedy jedno, pod jakou kategorií informaci zalogujeme. Kategorie se nám můžou hodit později, až si budeme vytvářet vlastní logovací funkci. Ta může jednotlivé kategorie rozlišovat a chovat se pro ně rozdílně. Vlastní logovací funkci budu popisovat dále v článku.

Priority

Každý log také může mít vlastní priority. Prioritou blíže popíšeme, jak důležitá je informace, kterou logujeme. Priority budu popisovat vzestupně podle důležitosti:

  • Verbose – v překladu něco jako užvaněný. Pod touto prioritou si můžeme vypisovat libovolné informace. Můžeme vypisovat text, abychom věděli, na kterém místě programu jsme. Většinu problémů řešených touto prioritou lze řešit debuggerem.
  • Debug – pod touto prioritou budeme vypisovat informace týkající se debugování. Můžeme vypisovat hodnoty proměnných, které nás zajímají, vypisovat aktuální stav na určitém místě programu apod. Tyto problémy bychom měli opět řešit debuggerem.
  • Info – zde začínají užitečnější priority, které můžeme použít v aplikaci. Pod info prioritou můžeme zveřejňovat informace, které budou mít pouze informační charakter. Například „zapnutí aplikace“ nebo „načtení konfigurace“. Pokud budeme chtít později z logu zjistit, co se u uživatele stalo, pomůže nám info k přibližnému určení oblasti, ve které k problému došlo.
  • Warn – priorita oznamující, že je pravděpodobně něco v nepořádku. Pod touto prioritou budeme logovat informace, které jsou mimo obvyklý chod aplikace, ale nejsou důležité do budoucna. Pokud například aplikace bude chtít načíst joystick, ačkoliv jej nepoužívá (třeba není v aktuálním levelu potřeba). Není to důležitá součást systému, proto informaci o tom, že se nepodařilo načíst joystick, stačí označit prioritou Warn.
  • Error – priorita oznamující problém v aplikaci. Jedná se o událost, která přímo aplikaci nevypne, ale zpravidla je předzvěst konce. Například když se nám nepodaří vytvořit SDL_Surface, aplikace spadne až ve chvíli, kdy se pokusíme tento SDL_Surface použít. V kódu kontroly, zda byl SDL_Surface vytvořen, můžeme zalogovat informaci o problému s prioritou error.
  • Critical – priorita spjatá s kritickou situací v aplikaci. Pokud se vyskytne taková událost, aplikace zpravidla není schopná pokračovat v práci. Taková událost může nastat například když se nepodaří SDL inicializovat nebo vytvořit okno. Po takových událostech není aplikace schopná pokračovat a spadne nebo se sama ukončí.

Logování

Nejjednodušší funkcí pro logování je SDL_Log. Přijímá řetězec ve stejném formátu, jaký je u funkce printf. V řetězci přijímá zástupné znaky jako „%i“, „%f“ a jiné. Další parametry funkce jsou proměnné, které nahradí tyto zástupné znaky. Log se touto funkcí zapíše do kategorie application s prioritou info.

Přesným protikladem je funkce SDL_LogMessage. Prvním parametrem je kategorie, kterou chceme logovat, druhým parametrem je poté priorita, pod kterou budeme zprávu logovat. Třetím a čtvrtým parametrem jsou opět řetězec a proměnné, které chceme zalogovat. Tyto parametry jsou shodné s parametry SDL_Log.

Logování s určenou prioritou

Každá priorita má vlastní funkci, která loguje zprávy. Například funkce SDL_LogCritical zaloguje zprávu s prioritou critical. Kategorii opět určíme prvním parametrem, zbytek parametrů je určen pro řetězec a proměnné. Doporučuji používat tento soubor funkcí, ne ž samotnou funkci SDL_LogMessage. Ačkoliv je výsledek stejný, použitím určené funkce bude kód přehlednější a svým způsobem i kratší, protože nemusíme vypisovat konstantu priority. Seznam funkcí je zde.

Nastavení zobrazených logů

Výše v článku jsem řekl, že SDL jednotlivé kategorie při výstupu nerozlišuje. K čemu tedy slouží? Pro každou kategorii můžeme nastavit prioritu, která se zobrazí na výstupu. Nastavíme-li například ke kategorii aplication prioritu info, budou se logovat všechny informace s prioritou info, warning, error a critical. K nastavení priority pro kategorii slouží funkce SDL_LogSetPri­ority. Priority s vyšší důležitostí než je priorita zadaná (včetně) se budou logovat, ostatní na výstupu neuvidíme. Můžeme také nastavit stejnou prioritu pro všechny kategorie, a to funkcí SDL_SetAllPri­ority.

Ve výchozím nastavení má application kategorie nastavenou prioritu info, assert kategorie prioritu warn a test kategorie prioritu verbose. Všechny ostatní kategorie mají nastavenou prioritu critical. Jestliže priority změníme a budeme je chtít vrátit do výchozí hodnoty, poslouží nám funkce SDL_LogResetA­llPriorities.

Vlastní logovací systém

SDL nám nabízí prostředek, díky kterému můžeme logování napojit na vlastní logovací systém, aniž bychom museli měnit již existující aplikaci. Vystačíme si s jedinou funkcí, a to SDL_LogSetOutput­Function. Ta v prvním parametru přijímá callback, který zavolá vždy, když bude chtít něco zalogovat. V druhém parametru přijímá ukazatel na void, který bude předán callbacku. Můžeme do něj vložit třídu nebo strukturu, kterou budeme při logování potřebovat. Musíme ale počítat s tím, že už tuto třídu nemůžeme vyměnit za jinou (bez znovu zavolání funkce). Callback není nic jiného než ukazatel na funkci, která vypadá následovně.

void LogFunction(void* data, int kategorie, SDL_LogPriority priorita, const char* zprava);

Každá zpráva projde přes tuto funkci, která ji zaloguje (výchozí logovací systém se deaktivuje).

Ukázkový příklad

Ukázka logování

Jako ukázku si vezmeme příklad z minulé lekce a doplníme do něj logování (každý se můžete zkusit zamyslet, jak by to vypadalo - moje řešení si můžete stáhnout pod článkem). Ověříme každou funkci, kterou ověřit můžeme a pokud nastane problém, zalogujeme jej. Stejným způsobem by měla být postavena každá aplikace. Abychom viděli, jak SDL loguje zprávy s rozdílnou kategorií a prioritou, vložím do programu pár logů, které se zalogují vždy.

Všimněme si především inicializační funkce. Vidíme, že pro každou část knihovny (SDL, TTF, IMG) se kód liší. Pro SDL vrátí funkce při neúspěchu zápornou hodnotu. IMG naopak při úspěchu vrací konstantu, kterou jsme předali v parametru, a při neúspěchu 0. Je vždy nutné ověřit v dokumentaci, jak funkce reaguje na špatné parametry nebo prostředí.

To je pro dnešní lekci vše. V příštím díle se podíváme na časovač a na optimalizaci FPS, tedy snímků za sekundu tak, aby byla aplikace co nejplynulejší a zároveň spotřebovala co nejméně procesorového času.


 

Stáhnout

Staženo 138x (9.58 MB)

 

  Aktivity (1)

Článek pro vás napsal patrik.valkovic
Avatar
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu.

Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!


 


Miniatura
Všechny články v sekci
SDL
Miniatura
Následující článek
SDL - Práce s časovačem

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!