Forums before death by AOL, social media and spammers... "We can't have nice things"
|    comp.lang.c++.moderated    |    Moderated discussion of C++ superhackery    |    33,346 messages    |
[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]
|    Message 32,367 of 33,346    |
|    Thomas Richter to All    |
|    Re: Will we ever be able to throw from a    |
|    05 Jun 12 17:28:38    |
   
   From: thor@math.tu-berlin.de   
      
   Am 05.06.2012 19:26, schrieb Zeljko Vrba:   
      
   >> Can you? What happened with the object under destruction?   
   >> Apparently, it is not properly destructed, otherwise there wouldn't   
   >> be an exception, yet you have no longer a way of accessing this   
   >> object and trying a different clean-up strategy.   
   >   
   > You can wrap the relevant (not-yet-destructed) part of the object into   
   > the exception object and try to deal with it in catch. For example,   
   > if the object wraps a file, you would transport its file-descriptor   
   > through the exception to the catch site.   
      
   And where take you this object from? After all, its construction might   
   also trigger an exception, let it be due to memory exhaustion.   
      
   >> That handling cleanup errors (and others) requires cleanup of   
   >> objects.   
   >   
   > I'm not sure I get it. If in `delete x;` the destructor throws, is   
   > the memory freed? This is basically the only piece of work that the   
   > program can't do itself.   
      
   I don't remember what the standard has to say about that, but at least   
   the stack is unwound, so objects on the stack are most certainly gone.   
   Objects on the heap might or might not go, but what would you do with a   
   pointer to such an object? There are no longer any class invariants you   
   could depend on since the object is already destroyed by the very means   
   of the C++ standard.   
      
   >> This actually means, however, that you should better replace   
   >> throwing in the destructor by an assert, or instead build an   
   >> alternative   
   >   
   > assert() should be understood as no-op, so it's not adequate   
   > replacement.   
      
   Huh? assert() is certainly not a no-op. It is a mechanism to check for   
   broken class invariants due to bugs in the code.   
      
   >> cleanup strategy into the destructor that can happen the error   
   >> locally.   
   >   
   > In general this means that the 1) object needs to know where it was   
   > called from, or 2) I need many subclasses which differ only in their   
   > destructors.   
      
   If you need the context to handle the error, then a throwing destructor   
   is neither the solution since there is nothing you could do with the   
   object in first place. It is no longer in a valid state, so the caller   
   cannot do anything about it. If you need caller assistance on an error,   
   you need to provide a second method for cleaning up the object state,   
   something like "close()". Then you can ensure, by a proper object   
   design, that the state of the object is still such that you can do   
   anything about it. However, after the destructor is called, the object   
   is gone.   
      
   For the problem you have a throwing destructor is not the solution.   
      
   >> The problem is that you cannot delegate the handling of cleanup   
   >> errors as with other errors since the object that caused the problem   
   >> is already no longer reachable.   
   >   
   > You can copy the relevant part of the object and pass it as the part   
   > of the exception.   
      
   Where to?   
      
   >> Are they? There are many error reporting algorithms, amongst them is   
   >> also assert().   
   >   
   > Why do you believe that crashing on assert() [assuming it's not a   
   > noop] is better than std::terminate being called?   
      
   Whether assert() is "better" than "terminate" depends on the situation.   
   Assert is better for reporting errors in the program logic, i.e. a   
   program should run into an assertion whenever the code detects a state   
   that should logically not exist, but does due to some defect in the   
   code. It expresses this situation correctly.   
      
   terminate(), on the other hand, just terminates. Assert() could use   
   operating system means to report the error to the user and get some   
   debug output. For example a core dump. That is usually more helpful *in   
   such situations* that just terminating the code.   
      
   Terminate might be better in other situations. It depends. One can   
   barely say "one is always better than the other".   
      
   >> If you throw an exception, the point is that there is somewhere a   
   >> catch() that can deal with the situation - otherwise, you wouldn't   
   >> throw in first place. That's quite logical. However, if by language   
   >   
   > That may be the point, but it's not the *whole* point. For example,   
   > some STL algorithms throw (e.g., std::string methods throw on invalid   
   > indices), but I never write corresponding catch. End result: if my   
   > program passes invalid index (i.e., it is buggy), it crashes, just as   
   > it would after assert.   
      
   Wait a minute - that rather depends on its use. operator[] of the string   
   does *not need to* throw since it doesn't have bounds checking   
   guaranteed by the specs. If the index is out of bounds, the   
   implementation may do whatever it seems fit. Terminate, assert, nothing.   
   std::string::at, however, does throw. Thus, if you need bounds checking,   
   you can have it by using the appropriate function, but only then.   
      
   > STL is thus an example of using exceptions as "always on" substitute   
   > for assert().   
      
   Sorry - no, that's simply not correct. I don't know what your   
   implementation does, but that is surely not required by the specs for   
   operator[].   
      
   >> construct, you *cannot* deal with the situation since the object to   
   >> deal with is no longer reachable, the catch is superfluous in first   
   >   
   > The exception object can transport the relevant state of the deceased   
   > object. I'm repeating myself from the last post, but it seems that   
   > this point hasn't come across since you're repeating this.   
      
   I simply don't consider this a valid argument. Look at the design you   
   have: You delete the object, thus tell the code "please get rid of this   
   thing". Yet, what you get is a new instance of a partial object. So what   
   exactly does this mean? The object didn't want to die, so the attempt to   
   delete it wasn't justified. Wait a minute - we could have done that in a   
   simpler way: First try an orderly shutdown ("close()") which we can   
   handle *without* loosing the object, and then when we know that the   
   Parrot is really dead, bury it.   
      
      
   >>> The real question is: should destructors be used to enofrce   
   >>> invariants? If yes, how do you report inability to fulfill   
   >>> invariants? If not, what about RAII -- in this case the   
   >>> applicability of RAII suddenly gets *very* narrow.   
   >   
   >> I don't quite follow.   
   >   
   > As noted in another post, RAII is often used as generic scope-guard; a   
   > callback to be executed on scope exit. If we limit us to dtors being   
   > used ONLY for operations that "cannot fail", this 'other' use of the   
   > RAII idiom is invalid.   
      
   It means that you cannot use RAII where the object release may create an   
   exception, exactly. Why is that a problem? Typical example: RAII   
      
   [continued in next message]   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|
[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]
(c) 1994, bbs@darkrealms.ca