Opening Scene
INT. DOWNTOWN CLUB – MIDNIGHT
CODER and USER DEFINED TYPE, both dressed to kill, a little tipsy, and obviously about to close the deal, occupy adjacent bar stools. They lean close to be heard over the atmospheric music enveloping the room.
UDT
(putting down drink and looking deep into CODER’s eyes)
By the way, in my destructor I throw if the file fails to close properly.
CODER
I can live with that.
UDT
But it does mean that you can’t “new” an array of me.
CODER
Good to know, but not an issue since I typically use STL containers instead of arrays.
UDT
Yeah, about those STL containers… we don’t really get along.
CODER
They’re probably just jealous.
UDT
And if you call any code that might throw while I’m on the stack, I’m going to kill your program.
CODER
Wait: you throw exceptions, but if I hook up with you then I can’t call any other code that throws exceptions? That’s crazy! And Evil™.
UDT
Yeah, I get that a lot. Take it or leave it. You know you want me.
CODER
Oh, what the hell… I’ve always been a sucker for dangerous types.
FADE TO BLACK
NARRATOR (V.O.)
We invite you to journey with us now, into the heart of darkness, where we seek out throwing destructors and stare Evil™ in the face. Will CODER survive his encounter with exceptional destruction? The answers lie beyond the frontiers of usual best practice…
Anyone who’s been around C++ for a few years knows this rule: “thou shalt not throw from destructors!” After all, as some experts have written, that would be Evil™. We’ve gotten along fine without throwing destructors, and have always taken it for granted that this nut was simply not worth cracking, but recent newsgroup threads have made us curious. It turns out that, just as elsewhere in the exception-safety universe, this region of space is full of superstition and misunderstanding.
An Innocent Example
Before we venture far afield, let’s review what happens in this simple function. We’ve annotated it with comments that describe what happens when an exception is thrown, and that show where destructors are called implicitly:
1 2 3 4 5 6 7 8 | void foo() { alpha a; // if an exception is thrown, goto line 8 beta b; // if an exception is thrown, goto line 7 bar(); // if an exception is thrown, goto line 6 // b.~beta() // a.~alpha() } |
In other words, whatever else happens, before leaving foo(), the
program executes implicit calls to destroy any local variables that have
been fully-constructed, in reverse order of construction. These
destructions occur either as part of normal execution, or, if an
exception is thrown, as part of stack unwinding.
What’s in a Destructor?
Just to complete the picture, let’s quickly review destructor semantics.
An object’s destruction executes any destructor body for the
most-derived class, then destroys each non-static data member in reverse
order of construction, and finally destroys all the base classes, again
in reverse order of construction. So if beta is defined this way
struct base { ~base() { std::cout << "~base" << std::endl; } }; struct member { ~member() { std::cout << "~member" << std::endl; } }; struct beta : base { ~beta() { std::cout << "~beta" << std::endl; } member m; };
we can expect its destructor to print
~beta ~member ~base
Now, back to exceptions. If an exception is thrown, it is propagated up the call stack to a matching catch block (assuming one exists). On the way, it “unwinds” through each enclosing scope, destroying any local objects—say it with me—in reverse order of construction.
Evil Dead, Zombie Objects, and the Necronomicon
Note that the lifetime of an object extends from the (successful) completion of its constructor to the initiation of its destructor. In other words, no matter what else happens, as soon as we enter an object’s destructor body, its lifetime has ended. The lifetimes of its sub-objects have not yet ended, but will end as soon as their destructor bodies are entered.
Now consider what happens in our original example if bar() doesn’t
throw, but b’s destructor body does. The standard says that b’s
sub-objects are destroyed as part of normal stack unwinding. That’s
right! The only difference the throw makes in what actually happens
to b is that the rest of its destructor body is skipped.
Then, as before, it is a’s turn to be completely destroyed:
destructor body, data members, then base classes, all in reverse order
of construction. And again, the exception is propagated and the stack is
unwound.
In other words, the semantics of destructors that throw are well defined by the standard and are similar to those of any other throwing function. When a destructor body throws, the object—and all of its sub-objects—are destroyed. They don’t continue to exist in some dead-but-not-destroyed zombie state. That would be Evil™.
If you find this a bit disorienting, that’s understandable. In most cases, when a function throws, it means the function failed to achieve its purpose, so it’s natural to think that when a destructor throws, the object is not fully destroyed. Fortunately, it doesn’t work that way. Once the destructor is called, the object is gone, no matter what exceptions might be thrown. No ghost, no zombies, no (ahem) exceptions.
You might also reasonably worry that the throw from within the
destructor body could cause us to skip important code in the
destructor body, thereby causing a leak or violation of invariants… and
if so, you’d be right. However, you won’t need a prosthetic
chainsaw to ward
off these dangers. It’s just an ordinary, run-of-the-mill
exception-safety problem—the kind we might face in any ordinary
function—that you can address in the usual ways. The programmer is
responsible for writing exception-safe code in destructors, the same as
anywhere else. Just make sure that, if something partway through the
destructor body throws, any later operations that still absolutely
must happen are managed by a catch block—or, better yet, move them
into an RAII object’s destructor—and you’ll “be fine.”
The Real Problem
So if destructors that throw are supported by the standard and their semantics are well-defined, why are they feared? It’s not just the professional experts who say to avoid them: the standard library itself responds with undefined behavior if the destructors of container elements throw. It’s hard to imagine a much greater disincentive.
Well, there is a real problem here, but it’s not the intractable mess that it’s often made out to be. When a destructor throws during stack unwinding, there is already an exception “in flight.” That’s not a problem by itself: the compiler actually knows how to deal with it. For example,
void g() { throw 2; } struct X { ~X() { try { g(); } catch(...) {} } }; void f() { X a; throw 1; // first throw happens here // a.~X() // throws again } main() { try { f(); } catch(...) {} }
The first exception, thrown in f, is just an ordinary exception that
will propagate from f’s stack frame up to its nearest matching catch
block, in main. The second one, thrown in g() propagates from g’s
stack frame up to its nearest matching catch block, in ~X.
Because we don’t allow it to propagate out of ~X, the compiler only
has to propagate one exception at a time through any given stack frame.
The problem only occurs when we allow the second exception to propagate
out of ~X. Then, all of a sudden, there are lots of open questions:
is it possible to propagate two exceptions at once? If so, which one
will be caught by the next catch(int) block, and how do we access the
other one? If not, which one should we propagate, and what do we do
with the other one?
The interesting thing about all these questions is, no matter what the
answers, there’s no reason the program should do anything differently
between throw 2 and the next catch block that is entered. Whichever
exceptions are propagated, and however they are caught, all that’s
called for here is ordinary stack unwinding. But instead of answering
these questions—or giving users a way to answer them—the standard
“handles” the problem by calling terminate.
Toward an Answer
So there you have it. The reason we can’t have throwing destructors is that nobody worked out how to deal with multiple exceptions wanting to propagate through the same set of stack frames. Considering the fact that the program knows how to unwind from here, even if it doesn’t know exactly what to propagate, and the fact that it’s so easy to throw from a destructor by mistake, we think termination is a bit draconian.
Frankly, we don’t think it’s so hard to nail down the final details of how this should work. For example, it might be reasonable to simply drop the second exception on the floor and propagate the original one. Before you freak out, consider this: the second exception doesn’t change the unwinding process in any way, at least, not until the exception is caught, and the original failure is still the root cause of the current unwind. The program or the user can likely deal just as well with that root cause without knowing anything about the second exception.
This being C++, we expect someone to want more control over that second exception, so in our next installment, we’ll consider some alternatives. For now, we leave you with the suggestion that maybe destructors that throw are not truly Evil™, but just misunderstood.

RAII does not work if I forget to encapsulate the code with a try/catch block. This was almost not an issue for my real project. But with the introduction of std::async methods and std::thread object with lambdas are being advertised for coder friendly usage. Since, without try/catch, stack unwinding is undefined according to the standard and my VC and GCC compilers, guess what, don’t unwind; my lambdas become actual methods or instances of safety objects. async and thread calls become wrapped-around hot potatoes. And RAII related approaches becomes pretty much lies…
Interesting topic and discussion. It seems to me the use of destructors falls into two broad categories: release of the resources and sundry side effects, which make use of automatic destructor calls when going out of scope. I think there is a consensus that the former shouldn’t fail, while the latter can fail. Making destructors noexcept and separating the part that can fail into a separate function seems like a logical decision. The drawback of this design, as it was mentioned, is that it requires explicit call to those finalize functions, and there is no automatic call to the finalize functions of bases and members. I think making simple backward compatible changes in the standard can overcome this issue if the finalize functions were automatically called for all automatic objects in the same order but before the destructors. When the stack is unwound due to exception propagation, only destructors are getting called just as it is today, so there is no possibility for double exception. Here is an example, using invented syntax ~~ for finalize:
class File { Handle handle; public: ~File() { release(handle); } // always noexcept ~~File() { flush(handle); } // finalize can throw };
void fun() { File file1(“abc”); File file2(“def”); // compiler generated code on leaving scope // if exception in progress goto 3: // 1: file1.~~File() // if exception in progress goto 3: // 2: file2.~~File() // all destructors will run as they are not allowed to throw // 3: file1.~File(); // 4: file2.~File(); }
This kind of approach has the drawback that the finalizers are not guaranteed to be run. This drastically limits their utility.
But this is no different from the existing exception model: code after the exception thrown is not being executed, instead stack is being unwound. There is already a program fault, you shouldn’t execute code that can fail in unpredictable manner after that — unwind the stack to release the resources. The same happening here: finilizers are no different than the code following the exception, they can also fail, so they shouldn’t be executed in the presence of exception, instead the stack should be unwound, the destructors are being called, and those are not allowed to throw.
“The drawback of this design, as it was mentioned, is that it requires explicit call to those finalize functions, and there is no automatic call to the finalize functions of bases and members”
Below in comments, I showed how to do calls to deferred part of two-stage-destructor automatically(based on stack_unwinding library):
https://github.com/panaseleus/stack_unwinding?#two-stage-destructor , https://github.com/panaseleus/stack_unwinding/blob/master/examples/two_stage_destructor.cpp
That’s not the same mechanism. As far as I can see, you have destructors and finilizers executing together, but suppress finilizer exceptions. I proposed that all finilizers should run together and then all destructors should be run together when exiting scope. When the finilizer are run all the objects are being alive, when the destructors are run, their life being terminated.
I considered approach “all finalizers first”, and I have found several drawback:
{Some a; Some b;} and {Some a; {Some b;}} would have different order of actions, which is counter-intuitive.
Lifetime of objects will be longer than really needed, which would lead to superfluous resources usage. This is against the spirit of efficiency of C++.
And I don’t see any convincing advantages of “all finalizers first” approach.
The only behavior which makes sense in throwing exceptions while handling an exception is std::terminate();
Perhaps we should get away for a moment from the philosophical question of what destructors mean, and just get down to the very basic issue: an exception handler says “on this condition continue execution here” and a destructor says “but first do this”. When destructors throw, we may then have conflicting handlers – one says “continue at point a” and the other says “continue at point b”. So the fundamental problem is how to reconcile a program that wants to continue at two different places.
How about this (with the caveat that I haven’t though things through)? When an exception is thrown it becomes part of a set of pending exceptions. The compiler arranges for the stack to start unwinding, and any exceptions thrown from destructors join the pending set. When control unwinds through a try block that has catch clauses for any pending exception, one of them is executed. On any attempt to leave the catch clause (goto, return, throw, etc.), control resumes to the unwinding mechanism, executing more catch clauses from this try block (including possibly the same ones many times) if those clauses would have been executed if there had been only a single exception. Once that’s done, if there are more pending exceptions (including freshly thrown ones) then unwinding continues, otherwise control resumes following the final catch clause executed.
“… When an exception is thrown it becomes part of a set of pending exceptions. …”
Certainly an interesting idea, but it seems to me it would be quite a complex change to the current situation. And the idea of executing the same catch-handler block multiple times seems to me like something that might (silently) break quite some code.
Anyways … since you put some thought into this: How would it compare to the approach of suppressing/nesting “secondary” exceptions (thrown from destructors)? (see my comment below)
I don’t like the idea of suppressing destructors. I would prefer that we require that a handler executes for every thrown exception if there is one (subject to the usual caveats on running out of resources). I don’t know that breaking existing code is much of a worry, since this change just affects code that previously would have called std::terminate() and there isn’t much of that because people have been trained not to let exceptions out of destructors.
My approach has the advantage of being deterministic (if we require that handlers be executed in top-down order if more than one is eligible) and generally requires only the same sort of care that normal exception safety does (i.e., handlers need to be written knowing that there may be an invisible throw at the end). For handlers that care about being re-executed or being part of a further unwind, we have std::uncaught_exception() and other such functions to check.
By the way, I’m the “mentor” who gave Dilip those examples. With my suggestion, f() would print “Caught an a”. Caught a b.” and g() would print “Caught a b. Caught an a.” which seems like an unobjectionable and even desirable result.
I recently learned how the Java try-with-resources construct works: For multiple “concurrent” exceptions the root-cause it re-thrown and the exceptions “from the destructor(s)” are attached to it as suppressed exceptions. (see http://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html#addSuppressed%28java.lang.Throwable%29)
I also recently learned (from Jon’s great C++Now talk) about std::exception_ptr for moving exceptions around and about std::nested_exception, std::throw_with_nested, …
Between these features, it seems that C++11 already has everything in place to implement the Java7-try-with-resources behaviour, that is, simply “suppress”/”nest” the exceptions from the d’tors and continue to throw the original one.
“I also recently learned (from Jon’s great C++Now talk) about std::exception_ptr for moving exceptions around and about std::nested_exception, std::throw_with_nested, …”
I also saw Jon’s talk, and also think that nesting exceptions could be solution.
Which features of C++11 you think will help to implement “suppression” approach?
Well, take std::exception_ptr — it’s supposed to be used with std::current_exception() to move exceptions around (and then be rethrown with std::rethrow_exception).
I can imagine an additional function — let’s call it std::suppressed_exceptions() — that returns a vector of exception_pointers containing all suppressed secondary exceptions.
Just a quick thought.
I also considered std::current_exception, but it can’t be used for that purpose – it should be used in catch. Just search my comment below – keyword “std::current_exception”.
I thought about throwing deferred actions problem, and came up with stack_unwinding library. It provides primitives for building Uwinding Aware Destructor https://github.com/panaseleus/stack_unwinding#unwinding-aware-destructor . And based on top of this, there is implementation of Two Stage Destructor, which consists of Release(non-throw) and Deferred(can throw) parts – https://github.com/panaseleus/stack_unwinding#two-stage-destructor
‘ “letting go of a resource” must never fail ‘.
Resource releasing should be called in any case. Resources should not be leaked.
Deferred actions on objects are desirable. For instance “fflush”. Such actions may fail.
When deferred action is executed and failed, user should know about that. Swallowing of info (exceptions) is not good solution.
One approach to fit these requirenments into code without multi-exceptions is to call deferred actions only on during “normal” destruction, not stack unwinding. This leads to two-stage destruction:
Sample code (I tried to paste code here, but for some reason highlighting does not work for me): https://github.com/panaseleus/stack_unwinding?#two-stage-destructor . Example of usage: https://github.com/panaseleus/stack_unwinding/blob/master/examples/two_stage_destructor.cpp .
I have impression that such separation between resources releasing and deffered actions does have sense. Not execution of deffered actions during stack unwinding makes them similar to ordinary actions below throw statement – because ordinary actions are not executed during stack unwinding also.
I think this is essentially what I have in mind in my suggestion below.
a) Some destructors have multiple functions:
i) operations which can never throw – releasing resources, mutex, etc. ii) operations which might throw – flushing, etc – “finalize”
b) We can catch any exceptions in the destructor and handle any possible recovery or abort. There can be no general solution to this since the possibilities for recovery are application dependent.
c) Since this prevents exceptions from “escaping”, it the destructor can be “no except” – as it is anyway since an attempt to throw() would result a call to std::terminate().
Robert Ramey
Great Thread!
My question is pretty simple – maybe it’s been asked before: Suppose I want to make guarenteed correct code TODAY. What should I do?
Starting with this:
struct base { ~base() { std::cout << "~base" << std::endl; } };I make the following change:
struct base { ~base() { try{ std::cout << "~base" << std::endl; } catch(…){ ?; // what do I do here? } } };I could change this to:
struct base { private: bool m_is_closed; public: base() : m_is_closed(true) {} bool close() { try{ std::cout << "base closed" << std::endl; m_is_closed = true; } catch(...){ return false; } } ~base() { assert(m_is_closed) } };But this defeats the whole appeal of using a destructor in the first place. Now I have to sprinkle close() where ever I use a base. But only on the objects of type base. Very confusing and error prone.
Third try
struct error_handler { void operator()(exception_ptr ep){ // fill in to taste. }; struct base { private: error_handler m_eh; base(error_handler eh = std::terminate) : m_eh(eh) {} ~base() noexcept { try{ std::cout << "base closed" << std::endl; } catch(...){ exception_ptr ep = // I forget what goes here m_eh(ep); } } };which looks pretty good to me. Any comments appreciated. Remember, I'm not asking about what the next version of C++ should do or yada, yada … . I just want to write programs today that I can expect to be correct and predictable.
Robert Ramey
whoops – replace
with
Robert Ramey
PS. I also absolutely loved the screen play !!!
fact1: throwing destructors already exist and some must be used and supported.
fact2: when confronted with more complexity than expected committee went el cheapo with [except.ctor]/3 without offering a more refined alternative.
fact3: a real, practical solution is always much better than coding standards (and if it’s any good it usually becomes part of a coding standard).
my point is of course that this can was kicked enough down the road and we need to add refined standard functionality to allow enough unwinding context info in order to avoid terminating the program for an expected, recoverable exception.
ps: Dave, I really enjoyed your screenplay; do we get a second act maybe?
Thanks, gil. We never would have had that screenplay if Jon hadn’t written the dialogue into the first drafts of the posting. We’ll have to see if he’s inspired to do it again.
It would be nice to have something more informative than “bool uncaught_excetpion()”. Maybe something like “state uncaught_excetpion_state()” or “int uncaught_exception_count()”.
It would help us to find out when it is really dangerous to throw exception from destructor (by saving uncaught_excetpion_state() in constructor, and comparing with state in destructor).
As the result, we may achieve exactly same effect as manually placing “.close()” at end of scope automaticly.
{ File a,b; // ... b.close(); // may throw a.close(); // may throw }would became{ File a,b; // ... }More generally it would allows us to develop “advanced” Scope Guard, which respects exceptions from destructors, and does not require calling of release/commit by hands. D langauge has scope(success) and scope(failure) which are simmilar in something to that “advanced” Scope Guard semantic.
MSVC-specific uncaught_exception_count proof-of-concept, with example of “advanced” Scope Guard: code – http://ideone.com/IWiJu, output – http://ideone.com/oLZ4k
http://ideone.com/zA4Po Added GCC-specific uncaught_exception_count. Now output is generated by Ideone.com, at the bottom of page.
I think I’m missing why having a count would be useful. The real question is whether there is an uncaught exception that might escape, right?
Well, count is just one of the ways to do job. My initial message starts with “It would be nice to have something more informative..”.
It can be for example something like “bool is_called_due_to_stack_unwinding()”. For my demo, I have chosen uncaught_exception_count – because it is pretty easy to implement, because MSVC and GCC already maintain such count internally.
In fact, they both implement uncaught_excetpion, based on uncaught_exception_count(you can check this by step-into assembler):
return get_some_tls_struct().uncaught_exception_count!=0;
BTW, C++11 has std::current_exception, maybe it will work too, need to check.
Standard says “current_exception Returns: An exception_ptr object that refers to the currently handled exception”, which means it should work only within handler and may not work in plain destructor. I have checked MSVC2012 – it returns default constructed exception_ptr in destructor during unwinding.
So it can’t be used for “unwinding check”
Wrapped to library form: https://github.com/panaseleus/stack_unwinding?#readme “The stack_unwinding is a small header only C++ library which supplies primitive(class unwinding_indicator) to determining when object destructor is called due to stack-unwinding or due to normal scope leaving.”
stress test: http://ideone.com/us2RY
500 simultaneous uncaught exceptions. (possible more, but ideone’s gcc has instantiation deepness limit)
Hi Dave
Apropos this:
“This being C++, we expect someone to want more control over that second exception, so in our next installment, we’ll consider some alternatives.”
When you consider those alternatives, could you please consider a more complicated case? The following example was sent to me by my mentor. Its not exactly real world but convoluted enough to make your effort to avoid std::terminate a little bit more interesting
struct a { }; struct b { }; struct c { ~c() { throw b(); } int f() { try { try { c c_obj; throw a(); } catch (const a &) { print("Caught an a"); } print("f"); } catch (const b &) { print("Caught a b"); } } int g() { try { try { c c_obj; throw a(); } catch (const b &) { print("Caught a b"); } print("g"); } catch (const a &) { print("Caught an a"); } }If I am not mistaken (or missing the point), this is a class for error handling introduced by Alexandrescu that precisely is designed to throw from the destructor: http://accu.org/index.php/conferences/accu_conference_2007/accu2007_sessions#Choose%20your%20Poison:%20Exceptions%20or%20Error%20Codes?
See this blog post for a lot of related material:
http://akrzemi1.wordpress.com/2011/09/21/destructors-that-throw/
Hallelujah! Finally someone else thought that throwing from destructors might actually be useful. I wrote an article, called Throwing Destructors, on this subject back in, let’s see, 2004! While it gets a bit theoretical when talking about fault-tolerant systems, I think it is quite useful for understanding what it means for C++ to terminate the program when more than one exception is active. Maybe you can get some ideas from it for your next post.
Hi Boris,
From looking over your article, it isn’t clear to me what you think throwing destructors might be useful for. From my point-of-view their usefulness is probably quite limited. On the other hand, it also seems to me that the C++ policy of invoking termination if an exception is thrown during stack unwinding is unduly harsh, and makes for a great deal more mental drag when programming than it’s worth.
Hi Dave,
Throwing from a destructor can be useful for the same reason as throwing from any other function — to report a failure, most commonly a resource release failure. The example I use in the article is a mutex with a RAII- based auto lock. In POSIX threads, for example, pthread_mutex_unlock() can fail with an error. And it doesn’t have to be something pathological, like mutex was not initialized properly. A good example is EPERM, which indicates that a thread is attempting to unlock a mutex that it did not lock.
While at it, let me say that I also disagree with Herb’s reasoning that since we cannot retry the destructor then it doesn’t make sense to throw from it. Re-trying the original function is not the only thing that an application may want to attempt. For example, it may want to fail (i.e., unable to recover from this error) but before that it may also want to log some information about what happened. In the mutex example, an application may want to log the mutex id and the thread id that is trying to unlock the mutex that it does not own. Or it may want to abort() the process so that the core can be examined.
Generally, it seems what a throwing destructor should do is externalize the resource that it is unable to release. Besides other things, an application may then try to release it in some other way without retrying the original destructor.
Now on throwing an exception during stack unwinding. What this really means is that we hit a second failure before managing to recover from the first. In this situation our options are: (1) to give up (which is what C++ currently does), (2) try to recover from the second failure, then proceed to recovering from the first, and, finally, as you suggested, (3) to ignore the second (or the first) failure.
I don’t think (3) is a good option on the fundamental level. Let’s say we ignored the second failure and managed to recover from the first. Now the application will continue executing with an ignored failure from which it didn’t formally recover. It doesn’t feel to me like a good way to write deterministic software.
Let’s examine option (2) now. The question here is, if we recover from one nested failure, do we also recover from two nested failures? What about 3? In the end I think it should be either no nested failures or any number of nested failures. Designing an application to deal with any number of nested failures can be rather hard. Think how would you implement vector::push_back() with such a requirement.
Which leaves us with option (1), that basically keeps things simple and says that we do not handle nested failures. I think when you put it this way, it removes a lot of mental drag that you mentioned. And implementing vector::push_back() in this case might just be manageable.
@Boris:
Unlocking a mutex you don’t own is a great example that someone else on the thread also raised. It’s a precondition failure — the calling code is incorrect. Precondition failures are always a bug in the calling code, they are usually best dealt with using assertions that fire during testing, not error codes or exceptions that attempt to recover from an already-incorrect program.
All of which can be done without exceptions, right?
But now you get to the heart of my problem with the suggestions for “why you might want a throwing destructor” made so far:
“Externalize” == “break encapsulation” and leads to spaghetti responsibilities where arbitrary callers are asked to share responsibility for the internal implementation details of an object. In my experience at least, even in cases where it has seemed initially reasonable, it ends up being just wrong and leading to code that is unclear, tightly coupled, and unmaintainable in practice.
I take is as axiomatic to good class design that the caller should not be asked to share responsibility for the internal implementation details of a class.
This is exactly why I disagree with all examples I’ve seen so far justifying throwing destructors (except possibly Andrei’s which uses throwing dtors as just a mechanism to get an effect but probably no longer needs to — that’s a separate article).
Herb
P.S.: The one thing I haven’t seen anyone mention, but that is very important to any discussion of throwing destructors, is what I mentioned in my original article about this (I think): If you really want to be notified of some failure or just informational condition that arises while shutting down an object, the right pattern is to replace a throwing dtor with a separate .close() function that can throw or report anything it wants and that the user can call explicitly if they want to know about the error/informational data, and have the dtor call .close() if it hasn’t been called already and swallow exceptions.
Hi Herb,
This approach doesn’t play well with RAII. The whole point of RAII is so that I don’t need to call destructors/release functions manually since, as we all know, that would quickly become unmanageable in code that uses exceptions. But you are basically suggesting that now I need to go back to calling close() (or unlock(), etc) manually if I want to detect failures.
I understand the concern, but note that destructor don’t go away — we still have destructors, and so it does play well with RAII.
It simply separates out a separate function that can be optionally called by a caller who cares about anything the .close() may want to report, that’s all.
You can’t detect failures from within a destructor without such side effects anyway; the signature doesn’t allow for it. For example, please consider: if it makes sense to throw from a destructor, it must also make sense to return an error code. I’m not a priori against considering whether to allow multiple exceptions and not call terminate() in the way Jon and Dave outline, but one thing I’ll be looking for in such a proposal is whether it also allows error codes from destructors (and for that matter constructors and operators) — i.e., non-void-returning destructors, and what exactly that would mean for callers.
I do think it’s important to treat “this function should be able to report an error in a normal way” consistently regardless of which of the two normal mechanisms is used — error codes or exceptions. Or at least show why they have to be treated differently. I’m still a little skeptical, but I’m willing to consider this alternate world as long as it’s consistent.
Let me give you a concrete example based on mutex/lock. I am wring a function where I lock some mutex and then call a bunch of other functions. Then the mutex is automatically unlocked:
mutext m; void f () { for (;;) { lock l (m); if (f1 ()) f2 (); else break; } }How can I detect unlock failures in this function? Note that there are three possible “unlock paths”: normal loop termination, break, and exception thrown by f1() or f2().
I suppose one answer would be to insert a scopeguard after “lock l(m);” that calls [&]{try{l.close();}catch(…){…}} — but this can’t throw again, which I think might be a problem.
That is certainly the right approach today. The question is whether that “rightness” is just a side-effect of an overly strict runtime response that could be fixed in the language, or not.
This design has at least two notable drawbacks:
Any mechanism for attaching a side-effect to block exit raises the same problems, so I’m very curious as to what you might have in mind here.
Then that would apply to finally blocks as well. So here’s an excellent related design question: If we had finally blocks, should they be allowed to throw?
I’m not trying to change the question, but I think this directly related to your (nicely general) question.
My point exactly.
Answering your question with a question: do you have a plan for helping the user avoid throwing
finallyblocks?Still very curious to hear what you had in mind when you wrote “probably no longer needs to.”
All good points, Herb. And I agree if we can avoid throwing from destructors, we should. If nothing else then to keep it simple.
But the reality is often messy. You are saying that pre-condition failures should be checked with assertions. What are the exceptions for then? If all the inputs are valid, then the only failures that can occur are implementation-specific. The main point of exceptions, compared to error codes, is to allow breaking encapsulation in such a way that layers that are not willing to know the details don’t have to.
Throwing from the destructor is the more flexible option. If nobody catches the exception, you get the equivalent of an assertion. For a user-facing application, dumping core might not be the best idea (yes, it should have been thoroughly tested and bug free by now, but we all know how it is in reality). So instead it might catch the exception, log the information, and then terminate gracefully. Yet for another application terminating might not be an option. So it will catch the exception, log the information, terminate the thread, and continue operating.
As you can see, an exception can do all that your suggested approaches can, and more. Modules that don’t care won’t observe any “de-encapsulation” (the exception will just pass through). But modules that need to care at least have a fighting chance.
Precondition failures indicate coding errors, which are non-recoverable conditions. Exceptions are for recoverable conditions. No point in unwinding if you won’t be able to recover. In general, you should only throw in response to a precondition failure if you can’t convince your customer that further arbitrarily erroneous (aka undefined) behavior is worse than termination.
I don’t know what you mean by implementation-specific. Is resource exhaustion implementation-specific? Doesn’t every implementation have finite resources?
That is a very interesting way of describing the situation (ten points!)
Exactly. This is all really in reply to Herb’s statement that a throwing destructor that externalizes the resource it failed to release is breaking encapsulation. And my point being that if you handle pre-condition failures with assertions then any exception thrown is by definition breaking encapsulation since it is reporting on the failure of some implementation detail.
Take std::string as an example. The fact that it uses operator new to allocate dynamic memory and that this operator may throw bad_alloc are all implementation details. And it is no different, conceptually, than a destructor throwing an exception that carries the resource this destructor could not release.
When asked how an application can recover from bad_alloc, we say that it can somehow (i.e., in an implementation-specific way) make more memory available and retry the failed operation. Similarly we can then say that an application can recover from a destructor failure by somehow (again, in an implementation-specific way) releasing the resource and continuing normal operation.
Thanks for explaining your interesting perspective.
I think/hope you meant postcondition when you wrote “pre-condition?”
No, I meant “pre-condition”. Think about it this way: in an ideal world, a module given correct inputs (i.e., pre-conditions are satisfied) will always succeed. That is, there is no such thing as post-condition failures for such a module. If inputs are correct but such a module still “failed” then it is not really a failure, at least not in the sense that is “unexpected”.
In the real world, however, modules can fail for all kinds of unexpected reasons (not enough resources, bugs, etc) — all of which are implementation details.
Take a class ‘file’. Its constructor’s pre-condition could be that the path is valid (say, shorter than MAX_PATH). I guess the failure to open the file because one does not exist is a post-condition failure but it is not really unexpected. Though in most cases it is probably convenient to report it as an exception, just like for the pre-condition failure (assert’ing on an invalid path feels too drastic).
Can you think of an example of an unexpected post-condition failure?
Precondition failures are non-recoverable. They indicate a potentially arbitrary level of brokenness that the caller was obviously neither aware of nor prepared to recover from. Throwing an exception under these circumstances does nobody any favors.
I agree with Herb on this: it’s a programming error; a non-recoverable condition that probably shouldn’t be handled by exceptions. On the other hand, inverting the order by attaching a release/acquire pair to a block scope is not an unreasonable use case, and in general, we know that resource acquisition can fail.
Well, for the purpose of dumping core I advise against the use of exceptions. Just call
abort()directly and immediately.In principle, I agree. To make sense, the secondary exception would have to flag a problem that isn’t fatal to correct recovery from the first condition… which is what might make swallowing the secondary exception reasonable. Generally, if you find yourself wanting to do something non-critical at block exit (or in a destructor) you should probably write a try/catch block and swallow the exception directly. But until the day comes when the compiler is able to help you remember to do that, maybe it’s a bit much to kill the whole program.
I actually don’t see a problem for
push_back.Isn’t “we do not handle nested failures” already the status quo, and known to be manageable?
The point I was trying to make is that different applications may want different things. I you are writing a reusable mutex/lock class, you cannot just assume that all applications will want to abort. Exceptions give you flexibility in this regard.
The problem is that things can keep failing and you cannot say “enough is enough”. You have to keep trying to restore the vector to some consistent state. Let’s say push_back() required reallocation and you start calling destructors for old elements. The destructor for the first element fails. You try to finish destroying the rest of the elements. But the second element’s destructor also fails. And so on. While if we don’t support nested failures, then the second destructor failure is the end, we call std::terminate(). I guess you could use some form of recursion though.
Yes, it is, as long as destructors don’t throw. My point is that no nested failures with throwing destructors can also be manageable.
I still don’t see a problem here. Yes, you might end up with an arbitrarily deep wad of exceptions, if you were to stack them up. But then, I’m the guy who suggested discarding everything but the first one as a plausible approach
.
It would indeed be weird to have
push_backthrow an exception after succeeding in creating the new vector state. But then, that would just mean thatpush_backoffers only the basic guarantee in the presence of throwing destructors. It would in fact be indistinguishable from a failure.Not sure what you have in mind here.
I’m not sure how “no nested failures with throwing destructors” differs from the status quo, since the language doesn’t prohibit destructors from throwing.
I think you are right about push_back() implementations for the two cases. In fact, I came up with the same implementation that (I think) will work for both models. That is, in current C++, std::terminate() will be called on nested failure while if nested exceptions are allowed, then it will keep grinding (recursively).
template <typename T> struct vector { struct cleaner { cleaner (T* b, T*& i, T* e): b_ (b), i_ (i), e_ (e) {} ~cleaner () { if (i_ != e_) { cleaner c (b_, i_, e_); for (; i_ != e_; ++i_) delete i_; delete[] b_; } } T* b_; T*& i_; T* e_; }; ~vector () { cleaner c (data_, data_, data_ + size_); } void push_back (T const& v) { if (capacity_ > size_) { // Easy case (no destructors called). ... return; } // Get a bigger vector. // { vector tmp; tmp.reserve (capacity_ + 1); tmp.assign (data_, data_ + size_); swap (tmp); // At this point tmp.~vector() is called that destroys old // elements. This can throw which is ok. The vector is at // least conceptually unchanged (though the iterators are // invalidated). } push_back (v); } private: T* data_; size_type size_; size_type capacity_; ... };Correction, this for-loop:
Should actually be something like this (we don’t want to keep trying to delete the same element):
I once made a class whose only purpose was to have a throwing destructor
For some back story, I was working with a library that didn’t use exceptions. It signaled errors through an in/out parameter. My code was using this library extensively, so I wanted a terse way to translate from in/out errors to exceptions. I ended up with something like this:
//api in question void func_that_can_fail(error_t &err); class error_thrower_impl { operator error_t &() {return err_;} ~error_thrower_impl() { if(FAILED(err_) && !std::uncaught_exception()) throw my_exception(err_); } private: error_t err_; error_thrower_impl() {} friend error_thrower_impl error_thrower(); error_thrower_impl(const error_thrower_impl &); error_thrower_impl& operator=(const error_thrower_impl &); }; error_thrower_impl error_thrower() { return error_thrower_impl(); } //usage func_that_can_fail(error_thrower());I’m sure I just botched a few of the details like the friend syntax, or the implementation of error_thrower, but hopefully this gets the point across.
There are some significant downsides of this that I know of. First, you have to get over the shock of throwing destructors. Second, if any function takes two in/out parameters, then you can’t really use two error_thrower() calls and get deterministic results.
The alternative to this approach is something like this:
I consider that more verbose than I would like.
Note that this isn’t a problem if the API returns an error code instead of using an in/out parameter. In that situation, you can basically write an operator= that throws, and those are well understood.
Hi Ben,
Wouldn’t it be rather unusual to have a function report errors through two separate in/out parameters?
Unusual for my component, but not unheard of within our source base. For example, suppose you had a unit testing framework. There may be a function that needs to report a test’s error as well as a catastrophic framework error. In that specific example, you would probably want an error_thrower for the framework error, and directly manipulate the test error, but there could be other uses as well.
Functions that report errors via two separate parameters – isn’t that what this whole discussion is about
ie having 2 exceptions in flight is like a function that reports 2 separate errors…
A different alternative in C++11 world may be the following. Again, probably botching the syntax on this, and I’m not even sure if variadic templates will work when I name the last argument.
template <typename Func, typename ...Args> auto thrower(Func f, Args&&... params, error_t &err) -> decltype(f(std::forward<Args>(params)..., err)) { auto retval = f(std::forward<Args>(params)..., err); if(FAILED(err)) throw my_exception(err); return retval; } int func_that_can_fail2(int val, error_t &err); auto result = thrower(func_that_can_fail2, 0, err);If this thing actually works, then it would get rid of the only use I’ve had of throwing dtors, at least once compilers catch up enough to make this possible.
They won’t, and that’s an extremely painful limitation. Although in your case there is no reason not to pass that argument first instead.
There’s no technical reason not to pass the argument first, but there is a psychological reason. If the argument was last in the original function (almost always the case for the APIs I’m dealing with), then it’s a bit strange to pass the argument first in the forwarding code.
Though with this particular example, I’d do even better to omit the “error” argument.
template <typename Func, typename ...Args> auto thrower(Func f, Args&&... params) -> decltype(f(std::forward<Args>(params)..., error_t())) { error_t err; auto retval = f(std::forward<Args>(params)..., err); if(FAILED(err)) throw my_exception(err); return retval; } int func_that_can_fail2(int val, error_t &err); auto result = thrower(func_that_can_fail2, 0);Of course, now, I run into the question of how to get the decltype when I don’t have an instance of the last parameter, which happens to expect a pass by ref. I’m pretty sure my example code won’t work, since it looks like it would be trying to bind an lvalue ref to a temporary (though only at compile time).
Hmm… I always thought that Java’s java.lang.Throwable class (http://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html) was designed to handle problem of subsequent exceptions. The second part of your article seems to be the most interesting.
(originally sent by private email, but Dave and Jon asked me to post here)
Great article, and the deal with multiple active exceptions is part of it.
However, the most fundamental reason destructors can’t throw is something else: The operation cannot fail (as you point out!) plus you cannot retry the operation. That you can’t use a throwing-dtor type with the standard library is just one consequence of this. In general, “letting go of a resource” must never fail, and can in general never fail even with other kinds of resources such as disk file handles.
To be crisp about what I mean by “fail” I’ll use this definition, along the same lines you mentioned in the article: In C++ Coding Standards, we define “errors” as situations where a function cannot do what it advertises — it cannot meet its postconditions. Errors can be reported using a variety of mechanisms, but the two big ones are error codes (the only option in C) and exceptions (the default and preferred option in C++).
Now consider destructors: As you correctly point out, they cannot fail to achieve their goal of destroying the object (conceptually, turning the memory from a bit pattern that satisfies the type’s invariants back into just raw memory with unknown contents). But this is super fundamental — the postcondition of a destructor is that the object is destroyed, and that is always achieved no matter what happens in the destructor’s body. For one thing, the object’s lifetime formally ends as soon as the destructor begins — regardless of what happens in the body. For another, you can’t retry it — once a destructor has begun you can’t call anything else, including the destructor. In fact, you usually can’t even spell an attempt to retry a destructor — in the examples you give of stack-allocated objects, and most other lifetimes that don’t involve pointers, there’s not even a way to talk about the object’s name after a destructor has begun executing and so no way to even spell an attempt to retry the operation.
So I’ll make the case that, since a destructor cannot fail (or be retried), it makes no sense at all for it to report an error – in any way, including as an exception. (It could report something informational perhaps, but not an error and therefore definitely not an exception since exceptions should only be used for “could not achieve postconditions” errors.) Finally, functions that cannot fail should be noexcept.
Personally, I would like to see the core language require that all destructors be implicitly noexcept, and that it be illegal to override that default.
This was discussed during C++0x, with the justification that that change would only “break” code that deserved to be broken because such code was really already broken. (I continue to believe this is true.) The main reason the proposal failed to progress was the general concern that maybe some existing code was using this ability in some reasonable and safe way, and in particular that people raised a very small number (literally a handful, low single digit number) of questionable examples from existing libraries, most or all of which failed to pan out as accurate upon investigation but by then it was too late. This is my characterization, of course; others may have different recollections.
Herb
P.S.: I don’t see references to other work, but if you do add any then since I’m the guy who wrote “destructors that throw (and why they’re evil)” you may want to link to that. Also, in your next article if you want to talk about multiple exceptions it would be great to compare the experience in other languages like C# — it’s a tradeoff for a different set of problems, I think.
P.P.S.: Not sure if you meant to address this valid issue, but:
Not sure if you mentioned it anywhere, but vector is required to use an array, so the issue of arrays has to be addressed somehow.
Hi Herb,
Thanks for posting. I don’t have time to respond to your whole article(!) now, but let’s start with this part: if all constructors needed to do was “conceptually, turn the memory from a bit pattern that satisfies the type’s invariants back into just raw memory with unknown contents” then things would be so much simpler. Among other things, it would become much more practical to use garbage collection in C++. Problem is, we routinely attach all kinds of non-memory side-effects to destructors: releasing locks, flushing caches, closing files, etc., and these effects can indeed fail to occur, thereby “not doing everything the destructor advertises,” to use your words.
I have serious problems with your suggestion that re-try-ability is fundamental to what can or should report an error, but leaving that argument aside for the time being, consider this: the exception can always carry any information needed to re-try these side-effects.
Does that mean that the exception would conceptually own any resources whose side-effects fail? Or perhaps it would be considered a reference to a zombie?
Hi Daniel,
I suppose so.
Well, not by me. I don’t even understand what that means
It may not look like it, but the non-memory side effects of destructors must always succeed as well.
Lets take the example of fclose in a destructor.
The purpose of fclose is to close a file handle. The purpose of fclose is not to make sure that the data got to disk successfully. When you call fclose, the file handle will always be closed regardless of what else fclose may happen to initiate. The only case where this is not true is when what you passed was not a valid file handle in which case that is a precondition failure, not the inability to satisfy postconditions.
The reason why people get confused about this is because fclose might tell you that data failed to be flushed to the file. Trying to use this information for anything useful is not a good idea. Why? Because even after fclose has returned, data can still be lost. There are disk caches, networking problems for remote files, and plenty of other potential problems.
Therefore if you have any desire to actually know that data has reached the storage medium, then there a different function you can use for that. fsync.
Lets have a look at Releasing locks. I don’t know of a locking API in which you can fail to release a lock. Looking at Win32 LeaveCriticalSection for example, it doesn’t even have a return code. Looking at Win32 ReleaseMutex, the only condition in which ReleaseMutex fails is when you don’t actually own the mutex. That is a precondition failure.
You will find that any other Close / Release functions for non-memory side effects will have similar semantics to these. They never fail at the task of releasing the resources they are designed to release. After all, what would you do in response to that? Try to release them again? It would just fail again.
Many functions fall into the category of fclose, in which they may return advisory information about failure, but they are not the primary way in which failure should be detected.
Hi Jonathan,
Thanks for your post. I can think of two ways of interpreting what you wrote here:
#1 is easily disproven, so I’m pretty sure you mean #2 here. But if that’s the case, I don’t see how looking at one specific example that, in your view, already satisfies your rule, supports your argument.
In that case, let’s suppose my aim is to ensure that the file is closed and data gets to disk successfully. I’ll write a function whose postcondition is that data gets to disk. It writes a buffer to disk (which can fail because the disk is full), calls
fcloseandfsync, and I’ll call that from my destructor.Alpha particles from space can flip bits in RAM, calling everything into question. Does that make it a bad idea to use information about failed memory allocation? I can easily think of reasons to want to know that
fclosefailed, e.g. so as not to spend precious CPU cycles trying to do something with the resulting file.I’ll say the same thing to you that I said to Herb: the ability to usefully re-try an operation has no obvious bearing on whether it’s a good idea to report that the operation failed. In fact, I’ll go further: if the best response to a particular problem is likely to be “try that again,” an exception is probably not the most appropriate way to report the situation, because it’ll usually need to be re-tried very close to the place where it occurred.
Here’s another way of looking at it: destructors are the only way we have in C++ of attaching side-effects to block exit (c.f.
finallyin other languages), and, yes, to the destruction of other objects, neither one of which is inherently a bad thing to do. Side-effects can fail to complete, and when they do, there are reasons to want to know about it.The decision to “try that again” might be made arbitrarily far down the stack, though.
Might be, yes. I’m making a (totally unsupported) claim about what is common. The further away you are from the low-level cause of the failure, the more likely it is that these low-level details are covered by some abstraction that makes it hard to know what to do about it. I’ll try to think of a good example.
Throwing exceptions from finally is generally a bad idea in other languages, for analogous reasons. Not to say that there isn’t an elegant solution to this, but I don’t know of any obvious examples in other languages.
For example, C# (and, I believe, Java) resolve the throwing-whilst-unwinding issue by simply forgetting the original exception (the opposite of your suggestion at the end of the article).
Because of this, there are some libraries that you simply should not use with the
usingstatement – those that throw fromDispose()(think~T()). This is because it works in essentially the same way as C++:using (var x = getX) { // inner }Expands into something like:
var x = getX; try { // inner } finally { if (x != null) x.Dispose(); }If an exception is propagating out of the inner section and the
finallyclause throws an exception, the original exception is lost. It’s possible that continuing to propagate the original exception would be a better solution, but I don’t know if anyone has explored that space.One infamous case is the MS WCF client library, which can throw exceptions from
Dispose()when a connection isn’t closable. This necessitates various ugly workarounds, which ultimately ‘fix’ the problem by suppressing any errors raised by the WCF client’sDispose()method. (Microsoft has a page on MSDN dedicated to the issue as well.)Interestingly enough, the Java try-with-resources statement seems to work the other way around, namely if the release (the “implicit finally block”) throws any exception, this exception will be suppressed (see: http://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html#addSuppressed%28java.lang.Throwable%29) and attached to the original one and the original one will be thrown.
Actually, I should never said “non-memory” here. In this domain, the same reasoning applies no matter what kinds of side-effects we’re discussing.
Vectors don’t ‘new’ their array, they use a constructor. Elements are destroyed individually so it’s possible to deal with exceptions in ways that can’t be done with ‘delete[]‘.
A
noexceptdefault for destructors was only omitted from the proposals I co-authored with Rani Sharoni and Doug Gregor in order to give thenoexceptfeature the best chance of passing the committee and thereby solving the exception-safety problem it was designed to address. But that was before our original design was—over my strong objections—changed to terminate unconditionally when violated. The C++ environment should be strict at compile-time and forgiving at runtime. There’s no good reason to reward easily-made mistakes with termination. A destructor writes to a log file and the whole program dies in the field just because an exception was thrown? Outrageous and unacceptable, IMO.I continue to object to declarations that code “deserves to be broken” because it doesn’t conform to a particular philosophy of how code should be written. And even if I bought into your idea that an exception coming from a destructor is somehow inherently wrong, it is even more wrong of the language to kill “wrong” programs in the field without also giving users strong tools for avoiding wrong programs in the first place.
Am I missing something? The first link in the whole article is to your book.
Herb,
Thanks very much for posting. I think your comment is right on target and I appreciate it very much. But I don’t completely agree with it and I’d like to share why.
Dave has already taken up the point of re-try-ability and I don’t have much to add except to agree with something that he said elsewhere, that if local re-trys are a remedy to the problem, perhaps throwing isn’t the appropriate solution in the first place.
He also took up the point that destructors can in fact fail because they can be called upon to do more than just destroy the object. Imagine an object that represents a host in a cloud (such as Azure or AWS). The destructor of our object may be responsible for notifying the cloud service that the host is no longer needed (so we will no longer be charged for its use). This is inherently a network operation and can therefore fail for transient errors. It seems to me that our object must somehow notify some non-local code that an error has occurred which must be dealt with.
In C++, the way that code notifies non-local code that an error has occurred which must be dealt with is by throwing an exception. Absent standard support for destructors that throw, developers will have to invent some other, ad hoc, way of doing what exceptions are designed to do.
You said that “letting go of a resource” must never fail and I completely understand this. (While writing this post I told Dave about an OS API I worked with that had a memory-freeing call with an error code as a return type. I still don’t know what a caller was expected to do if this called returned an error.) But letting go of a resource is not the same as releasing it. As in my example above, it is possible that attempting to release a resource can fail. If the resource is a remote host or on a remote host, failure is always an option.
You mentioned that you have been unimpressed with the examples of destructors that throw that are found in existing code. I don’t find this surprising nor persuasive as an argument. Accepted C++ wisdom has been that having destructors throw is wrong (I’ve taught this myself and—absent a fix in the standard—still believe it). So I would be surprised if you did find a library that had destructors that throw and made it work well. This doesn’t preclude the possibility that a library or application couldn’t do this and do it well with proper support from the standard.
I look forward to a discussion about how the standard could support what I call “subsequent exceptions,” exceptions that are emitted by destructors as part of stack unwinding due to an exception already thrown.
Perhaps there is no such solution, but I’m optimistic that we can find a way to make C++ both robust in the face of exceptions and also capable of handling clean-up code that can’t be guaranteed to succeed.
It looks to me that there exist two models or ways of looking at exception handling. These two models have different expectations of when exceptions should be thrown and what support from run-time is needed (or missing).
One model is that any exception indicates a serious and very rare problem that is very likely to cause the program to stop, or get re-set to some initial state: a sort of disaster. In this “disaster recovery” mode we are often not focusing that much on getting our program back, but on minimizing damage and minimizing damage to other programs (that may share some resources with us. In this scenario we want to do as little as possible: release memory, release global resource locks, rollback transactions. The situation is already fragile and we do not want to initiate any operation that might require acquiring other resources (even creating objects of type std::string is risky). In this model, your Cloud example, where you want to notify that the server is no longer needed does not qualify for a valid operation in “disaster recovery” and should not be handled by a destructor. Also in this model, calling std::terminate is not so scary: it doesn’t mean “you throw twice and we will terminate your program” but rather “if the first level of disaster recovery fails, we proceed to the second level of disaster handling”. What does this second level do? releases global resource locks, and other resources, restarts the program… (for more discussion see here).
The second model is more relaxed: you use exceptions wherever you want to some action to be performed at the end of some scope in any execution path. Or where error codes are not possible (e.g. in signalling failure from constructor, destructor or an operator). Then, the way of handling a double-exception situation with std::terminate appears too harsh.
I am not sure if there exist a consensus in the community as to which of these models is to be preferred. And if you do not have the consensus it may be difficult to provide a solution in C++’s exception handling mechanism that could be considered “good” or “preferred”. (Unless a customizable solution is proposed, but I am not sure if too much customization is not… well…, Evil.)
Regards, &rzej
Andrzej,
It seems to boil down to whether you want to restrict exceptions exclusively to handle errors (failure of invariants or post-conditions), or also to generate side-effects (like a goto on steroids).
In Stroustrup’s book TC++PL there is an example that uses exceptions to let a recursive search function unwind the stack after it found a result. In Sutter & Alexandrescu’s Coding Standards, this example is identified as an anti-pattern because neither finding nor failing to find a result is not an error in the sense of failure of invariant/post-conditions.
@Herb: what real harm can come from throwing in recursive functions?
Rein
The reason that’s in C++CS is because Bjarne explicitly told us to cite that example and prominently label it as an antipattern, as he does.
Exceptions are not for control flow.
Besides std::terminate()? (At least in the current language, though Jon’s and Dave’s point in this article is to see if that could be changed in a future C++.)
Herb
Throwing in a recursive function leads to
terminate? I don’t see how that’s possible unless you call the function outside atry/catch(...)block. What am I missing?The queue/recursive search examples in section 14.5 of TC++PL have try/catch blocks surrounding the exception throwing code, so
std::terminateshould not be an issue.Actually the text in TC++PL is very nuanced: “[...] one might think of the exception handling mechanisms as simply another control structure.” and “Exception handling is a less structured mechanism than local control structures [...]“, with a proper warning “[...] such use of exceptions can easily be overused and lead to obscure code. Whenever reasonable, one should stick to the ‘‘exception handling is error handling’’ view.”
I greatly appreciate your C++CS Item 72 “Prefer to use exceptions to report errors.”, but since it says “prefer to” as opposed to “always” I interpret it as a strong guideline, not as a dogma never to use exceptions for non-errors (caveat lector and all that implied).
I advise against the use of exceptions for unrecoverable conditions: they almost always do more than is necessary, and they can very easily be mistaken by the programmer for a signal of recoverable failure (e.g. disk full), which may lead the program to continue when it should halt.
Don’t you mean “you use destructors wherever…?”
Right: destructors.
Did I suggest using exceptions for unrecoverable conditions? I guess I might have, but it was not my goal. I really meant recoverable situations (of course, you may not be able to tell if the situation is practically recoverable or not at the point of throw) for which you are willing to:
A practical problem I sometime face with what I perceive an overuse of exceptions is the following. I configure my debugger to pause the program in case an exception is thrown (this helps me detect various program “malfunctions” and check how it is coping with “unusual” circumstances). But when I debug the program, I find it stops time and again, because many people use exceptions for things like input validation.
It’s that second category that typically corresponds to unrecoverable conditions.
I wonder if we mean the same thing when we say “unrecoverable”. Using Matthew Wilson’s nomenclature, you have situations “unrecoverable by definition”: bugs that qualify for assertions; and situations “practically unrecoverable”, which are technically recoverable, but one cannot think of a useful recovery, e.g. No DB-connection in a heavily DB-based application. Which one do you mean by “unrecoverable”?
Note that by the “stricter approach to exceptions” I provided above, I did not mean situations “unrecoverable by definition”, and not necessarily “practically unrecoverable”. For an instance, reporting out-of-memory is a situation that fits into the model: recovery exists: unwind the stack and destructors of automatic objects will likely have released some memory. Or another good example is when a server processes next request, it must access some unique resource (specific only to the request), and fails; recovery exists: abandon this request and proceed to the next.
On the other hand the following situations do not fit into the “stricter approach to exceptions”. (1)
boost::lexical_castthrows when it cannot convert astringto anint: Such inability is nothing serious and could be handled by returning an optional value. (2). When program validates user’s input and finds that the input is invalid: it is expected and frequent situation that users will enter invalid input.I don’t/won’t use Matthew Wilson’s nomenclature; it’s more complex than necessary.
A precondition indicates the contract with the caller. If the caller violates the precondition it has violated the contract and is therefore broken code. Once broken code has executed, the program is in an unrecoverable state.
When
boost::lexical_castthrowsbad_lexical_cast, it’s not responding to a precondition violation; it is, however, avoiding a postcondition violation. Exceptions are (in general) for avoiding postcondition failure.Fine.
Agreed. Although this appears to be getting too much away from the point I intended to make.
boost::lexical_castis an interesting example here. I agree with the above, but the point I am interested here in is somewhat different. The authors ofboost::lexical_casthad a choice to make:- The function always returns a valid value of type
- The function returns type
In other words, the choice here is: how strong we want the postcondition to be. And by answering this question we are also answering another one: how often we want exceptions to be thrown in our program. If you choose answer (1) the problem of “double exception” (that you describe in the article) in the system is important and practical and in need of solving. However, if you choose answer (2) you do not practically experience the “double exception” problem, and the solution to callint. If it cannot it would be a postcondition failure and would qualify for a throw.boost::optional<int>and not being able to create a validintis just yet another valid return value.std::terminateis satisfactory (or at least tolerable).Not that I am advocating for option (2), but I see it as a coherent approach to using exceptions.
I don’t understand. If you always choose answer 2 (which isn’t really available for constructors or destructors) then there are no exceptions, so of course you don’t experience a “double exception” problem. If you sometimes choose answer 1, the double exception issue remains, it seems to me.
Looks more like an approach to not using exceptions, to me.
The approach I try to describe is “use option 1 sparingly.” The criterion for ‘sparingly’ is to great extent subjective. This is similar to judging what you consider a ‘serious problem’ in the application (like exhaustion of some resource). The example of
lexical_castis something that would qualify for a ‘non-serious’ situation (like not being able to find an element in a container). The more sparingly you use exceptions in your program, the less of a problem “double exception” situation becomes. And in this approach when you think of an exception as of a “serious” contingent situation in the program, and when trying to cope with it (via stack unwinding) you encounter another “serious situation”, resorting to the second level of exception handling (whatever you registered for withset_terminate) is not only unsurprising, but even natural.It is true that for some special functions (constructors, destructors, operators or conversion operators) exceptions serve other purpose: the only way of signalling a failure because there is no other. And this is a limitation of the approach. However not a fatal limitation. Note STD types
lock_guardandtry_lock: not being able to acquire the ownership of a mutex can be treated as resource acquisition failure, but neither of the types reports it by throwing an exception.I don’t believe in issues for programmers becoming “less of a problem” just because they are less frequent. In fact, in some ways, that makes them more of a problem, because they’re more easily overlooked and they have a disproportionate cost in terms of “mental drag.”
The problem with termination in the event of a double exception is that the second exception does not necessarily indicate a condition that can’t be handled. If the initial failure can be handled, it’s often the case that the second exception won’t be encountered. It’s too drastic.
Yes, approach 2 is sometimes more appropriate than approach 1. Usually it comes down to a judgement about whether the condition will likely need to be handled by the immediate caller.
That’s a good point.
I admit I have a problem with imagining this situation. Could you give me an example that would illustrate this?
Sorry if I am saying something obvious, but I remember reading a paper (I cannot remember whose) that discussed the possibility of registering (somewhere inside the runtime) exception “reduction” functions. Reduction functions are something like this
void Reduce1(std::exception & current, Library1::Exception & second) { throw second; } void Reduce2(std::exception & current, std::exception & second) { std::throw_with_nested(second); } void Reduce3(std::exception & current, ...) { std::terminate(); } std::register_reduction(&Reduce1); std::register_reduction(&Reduce2); std::register_reduction(&Reduce3);Function Reduce1() reads “if you are currently handling exception of type std::exception, and a second exception of type Library1::Exception is thrown, ignore the current exception and continue stack unwinding with the new one”. Function Reduce2() reads “if you are currently handling exception of type std::exception, and a second one of this type is thrown combine them into one of type std::nested_exception”. Function Reduce3() falls back to C++03 default: on second exception call std::terminate.
You register reduction functions similarly as you register terminate handler.
Picking the right reduce function in-runtime is not easy and probably subject to rules similar as picking the right function overload and/or picking the right exception handler.
If you register no function, or we cannot find one unambiguous match, we resort to the default reduce function which calls std::terminate: this preserves backward compatibility.
There is the academic argument: Assume that an object can throw an exception in the destructor. Given that there isn’t way to refer to such an object, it must be destructed and hence destruction succeeded. If an exception signals a failed operation, this is a contradiction, and proof that destructors can’t fail. If we wanted to throw in a destructor the result should be a state rewind (instead of a stack unwind) back to before the destruction of the object began.
There is also the very practical matter that providing some strong guarantees makes reasoning about code far simpler and allows one to make additional guarantees.
For example if I have a destructor that looks like this:
~a() { delete m1_; delete m2_; }That becomes more challenging to write correctly if delete can throw – suddenly RAII becomes a bottomless pit. Throwing destructors will break with a lot of existing code, not just STL containers and it is especially difficult to wrap implicit calls with try/catch.
I am surprised that async operations have not come up in this discussion. Let’s say I have an object with operations that are executing asynchronously, and I destruct it, although the destruction itself doesn’t fail the operations that are still pending do. What happens? This is a similar question to flushing caches. It isn’t the destruction itself that fails, but rather the operations that were still pending at the time of destruction that fail.
This opens the issue up to a larger problem – how do we describe asynchronous operations? Solve that issue (and there are many existing solutions of varying quality) and you solve the issue of how to report an asynchronous failure, and hence many (all?) failures in destruction.
Consider the common pattern of saving a program’s state to disk by saving to a temporary file, then after a successful save overwriting the existing state file with the temporary file. Now, if an error can be detected in the file destructor and it doesn’t throw, then the state will be overwritten with a faulty file. So currently the programmer has to remember to call close for the pattern to work. If the destructor could throw, it would always do the right thing.
You’re assuming that an exception means the object wasn’t destructed, but exceptions in destructors could indicate other failures. In the case of the file, the file object would be successfully destructed but the file wasn’t successfully written to disk.
It’s just as challenging, if not more so, to get the constructor right when you don’t use smart pointers.
Hi Sean! Long time no post.
Since you’re going all academic on us
, I should point out that your logic is flawed. I can easily make an object unreachable without destroying it: simply dynamically allocate and leak.
Only if you think the basic guarantee is worthless. A basic guarantee operation is only required to preserve invariants.
Very true! To be clear, I don’t advocate throwing from destructors. It’s almost always unnecessary and as you say, it complicates other code.
As pointed out by Daniel, this kind of a structure is a problem for your constructor as well. To avoid this mess, I follow the Dimov Rule:
Absolutely (though the STL containers could be adjusted), and the amount of code that would be broken could arguably make it a waste of time to improve the “Go directly to
terminate” situation.Fortunately,
try/catchare seldom neededMore than similar, I think this is an equivalent question to flushing caches, which is why I didn’t bring up async operations as a separate issue.
C++ already has answers to reporting async failures, so I’m not sure what you’re driving at here.