Just about every piece of example code everywhere omits error handling (because it "confuses the issue" that the example code is addressing). My programming knowledge comes primarily from books and web sites, and you seldom see any error handling in use at all there, let alone good stuff.
Where are some places to see good examples of C++ error handling code? Specific books, specific open-source projects (preferably with the files and functions to look at), and specific web pages or sites will all be gratefully accepted.
Herb Sutter's and Andrei Alexandrescu's book C++ Coding Standards comes with a whole chapter on Error Handling and Exceptions including
- Assert liberally to document internal assumptions and invariants
- Establish a rational error handling policy, and follow it strictly
- Distinguish between errors and non-errors
- Design and write error-safe code
- Prefer to use exceptions to report errors
- Throw by value, catch by reference
- Report, handle, and translate errors appropriately
- Avoid exception specifications
Every topic also includes an example and I found it to be a very valuable resource.
"Use exceptions" vs. "Use error codes" is never as clear-cut as examples suggest.
Use error codes for program flow. If you have an error that is expected, do not throw an exception. E.g. you're reading a file, you may throw an exception for "file not found", "file locked"; but never throw one for "end of file".
If you do, you can never write simple loops, you'll always wrapping code in exception handlers. And don't forget exceptions are very slow, this is especially important in big multi-threaded servers. (Not so important at all in your desktop application).
Secondly, be very careful with exception hierarchies. You may think it's OK to have an Exception
class, then derive a NetException
from it, then SMTPException
for your SMTP class. But unless you hold generic data in the base class, you will always have to catch every type of exception in that hierarchy. E.g. if you put the reason for the SMTP error in your SMTPException
class, you must catch it - if you only catch Exception
types, you will not have access to the SMTPException
members. A good workaround for this problem is to have a string and an int member in the base exception class and only use them, even for the derived types. Unfortunately std::exception
only offers a string :(
Some people say that doing this means you might as well only have a single exception type, especially as you will always catch the base class type anyway.
If you do use exceptions you must take the trouble to populate them with more data than you would with an error code. With errors, you must handle them immediately or they get lost in the code. With an exception, it may get caught many levels away from where it was thrown - like in Roddy's example. DoC
is called, and gets an exception 2 levels in from DoA
. Unless you specify the error to be specific to the code in DoA
, you may think it was thrown from the DoB
function. (simple example, but I've seen code where an exception was handled many levels down the call stack. It was a bstrd to debug. This especially applies to OO programs)
So hopefully, I've given you enough to think about. The simple truth of the matter is that style means nothing in error handling, practicality is everything. If you have to put log statements everywhere an error can occur, then do so. It matters a lot more that you can see where the code went wrong (and what data was being worked with) than you have a elegant exception hierarchy or you've littered your code with exception handlers. If you cannot easily trace the error, your error handling code is useless.
Exceptions are good, use them. But think about what you're doing, do not misuse or overuse them. A misused exception is worse than no error handling at all (as you can grab a crash dump and view the unhandled exception to find the error in seconds. With an exception that is eaten and ignored, you're stuffed).
I've found over the years that the biggest assistant to debugging is logging. Write logs, write lots of logs.
I prefer the exception handling discussed in this article. It results in clean code and avoids explicit creation/deletion of objects just to process exceptions.
http://www.informit.com/articles/article.aspx?p=373339
With C++, you should end up with less visible error handling code anyway, because you can leave a lot of the heavy lifting to Exceptions.
In my opinion the most basic rule with exceptions (and one most commonly broken) is this.
Don't try to catch exceptions unless you have a specific plan to handle them.
With exceptions, you don't have to worry about error codes returned from functions, because well designed functions will just throw exceptions instead.
In C a typical error handling scenario looks like this:
int DoA()
{
if (location == hellInAHandcart)
return ERROR;
else
RETURN OK;
}
int DoB()
{
int err = DoA();
if (err != OK)
return err;
else
return DoSomethingElse1();
}
int DoC()
{
int err = DoB();
if (err != OK)
//Handle My error here in whatever way...
}
While in C++...
void DoA()
{
if (location == hellInAHandcart)
throw Exception("Gone To Hell in a Handcart");
}
void DoB()
{
DoA();
DoSomethingElse1();
}
void DoC()
{
try
{
DoB();
}
catch (Exception &E)
{
// Handle My error here in whatever way...
}
}