This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
Re: [rfc] fix libstdc++/10606
- From: Benjamin Kosnik <bkoz at redhat dot com>
- To: Martin Sebor <sebor at roguewave dot com>
- Cc: Richard Henderson <rth 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, 4 Jan 2005 19:21:25 -0600
- Subject: Re: [rfc] fix libstdc++/10606
- Organization: Red Hat / Chicago
- References: <20041228082031.GA6135@redhat.com><41D1D97E.4070506@suse.de><20041228225439.GA7588@redhat.com><20050104155218.1e3c1cb8.bkoz@redhat.com><20050104224721.GA13922@redhat.com><41DB2AA9.2030108@roguewave.com>
>> 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.
Also exceptions in dtors... there is standard-mandated usage for this already.
> 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).
For the lazy among us, can you post the test code for constructing
that table please?
>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 */ }
Disaster....
>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&) */ }
...compelling.
Also, this clarifies/simplifies uncaught_exception to something that
means true when an exception object (not temporary object) is "in
flight." I like that part.
-benjamin