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
87 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

 Overview
 Implications
 Specifications
 Designing With
 Exceptions

 Resources
 Interactions With
 Threads

 Real-Time Systems
 Philosophies
 Potential Pitfalls

 Printable version

 


Potential Pitfalls

Having run into a number of pitfalls in using exception handling, I'd like to offer some things to watch out for. Mainly these focus around understanding the system at run time and conceptualizing program flow control.

Knowing when to re-throw an exception and when not to can sometimes be difficult. It is far better to err on the side of caution and always re-throw the exception unless you are a thread routine or high-level component controlling a real-time system. This is at odds with deciding whether or not you are handling an exception and are not re-throwing it. The issue really hinges on the organization of your components and the level in the system at which they exist. Generally this means the top level components in the package or application should have most of the exception handlers.

The reason being is that if you do not re-throw the exception you are potentially introducing complex flow control that is not apparent when inspecting the code. Although using the technique of logging each exception on its construction can help alleviate this somewhat. This tends to indicate that you should minimize exception handlers in low-level components, using them only for freeing resources. When used this way, they can be replaced with objects on the stack which perform this resource management automatically. 

Also, if you find yourself writing duplicate cleanup code in an exception handler and the regular function body, particularly if this is a catch(...) handler, it can made clearer using the such techniques as applying auto_ptr. Whenever this duplicate code exists it indicates such code is intended to execute under any condition of exit of the current scope; such as a destructor call for local objects on the stack.

A more thorny issue has to do with using multiple catch handlers for different types of exceptions. It is important to remember that each catch handler is a separate branch of execution. If you find yourself doing different things based upon the type of exception you catch you are walking down a potentially dangerous path. Basically, this amounts to two bad things, one is case analysis of object type, generally considered bad and using exceptions as a form of messaging. 

The problem with case analysis is similar to other problems with case analysis. If a new exception object is introduced into the system it may need to have its own handler added and address the new 'case' of exception. Even worse is that if the catch handlers do radically different things then the application's behavior becomes dependent upon what exception it catches. This leads us into the second problem.

Since the program behavior is being guided by the type of exception caught a particular location you can get unexpected behavior caused by a different origin point of the exception. If the same exception is thrown from a different location in code that is called within the try block with the switch statement like catch handlers, then the program flow control will transfer to that handler for a different reason. This new reason may be just different enough from the origin set of assumptions that it causes subtle bugs. This can be particularly troublesome if this occurs in a low-level component that may not be accessible to the client.

Basically unless you write and maintain all the code that your try block executes you cannot safely make assumptions about the origin of an exception. Even if you do, the components are likely to be in different locations and their flow control interactions will not be immediately apparent. This can create a kind of pseudo-event handler mechanism and the problem, in this context, with the event handler model is you do not know the origin of the event, only the occurrence of it. It is far safer to assume that you have no idea what specific exceptions may be thrown from where, unless exception specifications are present, but there's that whole can of worms.

Unfortunately, oftentimes this form of exception type analysis is exactly the only viable solution. The thing to try to keep in mind is to avoid this as much as possible. And it can be mitigated a bit by trying to have those specific exception handlers at the highest level of organization possible. This is also true of re-throwing exceptions as well; let them bubble up as far as possible.

Conclusion

Exception handling is obviously a powerful feature of the C++ language. Although you can walk down the path of not using exceptions at al, their benefits far outweigh the costs. The key is to understand how exceptions operate; how they interact in a game application or other real-time system; where to use them and not use them; and to understand their effect on performance. Exceptions can greatly simplify the development of a C++ package and provide the component writer a way to enforce the assumptions made when the component was implemented.

I would be glad to hear of other's experiences with exception handling. Just send me an email at melkior@mediaone.net

- Steve Crocker