Jump to content
  • Advertisement
Sign in to follow this  
pixelhigh

Thoughts on error handling?

This topic is 4844 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have been thinking a bit on error handling in C++ and am looking for some input. Usually I have been slighty sloppy and not provided "foolproof" error handling in my apps. Sometimes just reporting the basic problem to the console and exiting ungracefully... I'm trying to remedy this though but I can't decide on what path to choose. The most obvious solution would be to use exceptions since they after all are a part of the language but I haven't seen many people use it in time-critical appliacations or libs. Besides, I have quite limited experience with exceptions and have no idea on how it scales. The other somewhat reasonable (and time-tested) choice seems to be to use C-style error code return values which to me feels somewhat more straightforward. So... any input on this? Dos and Don'ts wheb designing game-sized apps and frameworks? I guess this might have been discussed before and would greatly appriciate pointers to good threads on this board or other. Cheers!

Share this post


Link to post
Share on other sites
Advertisement
If by exceptions you mean 'try' and 'catch' then i dont use these very often either and i would say is not the first step to more secure code. You can avoid many errors by simple, good coding practices.

For example,

- minimising the passing and returning of pointers to and from functions helps, using references is a strong alternative.
- Using the STL whenever collections of things are needed, since STL is stable.
- A good book is Exceptional C++ by Herb Sutter. This has helped me fine tune and make my code more exception safe.

As regards to general error handling i am yet to find a solution to reporting errors that i am completely happy with. One method i tried was a singleton that was used to write the errors to a file. Another was an error handling object passed between objects.

Hope that helps,

ace




edit by kSquared: fixed broken link

Share this post


Link to post
Share on other sites
Error handling is an interesting topic.

There are different methods, some of them you have already mentioned:
- exceptions
- functions always return error code (like Windows API)
- functions return results or error code (Unix/DOS API, i.e. value >= 0 means okay, negative value means error-code)
- homebrewed exceptions (may be more lightweigth than C++ exceptions, but unsafe if implemented via goto)
- SEH (structured exception handling)

I think there is no error handling method that beats all the others. It more depends on the current case what you need.

The second and third alternative are very similar. The second one always returns an error code. You can ignore it, if you don't care about error handling. But it requires an additional parameter for the function call, if the function wants to return data.
The third alternative (found in Unix API) uses the value range of a data type to encode return values and error code. For instance an open() function will return a positive value (the file handle) if the call succeeded, and a negative one if the call failed. Without requiring an additional parameter in the function call for the result, this method is usually slightly faster. A drawback is, that often different return values yield error codes (i.e. if a function can return values from -100 to +100, a value of 999 can mean an error). After all, the third method is not recommended, as the speed gain is not worth the increased complexity of remembering error codes.

When programming in C++, excpetions (the first method) provide you with a powerfull feature. Imagine the case where you have a function call loadData(), which in turn calls loadImages(), loadSounds() and loadModels(). If one of the loading functions fails, then the game cannot start. With error codes returned by a function, you would have to check after each function call, making your code complex and error prone. But using exceptions allows you to simply call each function, and if there was an error, jump directly out to the code that cares.
Exceptions should be used like that what they are called, in exceptional cases. Don't use them to return values, but use them like in an open() function to indicate that the file did not exist.


A general guideline for error handling is this:
If the information known at the point where the error happens can let you handle the error, then handle it, otherwise request some instance up the chain for error handling.

Share this post


Link to post
Share on other sites
I personally quite like the more Linuxy approach. If there is an error in a function then return -1 (to signify failure) then set a global error string to the actual error message. After the call test the return value and if it is -1 then print the global error message.

This saves worrying about specific return types.

ace

Share this post


Link to post
Share on other sites
Quote:
Original post by ace_lovegrove
I personally quite like the more Linuxy approach. If there is an error in a function then return -1 (to signify failure) then set a global error string to the actual error message. After the call test the return value and if it is -1 then print the global error message.

This saves worrying about specific return types.

ace

That is, IMO, a very poor way to handle errors. Windows does this as well, and it drives me nuts having to check a seperate function, and then look up the return value every time something returns -1 or whatever. This is of course naturally my opinion. One of the things this method does have going for it is that error messages are standardized, which can be a plus.

The way I've been doing error handling so far is one of two ways. If the problem is a failure like invalid inputs, NULL pointers, or whatever, I return a specified failure value. If it is not something that the end user had anything to do with, such as a failure in OS functions, I wig out and throw an exception. Or I just throw an exception because the problem is particularly bad. Because all my code is RAII, there is never a problem with leaked memory, undestroyed objects, uncalled important code, etc. It actually quite hard to define exactly what I do, and I'm afraid I didn't explain to well.. But I hope you get the point [smile]

Share this post


Link to post
Share on other sites
Quote:
Original post by SirLuthor
Quote:
Original post by ace_lovegrove
I personally quite like the more Linuxy approach. If there is an error in a function then return -1 (to signify failure) then set a global error string to the actual error message. After the call test the return value and if it is -1 then print the global error message.

This saves worrying about specific return types.

ace

That is, IMO, a very poor way to handle errors. Windows does this as well, and it drives me nuts having to check a seperate function, and then look up the return value every time something returns -1 or whatever. This is of course naturally my opinion. One of the things this method does have going for it is that error messages are standardized, which can be a plus.

The way I've been doing error handling so far is one of two ways. If the problem is a failure like invalid inputs, NULL pointers, or whatever, I return a specified failure value. If it is not something that the end user had anything to do with, such as a failure in OS functions, I wig out and throw an exception. Or I just throw an exception because the problem is particularly bad. Because all my code is RAII, there is never a problem with leaked memory, undestroyed objects, uncalled important code, etc. It actually quite hard to define exactly what I do, and I'm afraid I didn't explain to well.. But I hope you get the point [smile]


Fair point [smile]

Share this post


Link to post
Share on other sites
I'm strongly considering using boost::tuple to handle error values. I despise mixing errors with actual return values, and this seems like a fairly straight forward alternative.


tuple<double, error_type> asin(double x)
{
if(abs(x) > 1.0)
return tuple<double, error_type>(quiet_NaN(), domain_error);
return tuple<double, error_type>(/*code*/, ok);
}

int main()
{
double ans;
error_type err;
tie(ans, err) = asin(1.5);
if(err != ok) {/*do something*/}
//I know this won't error...
tie(ans, tuples::ignore) = asin(0.3);
}


Still deciding if the extra hassle at the return site is worth it.

CM

Share this post


Link to post
Share on other sites
Maybe the general structure for an error aware function could be like this:

ReturnType function(P1 p1, P2 p2, ..., Pn pn)
{
// check parameters for validity
...
if (pi_is_invalid)
throw ParamterException;
...

// declare pointers for dynamically allocated memory inside the function
// set all those pointers to NULL
// also works for other resources
// if possible, try to use ctor/dtor for RAII instead,
// but thats not always the case
M1* m1 = 0;
...
Mk* mk = 0;

// declare and initialise a return value
ReturnType result = 0;

// try to do what the function should do
try
{
// function body
...
m1 = new M1();
...
result = 42;
...
if (an_error_occured)
throw AnError("has occured");
}
// if something went wrong, free
catch (...)
{
// free resources
delete m1;
...
delete mk;

// rethrow the exception
throw;
}

// return the result on success
return result;
}




[Edited by - nmi on August 11, 2005 9:36:30 AM]

Share this post


Link to post
Share on other sites
I am no expert, and this might be bad advice.

That said, I try and make sure that my errors fall distinctly into two categories: recovered or fatal.

Because personally, I think that placing checks 'oh did this work? no, how did it not work?' around called functions is messy, tedious, and prone to be ignored by lazy coders [like me]. Essentially, when calling a function I want one of three things to occur:

1- Fatal, unrecoverable error. Something like memory exhaustion, failure to initialize the display... standard stuff. I want the function that decided it was fatal to handle it, and not return.

2- Success! Function returns a nice valid value.

3- Sort of failure. To me, these include invalid parameters, a 'find' sort of function not finding anything, a texture loader not finding a file... Here I return a failure value. This is almost always a null pointer or 0 or "".

Something that is still valid, but if/when re-used to another function will trigger the standard 'invalid parameter' checks. And when passed to a non-returning function is ignored.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!