Sign in to follow this  
beebs1

Handling Errors

Recommended Posts

Hiya, I'm designing the architecture for my engine, and I have a question I was hoping someone could comment on/suggest an opinion? The problem I'm having is that I'm not not entirely sure how to handle errors. My old engine had a global IEngine class, which had a ReportError() method. The idea of a central 'Engine' class seems completely redundant though - my new design comprises a DLL containing the components (Window, Renderer, etc.) and factory functions to create them, which shifts the responsibilities of the old 'Engine' class onto the client (main loop, etc.) The methods for error handling which I've considered are using return codes and using exceptions. I don't particularly like either of them in this case, as the list of return codes tends to get very long and difficult to maintain. Throwing an exception will require the client code to catch some form of custom error class or perhaps a string, and likely half of the client's code will be enclosed in try/catches, which doesn't seem optimal or very natural. I could just return true/false, and provide a GetLastError() function, similar to Windows. If anyone could give any suggestions, or what describe what has worked for them, it would be much appreciated, thanks [smile]

Share this post


Link to post
Share on other sites
I wouldn't use a boolean return; users will want to know what is wrong, no just that it did go wrong.

If you plan to let a function fail then return a meaningful error code. If you are going to let it continue without doing what it is expected to, then set an error message, a la glGetError();

Share this post


Link to post
Share on other sites
Quote:
Original post by erissian
I wouldn't use a boolean return; users will want to know what is wrong, no just that it did go wrong.


Hence:

Quote:
Original post by beebs1
and provide a GetLastError() function, similar to Windows.


OP: I vote for a Windows-esque GetLastError() function. That way, if all you care about is if something succeeded/failed, you get it easily, and if you want more info, it's available.

Share this post


Link to post
Share on other sites
Quote:
Throwing an exception will require the client code to catch some form of custom error class or perhaps a string, and likely half of the client's code will be enclosed in try/catches, which doesn't seem optimal or very natural.


No, it doesn't.

That's abuse of exceptions as return codes.

Exception means application stops. Or a subsystem stops. Crash. Bad thing(tm) happened. For example, the 3D graphics driver fails to initialize. Resource fails to load.

So exception handlers are few and far in between around critical subsystems. While testing the code I believe the overhead from not handling an error to using a std::exception was around 4% tops in extremly tight network code that needed to check for valid data some 100,000 times a second or throw an exception. But there was only one try/catch block. The total number of places where exception could be thrown (due to templates) is in hundreds (but compiler is smart about it, so this isn't a problem).

The problems with exceptions in C++ come from the way they are implemented (or incompletely define, I forget which). But exceptions are a completely valid, and IMHO much better way to handle errors than return codes for almost all cases.

Share this post


Link to post
Share on other sites
Thanks for the replies - I'll go with the boolean return and a GetLastError() function.

Do you think it would be acceptable to use a global for the error code/string which will be returned by GetLastError? I'm fairly suspicious of using globals, but perhaps in this case it could be OK. I'm thinking something like the following:


// error.h

int g_lastError;

__declspec (dllexport) int __stdcall GetLastError();

void SetLastError( int iErrorCode );


That way the client can retrieve the last error code, and the DLL code can both set and retrieve it.

Perhaps even a singleton class would be OK.

Any thoughts? Thanks again :)

Share this post


Link to post
Share on other sites
Quote:
Original post by beebs1
Thanks for the replies - I'll go with the boolean return and a GetLastError() function.

Do you think it would be acceptable to use a global for the error code/string which will be returned by GetLastError? I'm fairly suspicious of using globals, but perhaps in this case it could be OK. I'm thinking something like the following:

...

Perhaps even a singleton class would be OK.


Will your code be multi-threaded?

If so, how will you handle access to global variables and global error state?

Share this post


Link to post
Share on other sites
Antheus - good point, parts of the sound system are likely to be on a seperate thread. I'm not sure how to get around that, unless I have two functions GetLastError() and GetLastSoundError(). I guess that might need a critical section.

Rip-off: You're right of course, neither do I. Please excuse that suggestion...

Thanks for the replies.

Share this post


Link to post
Share on other sites
Quote:
Original post by beebs1
Antheus - good point, parts of the sound system are likely to be on a seperate thread. I'm not sure how to get around that, unless I have two functions GetLastError() and GetLastSoundError(). I guess that might need a critical section.


What about deadlocks?

How will performance be affected? You'll need to lock entire application, including rendering thread. Or, vice-versa, lock the sound thread in rendering thread.

What happens if you add other threads? GetLastXX1Error()? GetLastXX2Error()? GetLastXX3Error()?

How exactly will locking work? It's not writing to the variable that's the problem. It's locking the value for entire time during setError() and the time that getLastError() will get called. What happens, if another problem happens in between these two calls?


Disclaimer: I'm a strong advocate of exceptions (while being well aware of the problems they can cause).

Return values are very C-ish concept. They get used in certain situations, but exceptions provide a decent alternative without many of the problems.

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
What about deadlocks?

How will performance be affected? You'll need to lock entire application, including rendering thread. Or, vice-versa, lock the sound thread in rendering thread.

What happens if you add other threads? GetLastXX1Error()? GetLastXX2Error()? GetLastXX3Error()?

How exactly will locking work? It's not writing to the variable that's the problem. It's locking the value for entire time during setError() and the time that getLastError() will get called. What happens, if another problem happens in between these two calls?


Ugh, I have a headache ;)

Thanks for your input Antheus. How would you handle this using exceptions? Define your own exception class (classes?) and require the client to use try/catch blocks? Or perhaps throw an error code or std::string describing the error.

I think exceptions are probably the best way of solving this, as long as I don't overdo it.

Thanks again.

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
What about deadlocks?

How could what he is proposing result in a dead lock? You lock whatever variable contains the error code, replace its content and then unlocks the critical section. It doesn't acquire any additional locks while in the critical section.

Quote:
How will performance be affected? You'll need to lock entire application, including rendering thread. Or, vice-versa, lock the sound thread in rendering thread.

Yes if they both encounter an error at once he will have to wait for about the time it takes to set the error code. As long as he expects less than 100 errors per second then it shouldn't be a problem.

Quote:
What happens if you add other threads? GetLastXX1Error()? GetLastXX2Error()? GetLastXX3Error()?

I don't know what he'll do, but he could have a function returning a list of pairs where each pair consists of the thread id and that threads error code. I'm not saying that is the optimal solution, but for an arbitrary number of threads it's the natural extension of the idea.

Quote:
Return values are very C-ish concept. They get used in certain situations, but exceptions provide a decent alternative without many of the problems.

Has the OP even informed us what kind of errors he is intending to use this for? I agree exceptions is the right tool for "computer on fire", "sound card unplugged" and "file not found", but if he is intending to use it for something which happens fairly frequently then I wouldn't use exceptions.

To the OP: Think about what you're doing and what is the best option for that exact thing. Don't just take whatever options is proposed because someone thinks it's a good idea. Exceptions are great if you want to show something exceptional happened (something which wasn't expected to happened), but error codes are good when you expect something to happen pretty frequently.

Share this post


Link to post
Share on other sites
Fair enough.

I'm thinking of using exceptions for errors which make it impossible for the program to continue - if the Direct3D device could not be created, etc.

For errors which are not necessarily showstoppers, the plan is to log the error and continue. For example, if a texture file could not be found, it might load a default one, say a black and white chequer, and continue.

I've noticed that Direct3D only has about 15 error return codes, which suprised me.

Anyway, thanks for all the replies, that's given me a lot to think about [smile]

Cheers.

Share this post


Link to post
Share on other sites
Quote:
Original post by beebs1
For example, if a texture file could not be found, it might load a default one, say a black and white chequer, and continue.

Don't do this. The error needs to be corrected so you should notify the programmer as soon as possible. In the final version of your software you may just use a default texture because the user couldn't do anything anyway, but while developing make sure to bail out as early as possible so you are still able to see the contents of variables, functions called, where in the code you are, etc.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this