Errors

Started by
9 comments, last by beebs1 16 years, 9 months ago
Hiya, I'm thinking about the best way of handling errors in my current engine project. I had another thread where it was recommended I use exceptions, but I had couple of questions about this, as I havn't really used them before. Firstly, what sort of exception should I throw? std::exception seems to be a good choice, or should I perhaps create my own exception class? Also, in regard to this, I'm not sure how the calling code would know exactly what went wrong when it catches an exception - maybe the exception contains an error code - does that sound OK? Secondly, when exactly should I use exceptions? For example the renderer currently has this method: bool Renderer::Initialize( /* parameters */ ); If it can't initialize, should it throw, or just return false? Either way, the calling code will wan't to know exactly what went wrong, and I'm not sure of how to do this. I hope that made sense, any help would be very much appreciated. [smile] I wonder, it would be an immense help to see some code that uses exceptions properly, but there doesn't seem to be much about - Irrlicht and Ogre don't seem to be example of good C++ design... Thanks again.
Advertisement
I recommend you check out Thinking in C++, it's a great book freely available for download. Check out the first chapter, Building Stable Systems, in Volume 2, has a very good section on exception handling.
Best regards, Omid
I'd do something like:
void renderer::init(...) {    if (somethingWentWrong)        throw std::runtime_error("somethingWentWrong");}// somewheretry {    myRenderer.init(...);} catch (const std::exception &e) {    log(std::string("Renderer did not initialize correctly: ") + e.what());}

IIRC, std::runtime_error *is* an exception. So you can also maybe inherit from exception for specific ones (although, runtime_error works just the way I like it - for runtime errors).

Obviously you can do this, too:
try { ... }catch (const std::runtime_error &rE) {     // runtime error, oh-oh!} catch (const std::exception &gE) {     // some general exception not handled above}


Etc., etc. Just look up std::exception and all it's buddies.
Thanks for the replies and the linky, much appreciated :)

In the following:

try {    renderer.initialize();    scenegraph.initialize();    something_else.initialize();    // etc...}catch( const std::exception &e ){    log( e.what() );}


What if the calling code wants to know what the problem was - a string isn't really very useful. Also, does that mean there is no need for initialize() to return a value?

Thanks again.
A string is plenty useful, it describes exactly what happened [smile].

Typically exceptions are thrown where the *type* indicates the error.

Eg:
try {   Renderer renderer;} catch( const WhoopsNoScreenAttached &e ) {   // handle} catch( const NoOpenGL &e ) {   // handle}


This is preferred because it is easier to decide which exceptions to catch. For example, imagine we wanted to handle the "no opengl" state, but not the "no screen" error.

With error codes:
Renderer *renderer = 0;try {   renderer = new OGL_Renderer();} catch ( const GenericException &e ) {    if( e == GenericException::NO_OPENGL ) {        // etc    } else {        throw;    }}

With error by type:
try {   Renderer renderer;} catch( const NoOpenGL &e ) {   // handle} // other exceptions will propagate up the call stack automatically
Quote:Typically exceptions are thrown where the *type* indicates the error.


As long as you don't define one type for each type of error.

This is bad:
class FailedToInitializeDriver;class FailedToLockSurface;class FailedToUnlockSurface;class FailedToSwitchBuffer;


The following is more appropriate:
class DriverException;class EngineException;


Then, for every DX or OGL error, you throw DriverException, and for every problem in the engine, you throw EngineException.

This makes sense, since they come from completely different sources.

For fatal exceptions, ones you know you'll never be able to recover from, std::exception will do just fine, extra knowledge won't do you much good, all you need is what happened as a string.
Of course yes. I would rarely have more than 2 catch blocks anywhere.

The point I was trying to make is that you group together exceptions based on how you handle them. Maybe I got that across badly [smile]. Your example is a bit more concrete.
Thanks, that's helpful.

Just to check if I'm getting it, does the following code look OK:

class RendererException : public std::exception;class InputException : public std::exception;void SomeFunction(){ try {    Renderer *pRenderer = new Renderer();    Input *pInput = new Input();    // these might throw    pRenderer->Initialize();    pInput->Initialize(); } catch( std::bad_alloc ) {    Log( "Bad memory allocation!" ); } catch( RendererException ) {    Log( "Renderer failed to initialize!" ); } catch( InputException ) {    Log( "Input failed to initialize!" ); }  return;}


I think that's the general idea? Thanks for the help, I'm trying to get my head around this :)

Many thanks.
Why have separate "Initialize" functions? Just do any initialisation in the constructor, that is what it exists for...

Other than that it looks fine. Maybe try setting your exceptions to have a string that can be printed that indicates the actual problem. If you give your game to other people you are going to want more than just "Renderer failed..." as an error, you'll want to know the exact reason.
Quote:Original post by rip-off
Why have separate "Initialize" functions? Just do any initialisation in the constructor, that is what it exists for...

Other than that it looks fine. Maybe try setting your exceptions to have a string that can be printed that indicates the actual problem. If you give your game to other people you are going to want more than just "Renderer failed..." as an error, you'll want to know the exact reason.


Exactly. Following on from this, make sure that the messages you place inside of your exceptions are meaningful. Put in those messages any data that the person viewing the error message might find useful in debugging the error. Such data might include the current render state if the error is a rendering based error. Or, the data could be the filename of the file that you are trying to open if the error is a file-opening based error.

Just make sure that the data is meaningful and can be used. If you do this, your game debugging will be made far easier.

This topic is closed to new replies.

Advertisement