From: dave@boostpro.com   
      
   on Mon Jun 18 2012, DeMarcus wrote:   
      
   > On 2012-06-17 22:24, Dave Abrahams wrote:   
   >> on Fri Jun 15 2012, DeMarcus wrote:   
   >>   
   >>> I have always had the feeling that throwing from a destructor is a   
   >>> bad idea, but I've never seen a proof that it will be impossible   
   >>> even with future C++. Therefore I tried to state such "proof".   
   >>>   
   >>> With the input from the people here I managed to come up with an   
   >>> even better "proof" (at least in regards to convince myself). It's   
   >>> the following.   
   >>   
   >> I don't understand the drive to convince yourself that the idea of   
   >> throwing from destructors is somehow fundamentally broken.   
   >   
   > It's just my interest within programming. Right now I'm into   
   > exploring failure handling, and I try to see what exception handling   
   > could look like in ten years from now.   
      
   I think chances are good that it will be fine to throw from   
   destructors. In fact, I intend to submit a proposal to make it so.   
      
   >>> We can't throw from destructors unless we are able to bulk up all   
   >>> exceptions thrown from all the destructors until the first catch.   
   >>   
   >> Either that, or throw out information from secondary exceptions.   
   >>   
   >>> Even if that was possible we would need to choose between   
   >>>   
   >>> 1. Just running each destructor until it throws and then proceed   
   >>> with the next destructor.   
   >>>   
   >>> 2. Running the whole destructor even if it throws (bulking it).   
   >>   
   >> I don't unknw what either of these things mean. C++ already has   
   >> well-defined semantics for what happens when a dtor throws, but not   
   >> during stack unwinding. It's easy to extend these semantics for   
   >> exceptions thrown during unwinding if you're willing to chain up   
   >> multiple exceptions or throw out secondary exception information.   
   >>   
   >   
   > I apologize. I realize that I'm not clear with what I write so I'll   
   > try to explain better.   
   >   
   > If we allow throwing from destructors and want to be able to handle   
   > that scenario, we must be prepared to take care of all exceptions   
   > that can be thrown during unwinding. Right now we terminate if we   
   > throw an exception while another one is active.   
   >   
   > Let's look at scenario 1) above.   
   >   
   > One way to allow throwing from destructors (that works with current   
   > C++) is to chain exceptions to avoid termination, i.e. if you need   
   > to throw yet another exception during stack unwinding, you catch the   
   > active exception and chain it with the new exception and throw that.   
   >   
   > Even though we have circumvented the termination problem with this   
   > solution we still face the resource leak problem since the   
   > destructors most often must release the resources the very last   
   > thing they do.   
      
   There's no new "resource leak problem." Anytime you have an   
   un-managed resource you have to watch out for exceptions. Any   
   resources managed directly by an object become un-managed when you   
   enter the destructor body. Either don't throw an exception from   
   destructors of such objects (status quo), or (better) delegate   
   resource management to a sub-object.   
      
   > Now let's look at scenario 2) above (never mind the bulking, I'll   
   > come to that).   
   >   
   > You may be able (even with current C++) to queue up exceptions in a   
   > destructor, chain them all in the end of the destructor and   
   > throw. Now you have solved the resource release problem but most   
   > certainly the destructor will have undetermined behavior since code   
   > after an exception should not be executed.   
      
   What do you mean by "undetermined behavior?" The behavior of dtors in   
   the presence of exceptions is well-specified, and actually pretty   
   reasonable: when not already unwinding, the rest of the current dtor's   
   body is skipped but all of the other sub-objects are destroyed as part   
   of unwinding. If you don't like that behavior, well, don't throw from   
   your destructor. But if you want to throw from your destructor you   
   can easily work with these semantics to get a useful "determined"   
   result.   
      
   > Above is convincing enough for me to not throw from destructors.   
   > Nevertheless, if one would try anyway we hit another problem.   
      
   I'll bite (seeing as there's no problem so far)...   
      
   > The exception chaining will chain every exception it encounters. The   
   > idea (as I see it) with chaining is that you should be able to   
   > follow the exception and what caused it, look at its parent   
   > exception in the chain and its parent to finally see the root   
   > cause. This could be good in a logging system.   
      
   Actually, we plan to propose a handler (like the standard unexpected   
   handler) that can do anything it wants with the secondary exception,   
   including logging or chaining it. And if you do chain it, the root   
   cause of unwinding should stay at the head of the chain.   
      
   > Now if you are unfortunate, a destructor during unwinding may throw   
   > an exception that is independent of the active exception, but will   
   > still be part of the chain, causing confusion if the whole chain is   
   > written to a log.   
      
   Reading logs is not for the faint-of-heart already. I don't see how   
   this is going to make anything worse.   
      
   > That is what I meant with bulking exceptions. Bulking is a pure   
   > hypothetical non-C++ way of bulking up several exception chains to   
   > be thrown if several unrelated problems arises during unwinding. As   
   > I write I think it may even be feasible to have a main exception   
   > that contains a list of exception chains, but we will still suffer   
   > from resource leaks or undefined behavior.   
      
   No, those are imaginary problems, unless you choose to both   
      
   a) throw from your destructor, and   
   b) ignore the specified semantics of throwing from destructors   
      
   > You write "If you code to account for the semantics of exceptions   
   > thrown from destructors, you can throw them". That is what I try to   
   > say is close to impossible. Let's make a simple example.   
   >   
   > SomeClass::~SomeClass()   
   > {   
   > int* i = new int;   
   > doSomethingWithInt( &i );   
   >   
   > if( i == NULL )   
   > throw "Error";   
   > *i = 4711;   
   >   
   > delete i;   
   > }   
   >   
   > If you choose 1) from above, you will have a resource leak.   
      
   It's not at all clear from what you wrote above where there's a   
   resource leak. Since i is a raw pointer, ownership semantics are not   
   communicated by the interface. It's always possible that   
   doSomethingWithInt deletes the pointer before setting it to NULL. Try   
   making an example that uses unique_ptr instead. It's pretty hard to   
   generate a resource leak in that case.   
      
   Of course, it's still possible, but that misses the point: the fact   
   that you can write an example that has a resource leak doesn't prove   
      
   [continued in next message]   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|