53

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?

14
  • 32
    I am tempted to downvote this as "rant, disguised as question". You ask three valid points about E.specs, but why do you bother us with your C++-is-so-annoying-and-I-like-C-better ramblings?
    – Martin Ba
    Oct 14, 2011 at 12:55
  • 12
    @Martin: Because I want to point out that I'm both biased and not up to date with all the details, but also that I regard the language with naive and/or not yet ruined eyes. But also that C and C++ are already incredibly flawed languages, so one flaw more or less doesn't really matter. The post was actually much worse before I edited it down :) The arguments against exception specifiers are also quite subjective and therefore it is hard to discuss them without getting subjective yourself.
    – user29079
    Oct 14, 2011 at 13:37
  • 1
    SpanishInquisition_t! Hilarious! I personally was impressed by the usage of stack frame pointer to throw exceptions and seems like it can make the code much more cleaner. However, I never ever actually wrote code with exceptions. Call me old fashioned, but the return values work just fine for me.
    – Shahbaz
    Oct 14, 2011 at 14:28
  • @Shahbaz As one can read between the lines, I'm quite old-fashioned too, but still I have never actually questioned whether exceptions by themselves are good or bad.
    – user29079
    Oct 14, 2011 at 14:38
  • 2
    The past tense of throw is threw, not throwed. It's a strong verb.
    – TRiG
    Dec 8, 2014 at 16:58

3 Answers 3

51

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.

18
  • 2
    But the run-time check isn't the fault of the exception spec as such, but rather of whatever part of the standard that states that there should be a termination if the incorrect type is thrown. Wouldn't it be smarter to simply remove the requirement leading to this dynamic check and let everything be evaluated statically by the compiler? It sounds like RTTI is the true culprit here, or...?
    – user29079
    Oct 14, 2011 at 11:06
  • 9
    @Lundin: it is not possible to statically determine all of the exceptions which could be thrown from a function in C++, because the language allows arbitrary and non-deterministic changes to the control flow at runtime (for example via function pointers, virtual methods and the like). Implementing static compile-time exception checking like Java would require fundamental changes to the language and a lot of C-compatibility to be disabled.
    – user31664
    Oct 14, 2011 at 11:56
  • 3
    @OrbWeaver: I think static checks are quite possible - the exception spec would be part of a function's specification (like the return value), and function pointers, virtual overrides, etc. would have to have compatible specs. The main objection would be that it's an all-or-nothing feature - functions with static exception specs could not call legacy functions. (Calling C functions would be fine, since they can't throw anything). Oct 14, 2011 at 12:01
  • 2
    @Lundin: Exception specs have not been removed, just deprecated. Legacy code that uses them will still be valid. Oct 14, 2011 at 15:32
  • 1
    @Lundin: Old versions of C++ with exception specs are perfectly compatible. They will neither fail to compile nor run unexpectedly if the code was valid before.
    – DeadMG
    Oct 14, 2011 at 19:06
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?

8
  • 4
    @Lundin it compiled without warnings. And no, you can't. Not reliably. You can call via function pointers or it could be a virtual member function, and the derived class throws derived_exception (which the base class can't possibly know about).
    – Kaz Dragon
    Oct 14, 2011 at 13:44
  • 1
    @Lundin: It gets a lot harder with polymorphism and templates. Indeed, with polymorphism, it requires whole-program analysis. Take a file with a class A, and a function foo that takes an A *, and has an appropriate exception specification. Now, take another file with class B : public A, where its virtual functions can throw something not thrown by any A member functions, and pass a B * to foo(). Oct 14, 2011 at 13:48
  • 16
    @Lundin: Please don't start arguing and repeating false information. Your question was already something of a rant, and claiming false things doesn't help. A static analysis tool CANNOT find if this happens; to do so would involve solving the Halting Problem. Moreover, it would need to analyze the whole program, and very often the whole source isn't available. Oct 14, 2011 at 13:51
  • 1
    @Lundin the same static analysis tool could tell you which thrown exceptions leave your stack, so in this case using exception specifications still buys you nothing except for a potential false-negative that will bring down your program where you could have handled the error case in a much nicer way (such as announcing the failure, and continuing to run: see the second half of my answer for an example).
    – Kaz Dragon
    Oct 14, 2011 at 13:54
  • 1
    @Lundin A good question. The answer is that, in general, you don't know whether the functions you are calling will throw exceptions, so the safe assumption is that they all do. Where to catch these is a question which I answered in stackoverflow.com/questions/4025667/… . Event handlers in particular form natural module boundaries. Allowing exceptions to escape into a generic window/event system (e.g.), is going to be punished. The handler should catch 'em all if the program is to survive there.
    – Kaz Dragon
    Oct 14, 2011 at 14:46
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.

10
  • 5
    "the points that Hejlsberg makes are perfectly applicable to C++ too" .. Wrong! Checked exceptions as implemented in Java force the caller to deal with them (and/or the caller's caller, etc.). Exception specs in C++ (while pretty weak and useless) only apply to a single "function-call-boundary" and do not "propagate up the call stack" like checked exceptions do.
    – Martin Ba
    Oct 17, 2011 at 7:37
  • 3
    @Martin: I agree with you about the behaviour of exception specs in C++. But my phrase that you quote "the points that Hejlsberg makes are perfectly applicable to C++ too" refers to, well, the points that Hejlsberg makes, rather than the C++ specs. In other words, I am talking here about versionability and scalability of the program, rather than exception propagation.
    – CesarGon
    Oct 17, 2011 at 8:10
  • 3
    I disagreed with Hejlsberg quite early: "The concern I have about checked exceptions is the handcuffs they put on programmers. You see programmers picking up new APIs that have all these throws clauses, and then you see how convoluted their code gets, and you realize the checked exceptions aren't helping them any. It is sort of these dictatorial API designers telling you how to do your exception handling." --- Exceptions being checked or not, you still have to handle the exceptions thrown by the API you're calling. Checked exceptions make simply make it explicit.
    – quant_dev
    Oct 18, 2011 at 9:53
  • 5
    @quant_dev: No, you don't have to handle the exceptions thrown by the API you're calling. A perfectly valid option is to not handle the exceptions and let them bubble up the call stack for your caller to handle.
    – CesarGon
    Oct 19, 2011 at 16:01
  • 4
    @CesarGon Not handling exceptions is also making a choice how to handle them.
    – quant_dev
    Oct 20, 2011 at 11:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.