C++ Exceptions vs other error handling¶
In the following I will briefly point out how and when to use exceptions and compare them to other error handling techniques.
When to use exceptions¶
One can differentiate between logic and runtime errors. Logic errors are programmer mistakes. Those can be handled well using assertions (even though one could use exceptions for that, too). Runtime errors are problems that occur at the runtime and cannot be prevented by the programmer, for example out of memory errors or if a necessary device is not attached to the computer. Those cannot be handled well with assertions, for several reasons. One reason is that you usually turn assertions off when you ship the product, but runtime errors may occur on the client's pc even they did not on yours. Another is that assertions will crash the programm and print something to standard out, which means even if you leave them on the user will not see the error message (if he is not running from console) and if he does it might not be very helpful (he does not care which line of code crashed).
Runtime errors can be handled using return codes, as can be seen in DirectX. The problem of this is whenever you call a function you have should check the return code and act upon it. It gets even more ugly if you call multiple subroutines in a function and both fail non fataly. Then you have to error codes and have to somehow pass them on to your caller. Most of the time the simple solution is just not to check error codes, with the risk that the program crashes somewhere in a totally different piece of code because a bug was not discovered. Furthermore you have to know from the beginning how you want to report errors. If you print to standard out and later decide you want message boxes, you can go to every error message and change the code.
This is where exceptions come in. If you throw an exception somewhere deep down the call stack, it will be passed up until an exception handler is found. Everything that is unwinded on the way is cleaned up (you do not leak memory) and you can, for unhandled exceptions, at the outmost loop add a catch all handler that will retrieve the error message and print it in whatever way you want - and this can be changed in one line. The price you pay for this is some performance, but it is negligible if you do not run the hardware at the very edge (and we won't).
If you look at Input\Mouse.cpp it has a very complete exception handling now. The reason why it is relatively ugly is that DirectX uses return codes - if they would throw exceptions you would have to do zero error handling. I also know that it is unrealistic to make every class as robust as Mouse. But I am sure there are many situations where exceptions will make our live easier - and encourage you to use them for runtime errors.
How to use exceptions¶
Here is an example of how to throw an exception:
#include <stdexcept>
using namespace std;
/**
* @brief initialize the mouse
* @throws runtime_error if there is not enough memory
*/
void InitMouse(){
HRESULT hr = directInput->CreateDevice(GUID_SysMouse, &m_pDevice, NULL);
if(DIERR_OUTOFMEMORY == hr)
throw runtime_error("Could not create mouse device - out of memory!");
}
If you want to catch an exception, use:
void InitGame(){
try {
InitMouse();
}
catch (runtime_error e) {
//do something about it (free memory, print an error message)
}
}