54

Back in school some 10+ years ago, they were teaching you to use exception specifiers. Since my background is as one of them Torvaldish C programmers who stubbornly avoids C++ unless forced to, I only end up in C++ sporadically, and when I do I still use exception specifiers since that's what I was taught.

However, the majority of C++ programmers seem to frown upon exception specifiers. I have read the debate and the arguments from various C++ gurus, like these. As far as I understand it, it boils down to three things:

  1. Exception specifiers use a type system that is inconsistent with the rest of the language ("shadow type system").
  2. If your function with an exception specifier throws anything else except what you have specified, the program will get terminated in bad, unexpected ways.
  3. Exception specifiers will be removed in the upcoming C++ standard.

Am I missing something here or are these all the reasons?

My own opinions:

Regarding 1): So what. C++ is probably the most inconsistent programming language ever made, syntax-wise. We have the macros, the goto/labels, the horde (hoard?) of undefined-/unspecified-/implementation-defined behavior, the poorly-defined integer types, all the implicit type promotion rules, special-case keywords like friend, auto, register, explicit... And so on. Someone could probably write several thick books of all the weirdness in C/C++. So why are people reacting against this particular inconsistency, which is a minor flaw in comparison to many other far more dangerous features of the language?

Regarding 2): Isn't that my own responsibility? There are so many other ways I can write a fatal bug in C++, why is this particular case any worse? Instead of writing throw(int) and then throwing Crash_t, I may as well claim that my function returns a pointer to int, then make a wild, explicit typecast and return a pointer to a Crash_t. The spirit of C/C++ has always been to leave most of the responsibility to the programmer.

What about advantages then? The most obvious is that if your function tries to explicitly throw any type other than what you specified, the compiler will give you an error. I believe that the standard is clear regarding this(?). Bugs will only happen when your function calls other functions that in turn throw the wrong type.

Coming from a world of deterministic, embedded C programs, I would most certainly prefer to know exactly what a function will throw at me. If there is something in the language supporting that, why not use it? The alternatives seem to be:

void func() throw(Egg_t);

and

void func(); // This function throws an Egg_t

I think there is a big chance that the caller ignores/forgets to implement the try-catch in the second case, less so in the first case.

As I understand it, if either one of these two forms decides to suddenly throw another kind of exception, the program will crash. In the first case because it isn't allowed to throw another exception, in the second case because nobody expected it to throw a SpanishInquisition_t and therefore that expression isn't caught where it should have been.

In case of the latter, to have some last resort catch(...) at the highest level of the program doesn't really seem any better than a program crash: "Hey, somewhere in your program something throwed a strange, unhandled exception.". You can't recover the program once you are that far from where the exception was thrown, the only thing you can do is to exit the program.

And from the user's point-of-view they couldn't care less if they get an evil message box from the OS saying "Program terminated. Blablabla at address 0x12345" or an evil message box from your program saying "Unhandled exception: myclass.func.something". The bug is still there.


With the upcoming C++ standard I'll have no other option but to abandon exception specifiers. But I would rather hear some solid argument why they are bad, rather than "His Holiness has stated it and thus it is so". Perhaps there are more arguments against them than the ones I listed, or perhaps there is more to them than I realize?

3 Answers3

52

Exception specs are bad because they're weakly enforced, and therefore don't actually accomplish much, and they're also bad because they force the run-time to check for unexpected exceptions so that they can terminate(), instead of invoking UB, this can waste a significant amount of performance.

So in summary, exception specs aren't enforced strongly enough in the language to actually make code any safer, and implementing them as specified was a big performance drain.

DeadMG
  • 36,914
22

One reason no-one uses them is because your primary assumption is wrong:

"The most obvious [advantage] is that if your function tries to explicitly throw any type other than what you specified, the compiler will give you an error."

struct foo {};
struct bar {};

struct test
{
    void baz() throw(foo)
    {
        throw bar();
    }
};

int main()
{
    test x;
    try { x.baz(); } catch(bar &b) {}
}

This program compiles with no errors or warnings.

Furthermore, even though the exception would have been caught, the program still terminates.


Edit: to answer a point in your question, catch(...) (or better, catch(std::exeption&) or other base class, and then catch(...)) is still useful even if you don't know exactly what went wrong.

Consider that your user has hit a menu button for "save". The handler for the menu button invites the application to save. This could fail for myriad reasons: the file was on a network resource that vanished, there was a read-only file and it couldn't be saved over, etc. But the handler doesn't really care why something failed; it only cares that it either succeeded or failed. If it succeeded, all is good. If it failed, it can tell the user. The exact nature of the exception is irrelevant. Furthermore, if you write proper, exception-safe code, it means that any such error can propagate through your system without bringing it down -- even for code that doesn't care about the error. This makes it easier to extend the system. For example, you now save via a database connection. Are you going to propagate throw(SQLException) in the function stack all the way up, or just class it as "error" and handle it properly already along with all the other things that could go wrong?

19

This interview with Anders Hejlsberg is quite famous. In it, he explains why the C# design team discarded checked exceptions in the first place. In a nutshell, there are two major reasons: versionability and scalability.

I know the OP is focusing on C++ whereas Hejlsberg is discussing C#, but the points that Hejlsberg makes are perfectly applicable to C++ too.

CesarGon
  • 2,941