FAQ (avagy a grafika listán a legtöbb levelet generáló "misztikus" hibák)
- Fordításkor ilyen hibát kapok:
error: stray '\357' in program... - UTF-8 BOM hiba
- Az osztást tartalmazó számításom rossz eredményt ad - Egészosztási hiba
- A 3D-s kép zajos, az élek cikk-cakkosak - Z-Fighting
- Sugárkövetéskor a testen fura fekete részek jelennek meg - Önárnyékolás
- Sugárkövetéskor az egész képem fekete/fehér - Tone Map használata
glQuad használatakor négyszög helyett két egymásba csúszott háromszög jelenik meg - QUAD
Tipikus C++ hibák
- Ha stabil C++ tudással rendelkezel, ezt a részt nyugodtan ugord át
- UTF8 BOM problémák:
- Ha a kódodat UTF-8 kódolással mented, sok szerkesztő a fájl elejére biggyeszt három nem látható karaktert, az úgy nevezett UTF-8 Byte Order Mark-ot. Ez a Gordiuson fordítási hibát fog eredményezni, ezért a kódunkat feltöltés előtt mindenképpen konvertáljuk sima ASCII/ANSI szövegfájllá (Még a Jegyzettömb is képes erre, Mentés másként - ANSI)
- Lokális változók scope-on kívüli használata:
- Figyeljünk rá hogy a lokális változók csak addig léteznek, amíg a scope-on belül járunk
- Ennek megfelelően ne adjuk át őket cím szerint, ha mégis referenciát akarunk készíteni ilyen változóról, használjunk const referenciát!
- Egészosztás véletlen használata:
- A C++-ban az operandusok típusától függ, hogy egészosztás (div) vagy rendes osztás történik
- Ez a működés nehezen detektálható hibákat okozhat, ezért mindig figyeljünk rá! Például ha egész típusú változót osztunk egy számmal, az eredmény egész lesz, hiába akarjuk egy lebegőpontos változóban tárolni:
int a=5;
float b=a/10; //értéke nem 0.5 lesz, hanem 0!
- A hiba elkerülése végett használjunk cast-olást vagy típusos konstansokat:
float b=(float)a/10;
float b=a/10.0f;
- Hiányzó break a switch szerkezetben
- A C++-ban a switch szerkezetének egyszerre több ága is lefuthat! Ha ezt nem szeretnénk, figyeljünk rá hogy minden ágat zárjunk le a break; paranccsal!
- T&-át váró függvénynek nem lehet egy 'T' temporális változót odadni, de a VisualC++ fordító nem szól emiatt. Pl:
class Foo {
Foo(Bar&) {}
};
Foo(Bar()) <- Ez nem szabályos, a beadón fordítási hibát ad!! A megoldás: a függvény várjon const Bar&-et, vagy Bar b; Foo(b);
OpenGL hibák
- Az OpenGL sajnos nem túl típusos, így ha egy függvényt rossz paraméterekkel hívunk meg, akkor az általában nem generál fordítási hibát, hanem szimplán csak nem működik.
- Az ilyen hibák megtalálása nagyon nehéz, mert csak annyit látsz, a program nem működik, de nem szól, hogy mivel van gondja.
- Szerencsére az OpenGL készítői gondoltak erre, és az ilyen hibák beállítanak egy flaget amit le lehet kérdezni. Ezt a glGetError() függvénnyel lehet megtenni. A függvény ömagában csak egy számot ad vissza, amit nehéz értelmezni, de itt van egy wrapper, ami emberileg is fogyaszható formába írja ki, hogy mi történt. Ezt persze a beadón nem lehet használni, de az otthoni változatba debuggoláshoz sokat segíthet.
#include <iostream>
#include <cstring>
#define CheckError() __CheckError(__FILE__, __PRETTY_FUNCTION__, __LINE__)
inline void __CheckError(const char *file, const char *func, int line) {
GLenum error = glGetError();
if(error != GL_NO_ERROR) {
std::cout << "\n---------========={[ ";
int err_len;
switch(error) {
case GL_INVALID_ENUM:
std::cout << "GL_INVALID_ENUM";
err_len = strlen("GL_INVALID_ENUM");
break;
case GL_INVALID_VALUE:
std::cout << "GL_INVALID_VALUE";
err_len = strlen("GL_INVALID_VALUE");
break;
case GL_INVALID_OPERATION:
std::cout << "GL_INVALID_OPERATION";
err_len = strlen("GL_INVALID_OPERATION");
break;
case GL_STACK_OVERFLOW:
std::cout << "GL_STACK_OVERFLOW";
err_len = strlen("GL_STACK_OVERFLOW");
break;
case GL_STACK_UNDERFLOW:
std::cout << "GL_STACK_UNDERFLOW";
err_len = strlen("GL_STACK_UNDERFLOW");
break;
case GL_OUT_OF_MEMORY:
std::cout << "GL_OUT_OF_MEMORY";
err_len = strlen("GL_OUT_OF_MEMORY");
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
std::cout << "GL_INVALID_FRAMEBUFFER_OPERATION";
err_len = strlen("GL_INVALID_FRAMEBUFFER_OPERATION");
break;
};
std::cout << " ]}=========---------\n" << std::endl;
std::cout << "A '" << func << "' függvenyben" << std::endl;
std::cout << "A '" << file << "' fajl " << line << ". soraban\n" << std::endl;
int ending_line_length = err_len + 42;
for(int i = 0; i < ending_line_length; i++) {
std::cout << "=";
}
std::cout << std::endl;
}
}
- A használata:
- A kódba ha leírod, hogy CheckError(); akkor az kiirja, hogy addig volt-e hiba.
- Példa:
1 void SetProjectionMatrix() {
2 CheckError();
3 glMatrixMode(GL_PROJECTION);
4 glLoadIdentity();
5 gluPerspective(60, 1, 1, 50);
6 glMatrixMode(GL_MODELVIEW_MATRIX);
7 CheckError();
8 }
---------========={[ GL_INVALID_ENUM ]}=========---------
A 'void SetProjectionMatrix()' függvenyben
A 'main.cpp' fajl 7. soraban
=========================================================
- Ebből megtudod, hogy a hiba a 2. sorban még nem lépett fel, de a 7. már igen. Tehát a 4 OpenGL függvényhívás egyike a rossz, és az a gondja, hogy rossz enumot használsz (GL_INVALID_ENUM). A 4 függvényből csak kettőben van enum, a két glMatrixMode() hívásban. A kettő közül a 6. sor a rossz, az helyesen glMatrixMode(GL_MODELVIEW) lenne, mert a GL_MODELVIEW és a GL_MODELVIEW_MATRIX enumok nem ugyan arra valók. Ezt a hibát ennek a trükknek a használata nélkül nagyon nehéz lenne megtalálni, hiszen a kód jónak látszik, viszont a képernyőn nem látszódna semmi, vagy ha látszódna is, akkor annak semmi köze nem lenne ahhoz, amit rajzolni akartál. Egy ilyen hiba megtalálása még a legrutinosabbaknak is valószínűleg több órába telne. Ez az egyszerű függvény viszont szinte pontosan megmondta, hogy hol és mi a hiba.