This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
Re: [rfc] fix libstdc++/10606
- From: Martin Sebor <sebor at roguewave dot com>
- To: Richard Henderson <rth at redhat dot com>
- Cc: Benjamin Kosnik <bkoz at redhat dot com>, Paolo Carlini <pcarlini at suse dot de>, gcc-patches at gcc dot gnu dot org, libstdc++ at gcc dot gnu dot org, Cary Coutant <cary at cup dot hp dot com>
- Date: Tue, 04 Jan 2005 16:45:45 -0700
- Subject: Re: [rfc] fix libstdc++/10606
- References: <20041228082031.GA6135@redhat.com> <41D1D97E.4070506@suse.de> <20041228225439.GA7588@redhat.com> <20050104155218.1e3c1cb8.bkoz@redhat.com> <20050104224721.GA13922@redhat.com>
Richard Henderson wrote:
On Tue, Jan 04, 2005 at 03:52:18PM -0600, Benjamin Kosnik wrote:
+ {
+ S s0;
+ throw s0; // s1 is the exception object
we want
+ { 1, 0, false },
for the s1 temporary object test data. I think this is correct because
this test occurs in the temporary object's ctor body, before the the
object is considered complete. I don't think it makes sense to have
uncaught_exception return true here, since the "uncaught exception
object" in question hasn't yet been (fully) constructed.
On the other hand, we *will* std::terminate if the s0->s1 copy
constructor throws -- just like if the s1->s2 copy constructor
throws while setting up the catch handler.
Given that the copy constructor cannot throw, I think it makes
sense to consider that we've begun processing the exception,
and thus there must be an uncaught exception.
It all depends on what you consider uncaught_exception useful
for, I guess.
When uncaught_exception() returns true, throwing an exception may
(but isn't required to) cause the runtime to call terminate().
When it returns false, throwing an exception is guaranteed not
to cause a call to terminate (provided there's a handler for it,
of course). This is useful in implementing the copy ctors of
exception classes.
From the table in core issue 475, requiring "0|1|1|1" as the
proposed resolution suggests, means that either of "0|1|1|1|OK"
and "0|1|1|1|ABRT" is permitted. Requiring "0|0|1|1" (which would
be my preference) would mean that only "0|0|1|1|OK" would be
allowed (i.e., no call to terminate() could happen during the
construction of the [temporary] object to be thrown).
Suppose "0|1|1|1" is required and you are implementing the copy
ctor of an exception class. You'd have to code it along these
lines for exception safety:
struct B { /* third party non-exception class */ };
struct M { /* third party non-exception class */ };
struct S: B {
M m;
S (const S &rhs)
try: B (rhs), m (rhs.m) { /* ... */ }
catch (...) {
if (std::uncaught_exception ()) {
// what to do here?
}
else
throw;
}
};
try {
throw S (); // can only throw S or call terminate()
}
catch (const S &s) { /* cannot throw */ }
catch (...) { /* never reached */ }
and hope that B's or M's copy ctor won't throw while S is being
thrown. If they did there isn't much you could do except terminate
your program (or let the exception escape and risk that the runtime
might call terminate()).
If, on the other hand, "0|0|1|1" were required, the copy ctor wouldn't
have to worry about exceptions and could simply let them propagate to
the caller. If an exception occurred while S was being thrown the new
exception would simply displace S. A defensively written program won't
catch exceptions by value so S won't have to worry about terminate()
being called while the handler's exception object is being initialized.
The code would look like this:
struct S: B {
M m;
S (const S &rhs): B (rhs), m (rhs.m) { /* ... */ }
};
try {
throw S (); // can throw S or anything else
}
catch (const S &s) { /* cannot throw */ }
catch (...) { /* handle exceptions thrown from S::S(const S&) */ }
Martin
Here's the issue for reference:
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#475