Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
65 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Contents
 Prelude
 What are
 Exceptions?

 Exception Safety
 and Error Handling

 Exception Class
 Final Thoughts

 SmartTest.zip
 Exception.zip
 Printable version
 Discuss this article
 in the forums


Exception class

The Base

Up to now, I have been throwing an int exception. As I said before, you can use throw any type of exception. So let’s define a more useful error reporting class.

Example

class BaseException
{
public:
  BaseException( const char * const err )
  {
    strncpy( buf, err, 128 );
  }

  virtual const char * what()   // get the error string
  {
    return buf;
  }

protected:
  char buf[128];          // store the error string
};

I use a char buffer for simplicity, but I recommend using the STL string class to replace all the strings in your programs.

So now instead of throwing an int, we throw a BaseException object and pass a meaningful error string to the constructor. Later in the catch handler, we can retrieve the error string using the what() function.

Example

try
{
}
catch( BaseException &e )      // note we catch by reference
{
  cout << e.what() << endl;
}

Getting exceptions to work in Windows is the same as in a console program. Just wrap the main message pump function with a try/catch block.

What can we do with it?

Now we have a base exception class, we can use inheritance to create specific types of exceptions. Each derived class can correspond to a different type of error - File not found, Not enough memory, etc.

I will give two examples here - one for dealing with Windows errors and the other for handling SEH exceptions.

Windows exception class

I bet you have noticed Window functions use return codes to signal errors and GetLastError() to find out what the error was. Since we are coding in Windows, we have to adapt to their weak error handling implementation.

You can see the WindowsException class in the sample later on. It just translates the GetLastError code to a system message. It’s pretty simple to understand so you can just look at the source.

SEH Exception class

SEH in VC++ encompasses hardware exceptions too, but I bet you figured that out already. In any case, Microsoft 'suggests’ C++ exception handling to be used over SEH, and has kindly provided us with a means to translate SEH exceptions to C++ exceptions.

The function we are interested in is _set_se_translator. Whenever a hardware exception occurs, the registered translator function will be called. So what we do is throw a C++ exception within the translator function, like so

Example

// the translator function
void SEH_To_C++( unsigned int u, EXCEPTION_POINTERS *exp )
{
  throw int;        // throw an exception of type int
}


void main()
{
  try
  {
    _set_se_translator( SEH_To_C++ );  // register the translator

    // now all hardware exceptions will generate an C++ int
    // exception
  }
  catch(...)  {}
}

Note: _set_se_translator must be called for each separate thread.

Sample

Combining all your newfound knowledge, I present a simple sample with the exception class in action. You will see how we can incorporate and extend the base exception in the Windows environment.

I don’t normally use MFC, but since I’m not getting paid for this, oh well, too bad for the code bloat. Don’t blame me; I’m not the one who wrote the wizard.

The main points to note are

  1. The main file you should look at is BaseException.h. It contains the aforementioned exception classes.
  2. The entire main function is enclosed in a try/catch block, just like a console program.
  3. I generate an exception when the mouse is moved a few times when the program is active. Of course the rest of the MFC Appwizard generated code is not exception safe, but I’m too lazy to fix it.

More! I want more!

I know you want more, so here it is

More information

You can store the line and source file where the exception occurred by passing in two additional parameters. Use the __FILE__, __LINE__ macros.

You can also do a stackwalk inside the exception class too. Take a look at the BugSlayer column by John Robbins at MSDN on how to do this.

Inherit from STL exception class

Instead of defining a new class by itself, your base exception class can be inherited from the STL exception class. This would allow you to catch exceptions from the standard library as well as your exceptions using one catch handler.

Evil Assert

Assertions are evil. No wait, I don’t mean there are not useful, I meant the default assert implementation just calls abort, and that may not be exception safe.

I recommend throwing an exception instead when an assertion failure occurs. You can do this easily by extending the base exception class.

Extending it for DirectX

Many times, I see ingenious hacks being invented for error handling in DirectX. Personally, I’m guilty of those hacks too.

So, this is the latest hack using exceptions for a full screen DirectX application.

  1. Extend another class that takes in a HRESULT. In that class, translate the passed result to a string using either the D3DX library or manually translating the error code. This is similar to the classes I presented.
  2. In the catch handler of the main function, restore the video mode before displaying the MessageBox. This will ensure the Messagebox is always visible without a need to call FlipToGDI at all (which is a slow call).
  3. If the DirectX function call fails, throw an exception.

If you have worked in full screen DirectX applications before, you will probably realize a MessageBox does not always show up. This is precisely the reason why I throw an exception for all the errors, even for assertion failures. When the exception reaches up to the main function, I restore the display mode and then I display the MessageBox describing the error.

Anyway, the above 'hack’ is for DirectX 7.0. I haven’t worked with DirectX 8 yet so I cannot guarantee if there are more 'hacks’ needed.





Next : Final Thoughts