Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

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?

share|improve this question
15  
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
2  
@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 '11 at 13:37
    
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. –  user29079 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
show 6 more comments

3 Answers

up vote 28 down vote accepted

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.

share|improve this answer
    
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 '11 at 11:06
4  
@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. –  OrbWeaver Oct 14 '11 at 11:56
2  
@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
1  
@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  
@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
show 12 more comments

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.

share|improve this answer
    
"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 7:37
1  
@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 8: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 8: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 8:20
1  
@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
show 5 more comments

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?

share|improve this answer
    
Fair enough, the compiler will give you a warning. The point is, you can statically check it with a compiler or with a static analysis tool. –  user29079 Oct 14 '11 at 13:43
2  
@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
6  
@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
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 '11 at 14:46
show 4 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.