From 7443812867444006048
X-Google-Language: ENGLISH,ASCII-7-bit
X-Google-Thread: f78e5,96a8c01f7043c2d9
X-Google-Attributes: gidf78e5,public
X-Google-ArrivalTime: 2002-09-25 09:02:53 PST
Path: archiver1.google.com!news1.google.com!newsfeed.stanford.edu!news.tele.dk!small.news.tele.dk!212.74.64.35!colt.net!kibo.news.demon.net!mutlu.news.demon.net!demon!mail2news.demon.co.uk!devnull
From: pierrebai@hotmail.com (Pierre Baillargeon)
Newsgroups: comp.std.c++
Subject: Re: Proposal: exception stack unwinding resumption
Date: Wed, 25 Sep 2002 16:02:51 +0000 (UTC)
Organization: http://groups.google.com/
Lines: 184
Approved: fjh@cs.mu.oz.au (Fergus Henderson , moderator of comp.std.c++)
Message-ID: <6df0c6a8.0209240716.6fb5ec00@posting.google.com>
References: <6df0c6a8.0208260746.633710cb@posting.google.com> <e6jg9.4504$7x3.204560@newsfep2-win.server.ntli.net> <23b84d65.0209161107.23bea618@posting.google.com> <6df0c6a8.0209170602.2cb54634@posting.google.com> <RW_h9.899$nU2.53545@newsfep1-gui.server.ntli.net> <6df0c6a8.0209201925.6faa9e57@posting.google.com> <c87c1cfb.0209220939.46d823af@posting.google.com> <6df0c6a8.0209230602.2aba3ca8@posting.google.com> <c87c1cfb.0209231924.6b639ab2@posting.google.com>
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
X-Trace: mail2news.demon.co.uk 1032969771 17946 10.0.0.1 (25 Sep 2002 16:02:51 GMT)
X-Complaints-To: abuse@demon.net
NNTP-Posting-Date: Wed, 25 Sep 2002 16:02:51 +0000 (UTC)
X-Received: from mulga.cs.mu.oz.au ([128.250.1.22])
	by news.demon.co.uk with esmtp (Exim 4.05)
	id 17uEcj-0004fJ-00
	for mail2news@news.news.demon.net; Wed, 25 Sep 2002 16:02:50 +0000
X-Received: from localhost (localhost [[UNIX: localhost]]) by mulga.cs.mu.OZ.AU
	id CAA24336; Thu, 26 Sep 2002 02:02:46 +1000 (EST)
X-Authentication-Warning: mulga.cs.mu.OZ.AU: fjh set sender to devnull@stump.algebra.com using -f
X-Path: comp-std-cpp-robomod!not-for-mail
X-Robomod: STUMP, ichudov@algebra.com (Igor Chudov)
X-Delivered-To: std-c++@ncar.ucar.edu
X-Newsgroups: comp.std.c++
X-NNTP-Posting-Date: 24 Sep 2002 15:16:52 GMT
Xref: archiver1.google.com comp.std.c++:14185

belvis@pacbell.net (Bob Bell) wrote in message news:<c87c1cfb.0209231924.6b639ab2@posting.google.com>...
> pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209230602.2aba3ca8@posting.google.com>...
> > belvis@pacbell.net (Bob Bell) wrote in message news:<c87c1cfb.0209220939.46d823af@posting.google.com>...
> > > pierrebai@hotmail.com (Pierre Baillargeon) wrote in message news:<6df0c6a8.0209201925.6faa9e57@posting.google.com>...
> > 
> 
> I disagree here. Current best practice seems to do pretty well
> already. My point is that you haven't yet established (to my
> satisfaction anyway) that current best practice is insufficient.

The current best practice (CBP) is sufficient for the current
situation where the language makes it difficult to determine if it is
safe to throw from the destructor. But anyway, I am very interested
about what would be a convincing argument for you and the other
detractors.

> Note that writing and testing that no destructor can ever throw" is on
> the same order of difficulty of testing the exception handling
> behavior of the program at large. In fact, I would even say that
> testing the exception handling behavior is probably harder. So by
> allowing destructors to throw, you're not really simplifying anything,
> because now you have to test that every throw in a destructor is
> properly handled at a catch point.

It's hard to debate which is simpler or harder: unless we have hard
data, it's only a matter of past experience ands opinions. Because I
use classic RAII extensively, I actually don't worry much about
exceptions. The programs usually have a few nexus of try/catch in the
main distpatch points. So in my experience, not having to worry about
destructors is easier, it's one less worry.

> Actually, the new rule you propose is "sometimes it's OK to throw from
> a destructor, depending on the setting of a global flag." That's the
> complexity: under this rule destructors may or may not be called in a
> context in which throwing is OK. The current rule says "it's never OK
> to throw from a destructor. Period." The current rule seems simpler to
> me.

I agree that the main weakness of the proposal is the switchable
behavior. I only proposed the switchable behavior to make the proposal
more acceptable to people who like the current terminate() calls.

I'm starting to believe that I was wrong on that point. Most
detractors mentionned that point. What's more, I've just read Francis
Glassborow's post in which he mention that he would like:

   "> destructors never throw.
      [snipped]
    And I wish we could actually make that the default for dtors"

Which I take it to mean that they would have an automatic invisible
try/catch block. My proposal only difference is to allow the exception
to escape the destructors when it could.

> How do I test that a destructor will never throw? One simple way,
> mentioned by you, is to wrap every destructor in try/catch. Another
> way, mentioned by Francis in his reply, is don't call functions that
> can throw. Usually, an inspection or review of the destructor code is
> all that is needed to verify that the destructor does not call
> throwing functions. This is viable because most destructors (that I
> write anyway) are trivial compared to the rest of the program, and
> usually do little more than release resources.

It's all true, but a bit of a simplification, IMO. In code reviews,
nothing in the call signature of a function tells you that it will
throw or not. It's always a matter of memory and consistent
maintenance to know that. Of course if you don't want your destructors
to throw you will keep them doing the least possible work. I know that
convincing people that expanding their usefulness is a hard battle.

> I agree with him that classes that can throw from destructors are
> broken. Consider a class A with a base class B. A::~A runs and throws.
> Not only is A::~A interrupted (which may cause it to leak resources,
> etc.), but the B::~B never gets called!
>
> Note that your proposal doesn't make this situation better.

I'd like to be corrected if I'm wrong, but I'm quite certain that if
~A() throws, all its data members and base classes are destructed. The
relevant clause, I believe, is 15.2, where it is said that partially
constructed objects will have their fully constructed parts destroyed
properly. It even mention that it works for arrays.

> But I think that ignoring the new exception is just as bad, but for
> slightly different reasons. You still suffer from information loss

No worse that with a try block in destructors, which is what is done
now.

> and
> resource leakage, but because the exception evaporates, there is
> nothing that any part of the program can do about it. Imagine a
> std::vector<Foo> being destroyed, where the destructor of the first
> Foo throws, leaving every other Foo, and possibly the std::vector,
> constructed and allocated, but leaked. And because the program doesn't
> terminate, it still limps along in some unpredictable, inconsistent
> state.

Objects which throw in their destructors cannot be used in STL
containers per the STL specification. My proposal doesn't change this,
which means that objects that throw cannot be used with STL
containers.

I didn't want to include changes to the STL in my proposal to limit
its scope. It would also be beyond my ability to correctly specify all
needed changes.

> > Tell me, what do you do in your destructors to have them not throw?
> > You put a try/catch. You catch everything, including divisions by zero
> > and invalid memory accesses on some implementation (MSVC++). You loose
> > information. That is the current best practice.
> 
> You're ignoring the possibility of designing the class to not call
> throwing operations in the destructor, which is the preferred method.

OK, then I'll admit that it is the preferred method. But there is a
nagging detail: what I would put in the destructor, the footer(), you
have to put somewhere else, right? That somewhere else, in the face of
an exception, must catch all exceptions, right? So what is different
here? Only the location of the code, not the behavior. Let's take a
real life example:

You have a resource, shared by many objects, that is ref-counted. Each
object grabs it when it needs it and releases afterward. When the
resources is released it may have to do some work on behalf of the
other objects. The real resource here is a color table that must be
written to a file. Accesses to the color table and file obviously have
to be serialized this way. The concurrent accesses may be actually
spread over many threads (so it is not simply a matter of reorganizing
code).

My code:

{
   Ref<Resource> ref( ColorTableManager.getCompressor() );
   //... use color table, may throw.
} // release happens here, exception may be swallowed.


Your code (correct me if I'm wrong):

{
   Ref<Resource> ref( ColorTableManager.getCompressor() );
   //... use color table.
   ref.release(); // sets a flag to not do work in destructor.
} // does release() in try block in destructor if flag not set.

I'm very interested to see how you and other detractors would write
that code. The point of the code example is: you have to release
something that can fail. *Where* you do it is only a red herring, you
have to do it anyway. The benefit of my code and proposal is that
there is more automation (and one less flag).

> > My proposal just makes it automatic, language-enforced. You gain
> > flexibility in the bargain because in the normal case (i.e.: not stack
> > unwinding), your destructors will *not loose information*. So acording
> > to you, my proposal is a gain. right? ;-)
> 
> Not only is the exception lost, but the object that generated it goes
> to The Twilight Zone, because it's neither alive nor dead. All legal
> references to the object are gone, so nothing meaningful can be done
> to correct the situation. That sounds like information loss to me.

See above code example the discussion about 15.2 above. Prove me
wrong.

> You're giving the programmer a function,
> set_unwinding__resumption, and allowing him to call it at any time.
> You can't hand-wave it away by saying that probably he'll call it once
> at the beginning.

I agree, it's the weak point of my proposal. Would it be more
acceptable if the proposed behavior would always be enabled? That
would not change the meaning of existing code, since it has really
been properly tested and reviewed right? ;-). Seriously folks, would
the proposal be more acceptable like that? I didn't say acceptable
(yet), just more...

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]



