From -257505940468078284
X-Google-Language: ENGLISH,ASCII-7-bit
X-Google-Thread: f78e5,96a8c01f7043c2d9
X-Google-Attributes: gidf78e5,public
X-Google-ArrivalTime: 2002-09-23 20:43:55 PST
Path: archiver1.google.com!news1.google.com!newsfeed.stanford.edu!skynet.be!skynet.be!kibo.news.demon.net!mutlu.news.demon.net!demon!mail2news.demon.co.uk!devnull
From: belvis@pacbell.net (Bob Bell)
Newsgroups: comp.std.c++
Subject: Re: Proposal: exception stack unwinding resumption
Date: Tue, 24 Sep 2002 03:43:53 +0000 (UTC)
Organization: http://groups.google.com/
Lines: 167
Approved: fjh@cs.mu.oz.au (Fergus Henderson , moderator of comp.std.c++)
Message-ID: <c87c1cfb.0209231924.6b639ab2@posting.google.com>
References: <6df0c6a8.0208260746.633710cb@posting.google.com> <6df0c6a8.0209121133.6f081c37@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>
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
X-Trace: mail2news.demon.co.uk 1032839033 4527 10.0.0.1 (24 Sep 2002 03:43:53 GMT)
X-Complaints-To: abuse@demon.net
NNTP-Posting-Date: Tue, 24 Sep 2002 03:43:53 +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 17tgc2-0001As-00
	for mail2news@news.news.demon.net; Tue, 24 Sep 2002 03:43:51 +0000
X-Received: from localhost (localhost [[UNIX: localhost]]) by mulga.cs.mu.OZ.AU
	id NAA28929; Tue, 24 Sep 2002 13:43:16 +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 03:24:03 GMT
X-MailScanner: PASSED (v1.2.6 89525 g8O3XbhR095491 mailbox3.ucsd.edu)
Xref: archiver1.google.com comp.std.c++:14154

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>...
> > > 
> > > And what I'm saying is "what if we allowed it to be non-dangerous if
> > > desired". The original thread was about a change to the standard, not
> > > a discussion of current best practice.
> > 
> > I would think that is appropriate. Any discussion of a change to the
> > standard should involve a discussion of current best practice, in
> > order to establish that, in some way, current best practice is not
> > good enough. This has not yet been established, so there is crucial
> > support missing from your proposal.
> 
> Where I disagree with your point is that my proposal *changes* what
> would be considered best practice. And in fact I do illustrate where
> the current best practice fails:
> 
> - In automating things that can fail.
> - In the difficulty of writing and testing that no destructor can ever
> throw.

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.

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.

> > Your proposal seems to change what is currently a simple, easy to
> > understand, easy to follow rule into something complex and error
> > prone. I don't see the improvement.
> 
> Where is the complexity? The current rule is easy to understand, easy
> to get wrong. Tell me: how do you actually test that no exception will
> ever be thrown from a destructor? The new rule is easy to understand:
> you can throw from a destructor. Where is the complexity?

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.

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.

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.

> > You propose allowing an unhandled exception to be discarded and
> > replaced by an exception thrown in a destructor.
> 
> This is incorrect. What I'm proposing is that the second exception is
> ignored. It is like putting a try block around all destructors when
> stack unwinding is under way. Which is *what you currently need to
> do*!

Oops! My bad. I misread your OP.

But I think that ignoring the new exception is just as bad, but for
slightly different reasons. You still suffer from information loss 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.

> >  This seems like a
> > really bad idea, because you're saying that under some circumstances
> > it's OK to throw away information. If a low-level function throws an
> > exception that should go to a high-level caller, an exception in a
> > destructor would "short-circuit" that.
> 
> 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.

I am also deliberately ignoring the possibility of divide by zero-type
exceptions -- basically, any exception that doesn't originate with a
throw statement. I've seen some pretty good discussions about why
these kinds of exceptions are not particularly useful, mainly since
they are not portable. The MSVC++ implementation is also broken in
that these exceptions are not accessible or catchable, which makes it
impossible to filter them out.

Personally, I regard conditions like divide by zero as a bug, which
should be fixed, not an expected condition that my exception
handler(s) should deal with.

> 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.

> > Finally, you haven't simplified the implementation of destructors,
> > you've complicated it. Because now they may have to be sensitive to
> > whether or not throwing is "allowed." If not (if some higher-level
> > caller hasn't called set_unwinding_resumption(true)), the destructor
> > must absorb the exception and prevent its escape. So under some
> > circumstances this destructor reports errors, and under others it
> > doesn't? I don't know about you, but this would simply encourage me to
> > take the simpler road: destructors never throw.
> 
> While this problem is true in theory, it won't be in practice, IMO.
> 
> The first case, when it is set to true is no problem: old classes will
> go on using try blocks, and new ones will be happy.
> 
> The second case. when it is set to false. The question is when will it
> be set to false? Not by old classes, since they are not aware of the
> existence of the function. So it will probably be managed by a
> singleton that keeps a ref-count counter to know when to set it back
> to false.
> 
> The common case, I expect, will actually be that it is either never
> used or set to true once and for all in main() and in all statically
> created singletons that are constructed before main().

The biggest problem with your proposal is that a destructor _does not
know_ if it's OK to throw. It could be destroyed with the
unwinding_resumption flag set to false, in which case the destructor
should not throw. 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. A library class, for example, can't know what
context it will be used in. Perhaps I'll use the class in a project
that doesn't ever call set_unwinding_resumption, while in your project
you call set_unwinding_resumption(true) and expect destructors to
throw. Thus, the class must either be sensitive to whether throwing is
allowed, or simply take the easier way out: just don't throw.

Bob Bell

---
[ 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                       ]



