No Exceptions - With One Exception
Exceptions are a necessary part of the C++ language, but for most programmers they are worse than
worthless - they are unusable. When exceptions were first added to the language back in the days
before standardization, they were seen as a brilliant improvement over the hideous
setjmp()/longjmp()
facility from ANSI C. Because exceptions unwind the stack when they are
thrown, they call destructors and clean up all of your untidy messes as they go.
|
It didn’t take long for somebody to notice that the emperor had no clothes. In 1994, Tom Cargill published a prescient article in the late C++ Report. Tom pointed out in detail just how difficult it was to write code that actually held up when exceptions were used. Scott Meyers flags this as one of his five Most Important C++ Non-Book Publications…Ever. (Incidentally, Tom is responsible for a piece of wisdom that sounds like a an offhand quip: the ninety-ninety rule. Any project manager who doesn’t understand this rule all the way into his or her bones needs to quit. Now.)
The intervening years have not been kind to exceptions. The problems Tom foresaw in 1994 were real, and while they can be managed, writing exception-safe code is still fraught with peril. As a result, exceptions are basically unused by most C++ programmers. After all, we have to deal with tough problems like writing safe multithreaded apps, understanding template metaprogramming, and dealing with the language’s high-maintenance memory management. The last thing we need is to make use of a feature, that while useful, is basically impossible to use correctly.
Naturally, after this rather verbose introduction, you have probably guessed that I’m using this article to tell you about a simple little utility class I use to help me throw informative, easy to build exceptions in my C++ programs. Yes, exceptions are problematic, but there’s one place I can use them with impunity: to cause a fatal error that aborts my program.
Fatal Errors Considered Exceptional
If you analyze Tom Cargill’s article, or look into any of the additional work that has been done on exceptions up until today, you’ll see that most of the nasty side effects are only a problem for a program that tries to keep running in a predictable and safe fashion while throwing and catching exceptions.
The side effects of code that isn’t quite exception-safe include memory leaks, partially
constructed objects, and invalid containers. All bad things. But the one place they usually don’t
matter to me is when things have degraded in my program to the point where I’m ready to throw in
the towel anyway. That point is when I’ve encountered a fatal error and am aborting the program.
At that point, I throw an exception, which generally percolates all the way up to main()
,
where it is caught, and error message is printed, and the program exits.
Handling fatal errors this way makes for much cleaner code. I generally assume that every method or function called in my program succeeds, and don’t worry about creating special returns with error codes. I blithely march through call after call without checking results, secure in the knowledge that my code is working properly. I know this is the case, because if something went wrong, an exception would have been thrown, and my program would abort.
This error handling strategy is implemented entirely in main()
, which generally follows this
form in one of my programs:
There are a couple of interesting points to note about the error handler you see here.
First, because I am catching std::exception
, I will catch any
errors in the standard library, such as bad_alloc
, logic_error
, runtime_error
, etc. It’s a
good idea to have a top-level try/catch
block for these items anyway, so my use of exceptions
for fatal errors forces good hygiene practices.
Second, I’m using the what()
method to give human-readable
feedback on exactly what happened to send my program off the rails. This method is defined as
virtual
for the base class std::exception
, so all derived classes will support it. There’s
no requirement that they populate this string with great prose, but we can at least expect
implementors to provide something informative here.
My Exception Class
When I first started using exceptions for fatal errors, I kept things simple by just throwing
instances of std::runtime_error
when I wanted to abort my program. I could pass the constructor
of this object a descriptive string as it was constructed, and that same string would be returned
when what()
was called in the exception handler. Typical usage might look like this:
This code ensures that if the user leaves off the necessary command line argument, the
catch
block will print out a usage statement and return an error value to the invoking shell,
which is just what we want. I manage to handle faulty input with just one line of code, don’t
have to set any error variables, no if/then/break clause to print an error and then exit.
It’s tidy and works well.
Of course, it wasn’t long until I ran into situations where I wanted to provide the user a little more information with a fatal error - information that had to be formatted at runtime. I ended up writing a lot of code that looked like this:
This more or less worked, but it added a lot of lines to the code, and as a rule, the fewer
lines, the less typing, the more I like it. In this case I have to construct a separate
std::stringstream
object to hold the formatted error message (or a character buffer if I
choose to go old school and use vnsprintf()
, a second line to do the formatting, and a third
line to construct and throw the exception.
In addition to writing three lines instead of one, I now have to enclose the whole thing in brackets, because the clause following the if statement is multiple lines, which makes the whole thing require five lines of code instead of one!
A Better Way
I needed to find a better way to do this. I first dabbled with a class derived from
std::exception
that used vnsprintf()
to format arguments, C-style. But there were a few
disadvantages to this, the primary one being that it made it hard to take advantage of classes
that have their own overrides to stream classes. In other words, if I’ve bothered to create a
stream override so I can print the contents of class foo
, it’s easy to write that to a stream,
but not so easy to insert it into a buffer being formatted with vnsprintf()
.
So I determined that I wanted to have a class that that let me rewrite the code shown above so that it all fits on one line, like this:
class fatal_error
My first stab at getting this to work was to create a class that used multiple inheritance to
create a class that inherits from both
std::exception
and std::stringstream
:
With this routine I’d just need to override the definition of what()
and I would be in business.
But there was a fatal flaw in this problem: the absence of a copy constructor for
std::stringstream
. I had assumed that the copy constructor wouldn’t be needed, as I was
catching the exception object by reference. My thought was that the compiler would create a
temporary object, pass it along to my catch
clause, and all would be well.
No such luck. In this case, the compiler has the right to make a copy, and even if it doesn’t make a copy, it has the right to insist on a copy constructor even if it is only considering the possibility of making a copy. Strike one.
This One Works
So I needed to make a version of my fatal_error
class that doesn’t try to call the copy
constructor for std::stringstream
. This is accomplished easily enough by using
composition instead of inheritance:
This solves one problem, but creates another. Because fatal_error
is no longer derived from
std::stringstream
, I’ve lost the overloaded operators that insert text into the object before
it is thrown. In other words, my wished-for code shown above won’t compile.
In this case, the solution is simple, I just add a template method to the class:
Now the template class takes care of routing the stream insertion output into the std::stringstream
member I’ve incorporated into my class. Note also that the overloaded insertion operator returns
a reference to the fatal_error object, allowing me to chain
insertions.
Almost Done
There only two more issues to deal with in order for this class to be ready for use. First, I
need to deal with the possibility of a copy constructor being called as this exception is thrown.
Because I can’t copy the std::stringstream object from the old
object to the new, I have to save off its contents into a mutable std::string
member called
mWhat
. The result looks like this:
Finally, I need a good version of what()
that conforms to what is expected for std::exception
.
This is nice and easy:
Wrapped Up
With that, I have an exception that I can use to easily generate fatal error exceptions with as much data as I want. I only have to include a single header file in any code that uses the class, and I can create and throw the exception in a single line of code, which adheres to the C ideologoy of minimal typing.
Life is good.
Complete source code from the single file, fatal_error.h
, is given below. This code should work
in g++ 3.x code, and Visual Studio 2003 and 2005 programs. If you run into problems with
different versions of various compilers, please let me know!