Jump to content
  • Advertisement
Sign in to follow this  
roos

Error handling / better asserts

This topic is 4880 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

Hi, I was thinking about what a good way to handle errors is... The main 2 ways I've seen in game programming are assert() and returning a bool from a function. I've used assert() a lot in my code, but they can be a real pain... Among other things, asserts only exist in debug builds, so extra code is needed for handling release builds, like:
bool SomeFunction( char *pSomeParam )
{
   assert( pSomeParam && "Invalid someParam!" );

   if( !someParam )
   {
      WriteToLogFile( "Invalid someparam!" );
      return false;
   }

   ... etc....
}
; This is annoying, you have to type a lot, and the code becomes diluted with error checking so it's a bit tedious. (You could get rid of some code by moving the assert() into the if() block, and changing it to just assert(false), but that's probably not the best solution...) Another bad thing I've seen in my code and even at a company I worked at is, programmers get lazy and rely so much on asserts or return values that sometimes they don't even bother to put in proper error checking or reporting for release builds, so when the game crashes, no one knows where to start looking... So, my idea is, make a new function like assert (I'll call it "verify") which basically (a) makes for less code you have to type and (b) work for either debug or release builds. The usage of this function would be like this:
bool SomeFunction( char *pSomeParam )
{
   if( !Verify( param1 != NULL, "Invalid someparam!"  ) )
       return false;

   ... etc ...
}
In debug mode, it basically works just like assert(), except that it also returns the value of the expression (true/false) so that you can test against it with the "if". In release mode, reports the error if the condition is false, perhaps through a log file, and then returns the value of the expression. Also, this way, you don't end up with mysterious "release bugs" when some programmer stupidly puts an important function inside an assert() and then it doesn't get called during release. (this happened once and it took another programmer and me 2 hours to track down!) One downside I can think of is that, calling Verify() in really low-level functions or tight loops could get costly. In that case, you could just use assert(). Also, assert() is still useful if you want to do redundant "sanity checks" - for example, using a second method to calculate a value and then comparing against it. I'd appreciate any feedback about this idea and also if you guys have any good "defensive coding" tips I'd love to hear some... Thanks! roos

Share this post


Link to post
Share on other sites
Advertisement
Thanks _ugly, that was a great article! I especially like the way they had for displaying values of the variables

Share this post


Link to post
Share on other sites
i really love this kind of syntax made possible with SMART_ASSERT:

SMART_ASSERT( nUsers < 1000)(nUsers).error( "Too many users!");

let's say nUsers == 1200, this will tell us

a) that "nUsers < 1000" failed (from the initial arg to SMART_ASSERT
b) that "nUsers == 1200" ( from (nUsers) being added )
c) that there was "Too many users!" ( from the message send to .error() )

i also really like the various assert levels, debug, error, etc...

_ugly

Share this post


Link to post
Share on other sites
Asserts arent' a way of handling error's. There a way of detecting error's. To hanlde error's the two most common forms are exceptions are return codes, which is a religous debate unto itself:)

Cheers
Chris

Share this post


Link to post
Share on other sites
OK, what we have here is a misunderstanding of what asserts are for.

Take your "SomeFunction" code:
bool SomeFunction( char *pSomeParam )
{
assert( pSomeParam && "Invalid someParam!" );

if( !someParam )
{
WriteToLogFile( "Invalid someparam!" );
return false;
}

... etc....
}





You should only use one of those methods. Each of them exhibits totally different functionality and expresses different ideas.

Using assert: This says "no caller to this function should ever pass in a NULL pointer". When you use assert, you say that it is programmer error for NULL to ever get into the function and that the calling code is incorrect. In other words - it is the caller's responsibility to never pass in NULL.

The fact that you shouldn't pass NULL into that function should be properly documented (at the most basic level - with a comment on the function definition or prototype).

Using the if method: This says "The caller can pass in NULL variables and we will gracefuly handle it in a specified manner (by returning false, or perhaps throwing, we might also log an error or take other actions)".


As you can see "Callers should never ever pass in NULL" is VERY different behaviour to "Callers can pass in NULL, although it will fail in a specified manner".

This is the reason that assert is only in debug builds - it's strictly for debugging (passing NULL to a don't-pass-in-NULL function is a bug).

You should make a distinction between what behaviour you want your function to exhibit and select one of those two methods.

If your programming team is doing this, then you may want to pass on this advice

Share this post


Link to post
Share on other sites
i use asserts for checking pre-conditions and post-conditions for functions and sometimes iterations.

_ugly

Share this post


Link to post
Share on other sites
nice distinction there, Andrew ;)

asserts are meant to be in debug mode only becoz they are supposed to trap a condition that SHOULD NOT occur. It gets removed in release coz the code is supposed to be fully functional and the assert should always pass in such a scenario, hence the omission.

Share this post


Link to post
Share on other sites
Quote:
Original post by CraZeE
asserts are meant to be in debug mode only becoz they are supposed to trap a condition that SHOULD NOT occur. It gets removed in release coz the code is supposed to be fully functional and the assert should always pass in such a scenario, hence the omission.


You and Andrew are absolutely right- I'm sorry for blurring the distinction between error handling and assertions. As you said, this is the intended way for assert() to act... It works only in debug mode.

However, my point is, maybe that's not the way it *should* be. Here are a few reasons why I think Verify() (or something like it) might be better than assert():

1. Release mode builds aren't always "fully functional". The most obvious example is the builds QA use to test. So when something goes wrong, it should be reported or logged so the programmers have something to go on.

2. Putting assert() to check for NULL pointers being passed in, but doing nothing else, seems like a really bad idea to me personally if you're trying to code defensively. No one ever intentionally passes NULL to a function- they usually pass a pointer which was NULL as a result of some other function failing. So, suppose that the condition that causes the NULL to be returned never appears during development, but then some guy creates a new script for your game, and screws up... NULL gets returned, that's never checked (a mistake), and now you got this NULL floating around in your code un-checked.

3. assert() is a great tool for finding programming mistakes... Firstly, it is a good way to notify you of a problem at the earliest possible moment- as opposed to returning a value, where you might never find out about the problem depending on whether the return value is properly handled or not. assert() also gives you the ability to jump to the spot in code and debug from there. So, if you stick to the rule of never including assert() when you already have code for returning an error value, then you lose out on all that nifty functionality.

roos

[Edited by - roos on June 13, 2005 12:12:04 AM]

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!