# 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.

## 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 on other sites
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 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 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 on other sites
Quote:
 Original post by ace_lovegroveI 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 on other sites
Quote:
Original post by SirLuthor
Quote:
 Original post by ace_lovegroveI 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 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 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 on other sites

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.

1. 1
Rutin
32
2. 2
3. 3
4. 4
5. 5

• 13
• 9
• 9
• 9
• 14
• ### Forum Statistics

• Total Topics
633324
• Total Posts
3011369
• ### Who's Online (See full list)

There are no registered users currently online

×