This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [rfc] fix libstdc++/10606


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]