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?

  • 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 '11 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. –  Oct 14 '11 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 '11 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. –  Oct 14 '11 at 14:38
  • @Lundin, yes I understood that. Me neither, it DID look suspicious to me, but I assumed since C++ is so well-known all the issues must have been worked out. Honestly, now I am happy I never went around using that. – Shahbaz Oct 14 '11 at 14:59
  • I agree its confusing. Exceptions syntax seems more procedural / functional ("plain c") way to handle errors than object oriented... – umlcat Oct 14 '11 at 17:49
  • @umlcat: not everything that C++ added to C is about OOP. think about the `//` comments, easily reason enough to use a C++ compiler! (until C compilers got the message and borrowed it) – Javier Oct 14 '11 at 18:58
  • @Shahbaz: Why are people who do not use exceptions considered old-fashioned? Exceptions have been around for a long time (e.g. they are found in ML, which is almost 40 years old). – Giorgio Mar 14 '13 at 12:52
  • @giorgio, right. Perhaps it's the younger generation (like me) who started with C, then saw C++ and weren't or aren't familiar with a large number of languages extinct or otherwise uncommon. Since C was born ~40 years ago and is so simple, so I thought exceptions which seem rather complex should have had been invented later. In other words, I should better rephrase old-fashioned with C-fashioned ;) – Shahbaz Mar 14 '13 at 13:27
  • I may be late for this message, but you may also want to [avoid exceptions](http://stackoverflow.com/q/1744070/912144) in the first place anyway. – Shahbaz Mar 14 '13 at 13:32
  • Also, it's funny that I ran into [this answer](http://stackoverflow.com/a/88905) today – Shahbaz Mar 14 '13 at 16:53
  • 2
    The past tense of *throw* is *threw*, not *throwed*. It's a strong verb. – TRiG Dec 08 '14 at 16:58
  • I wish C++ had exceptions specifiers that weren't enforced at all. Just a comment that the compiler would help me ensure is accurate by checking what I throw and what my callees may throw considering the context (an exceptions that's always caught wouldn't propagate to the callers throw specification). This type of checking should be done at compile time. – Petr Skocik Jan 22 '16 at 12:06
  • Such a system would basically solve Martin Sustrik's problem: http://250bpm.com/blog:68 – Petr Skocik Jan 22 '16 at 12:08

3 Answers3

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.

DeadMG
  • 36,794
  • 8
  • 70
  • 139
  • 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...? –  Oct 14 '11 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. –  Oct 14 '11 at 11:56
  • @Lundin: static exception checks could be good, but they would have to be a completely new language feature, with different syntax to the existing half-arsed dynamic checks, since they would be incompatible. There would probably be quite a bit of resistance to adding such a large feature to the language. – Mike Seymour Oct 14 '11 at 11:58
  • 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). – Mike Seymour Oct 14 '11 at 12:01
  • They are removing exception specifiers, that is also a change of the language. Most of my old C++ code will be incompatible. People still insist that C++ should remain compatible with C, but at the same time they don't mind that it isn't compatible with older versions of itself... –  Oct 14 '11 at 12:32
  • Lundin, who are those people? I'd like to tell them that C++ is not compatible with C either. – Tamás Szelei Oct 14 '11 at 13:53
  • @Lundin, true some code will be made incompatible. But having to remove some exception specifiers is really easy compared to having to add them everywhere. – Winston Ewert Oct 14 '11 at 14:04
  • @Tamas The usual conservative bunch who struggle against every removal of backwards compatibility. The same people you should blame for function-like macros, trigraphs and every other such abomination. –  Oct 14 '11 at 14:16
  • 2
    @Lundin: Exception specs have not been removed, just deprecated. Legacy code that uses them will still be valid. – Mike Seymour Oct 14 '11 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 '11 at 19:06
  • 1
    Alright, I remain unconvinced that exception specifiers are bad. But I'll accept this answer as the most correct one as it addresses performance, which I hadn't considered. –  Oct 17 '11 at 06:41
  • We really should add something regarding `noexcept`to this answer :-) – Martin Ba Oct 17 '11 at 07:42
  • @Martin: No, I don't think so. At all. – DeadMG Oct 17 '11 at 15:28
  • @WinstonEwert Nothing is easy when you're sitting on a 1000 kLOC codebase. – quant_dev Oct 18 '11 at 09:49
  • @quant_dev: In this case, I disagree. Not only is this not a breaking change at all, but even if it was, you could solve it with a simple grep. – DeadMG Oct 18 '11 at 10:30
  • 1
    @DeadMG What if you have there, somewhere, a piece of code which relies on `unexpected()` function being called when the exception specification is violated? I know it'd be bad practice, but having working bad code is better than having broken nice code. – quant_dev Oct 18 '11 at 12:17
  • @quant_dev, not its not easy. But my point was that it is easier then what Lundin seems to want which is to force exception specifiers everywhere. Comparatively, removing exception specifiers is trivial. – Winston Ewert Oct 18 '11 at 14:00
  • Although I appreciate the efforts, this answer is not much useful. All I got was a superficial, vague sense of understanding without knowing the actual details.I would redirect people to this article by Herb Sutter instead http://www.gotw.ca/publications/mill22.htm – User 10482 Apr 21 '22 at 20:21
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?

Kaz Dragon
  • 878
  • 5
  • 12
  • 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 '11 at 13:44
  • Tough luck. Embarcadero/Borland gives a warning for this: "throw exception violates exception specifier". Apparently GCC doesn't. There is no reason they couldn't implement a warning though. And similarly, a static analysis tool could easily find this bug. –  Oct 14 '11 at 13:47
  • 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()`. – David Thornley Oct 14 '11 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. – David Thornley Oct 14 '11 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 '11 at 13:54
  • @David To clarify: the argument, as written in the original post, is that if the function itself explicitly throws the wrong expression, the compiler _can_ find it. Mine did, as I already cited. However, if I call a function throwing the wrong type back at me, the compiler can't find that. –  Oct 14 '11 at 14:21
  • @Kaz You do have a point that if you know that exceptions are being thrown from a specific function / set of functions, but you don't really care why, as in your save example, then indeed exception specifiers don't matter. But the question then is: how do you know that the save functions actually throw exceptions? What if they aren't properly documented with comments etc, do you add a try-catch and error handling just in case? –  Oct 14 '11 at 14:28
  • 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 http://stackoverflow.com/questions/4025667/what-circumstances-should-someone-try-catch-does-this-apply-to-libraries/4025728#4025728 . 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 '11 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.

CesarGon
  • 2,981
  • 1
  • 22
  • 26
  • 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 '11 at 07: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 '11 at 08:10
  • Maybe I sloppily read the interview. But I understood the points wrt. scalability to be closely related to the issue with checked exceptions that they propagate up the call chain and need to be stated (in the signatures) of intermediate functions that are not otherwise concerned with these exceptions. – Martin Ba Oct 17 '11 at 08:16
  • @Martin: Well, there is a relationship, indeed. But despite the fact that in C++ you may as well ignore an exception spec beyond the local function call boundary, I don't think it would be good practice; quite to the contrary, it would be very confusing! That's why I say that the same principles are applicable to C++ as well. But I get your point and I do agree that the C++ and Java languages, from a technical perspective, behave very differently. :-) – CesarGon Oct 17 '11 at 08:20
  • 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 '11 at 09: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 '11 at 16:01
  • 4
    @CesarGon Not handling exceptions is also making a choice how to handle them. – quant_dev Oct 20 '11 at 11:44
  • It seems to me that checked exceptions would be good if the "checkedness" of an exception was a function of the throw site, catch site, and instance, rather than being a function of the class, and if there were an easy way to specify that a checked exception which couldn't be properly handled should bubble up the call stack as its unchecked equivalent. Under such a model, code which calls a method and catches a "checked" `InvalidOperationException` could know that if the class was abiding by its contract, the exception was thrown for the reason specified thereby. As it is, ... – supercat Aug 16 '12 at 22:02
  • ...there's no decent way for code which would want to catch an `InvalidOperationException` which occurs for the expected reason, to avoid catching such exceptions which are thrown by methods called by the class, for reasons the class wasn't expecting. Do you know if any languages do something like that? Even without checked exceptions, exception types attempt to encode way too much information; IMHO, using exception type as the primary criterion for determining whether an exception should be caught may a popular idea with framework designers, but it's a bad design. – supercat Aug 16 '12 at 22:06
  • @supercat: No, I don't know any such languages, sorry. Regarding exception type as criterion for determining whether or not to catch, you can always use a single exception class in your app and use data from its properties to decide. – CesarGon Aug 17 '12 at 01:57