πŸš€ go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

10 total messages Started by Anatoli Tubman Tue, 14 Nov 1995 18:52
Re: Exceptions in destructors
#2572
Author: Anatoli Tubman
Date: Tue, 14 Nov 1995 18:52
67 lines
3659 bytes
******************* MODERATOR'S ATTENTION PLEASE **************************
I have never posted to a Usenet newsgroup before. I don't even have access
to NNTP, so I cannot read news on a regular basis. So it may well happen
that I'm doing something wrong. If this is the case, can you please email me
your suggestions? 
***************************************************************************

[ moderator's note: You did it right. You can post to comp.std.c++
  and it will be forwarded automatically to a moderator, or you can
  email the article to the "submission address" below. -sdc ]

James Kanze <kanze@lts.sel.alcatel.de> writes:
> 1. Given: 
>         T a[ 5 ] ; 
> When destructing the third element (for example), an exception is 
> thrown.  Are the remaining elements destructed?  I would say yes
 
Unfortunately, DWP is not clear enough in this area.
Here what it says (the numbers of statements are mine):
 
  (1) An object that is partially constructed will have destructors executed
      only for its fully constructed sub-objects.  
  (2) Should a constructor for an element of an automatic array throw an 
      exception, only the constructed elements of that array will be 
      destroyed.
  (3) If the object or array was allocated in a new-expression, the storage 
      occupied by that object is sometimes deleted also.

(Note that (3) does not appear in the ARM.)

I think that (2) should be rewritten, perhaps like this:

   (2*) An object becomes fully constructed when its constructor returns
        and remains such until a destructor for it is invoked.

This covers the case covered by the present wording, and many others, for
which DWP makes no provisions -- without violating the spirit.

Also, (3) needs to be a little bit more specific. I think the following
will do well:

   (3*) A delete-expression always deletes storage allocated for its operand,
        regardless of any exceptions that might be thrown by any of the 
        destructors involved.

If an implementation does not impose an added cost to code that does not
catch or throw exceptions, it can be easily modified to comply with the
above rules and still add no overhead to such code.

Whether throwing from destructors shoud ever be considered by the C++ user
(as opposed to implementor) is a totally different question.
I believe that inability to destroy what you've just created
is a symptom of some very serious problem, for which terminate() is an
appropriate solution. However, standards should not prohibit
things only because they are ``bad style'' -- there must be other reasons.

-----------------------------------------------------------------
Anatoli Tubman <anatoli@ptc.com>
DISCLAIMER: My opinions are not necessarily those of my employer
or any other person or entity.
-----------------------------------------------------------------


[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Re: Exceptions in destructors
#2626
Author: J. Kanze
Date: Fri, 17 Nov 1995 00:44
104 lines
5441 bytes
Anatoli Tubman (anatoli@ptc.com) wrote:

|> James Kanze <kanze@lts.sel.alcatel.de> writes:
|> > 1. Given: 
|> >         T a[ 5 ] ; 
|> > When destructing the third element (for example), an exception is 
|> > thrown.  Are the remaining elements destructed?  I would say yes
|>  
|> Unfortunately, DWP is not clear enough in this area.
|> Here what it says (the numbers of statements are mine):
|>  
|>   (1) An object that is partially constructed will have destructors
|>       executed only for its fully constructed sub-objects.  
|>   (2) Should a constructor for an element of an automatic array throw an 
|>       exception, only the constructed elements of that array will be 
|>       destroyed.

Does this only apply to automatic arrays.  I thought it applied to all
arrays.

|>   (3) If the object or array was allocated in a new-expression, the storage
|>       occupied by that object is sometimes deleted also.

|> (Note that (3) does not appear in the ARM.)

|> I think that (2) should be rewritten, perhaps like this:

|>    (2*) An object becomes fully constructed when its constructor returns
|>         and remains such until a destructor for it is invoked.

IMHO, this is already covered in (1).

|> This covers the case covered by the present wording, and many others, for
|> which DWP makes no provisions -- without violating the spirit.

|> Also, (3) needs to be a little bit more specific. I think the following
|> will do well:

|>    (3*) A delete-expression always deletes storage allocated for its
|>         operand, regardless of any exceptions that might be thrown by any
|>         of the destructors involved.

This is an entirely new point (4).  (3) talks about exceptions in
new-expressions; here we talk about exceptions in delete-expressions.

Consider the following:

	auto_ptr< T >*		pp = new auto_ptr< T >[ 3 ] ;
	//  ..
	delete [] pp ;

According to the standard, this will result in calling the destructors
of auto_ptr on pp[ 2 ], pp[ 1 ] and pp[ 0 ], in that order, then calling
operator delete[] on the resulting raw memory.  The destructors for the
auto_ptr's will delete the object they point to.

Now suppose that T::~T() throws an exception when destructing pp[ 1 ].
If the implementation does not continue by destructing pp[ 0 ] and
calling operator delete, there is *no* way the user can.  Even if the
user traps the exception, he cannot call ``delete [] pp'' a second time,
since this would cause the destructor of the already destructed pp[ 2 ]
to be called.  He could not even attempt to do the job by hand, calling
explicitly the remaining destructors, and then operator delete[], for
several reasons:

1. He has, a priori, no way of knowing how many of the objects have
already been destructed.

2. Even if he did, he cannot call the destructor on the auto_ptr element
in which the exception occurred.  So the memory that auto_ptr pointed to
is lost.

3. Even solving this problem, he cannot call operator delete[] directly,
since it is not guaranteed that the address returned by operator new[]
is the same as that returned by the new expression (and in general, in
the above case, it isn't).

|> If an implementation does not impose an added cost to code that does not
|> catch or throw exceptions, it can be easily modified to comply with the
|> above rules and still add no overhead to such code.

|> Whether throwing from destructors shoud ever be considered by the C++ user
|> (as opposed to implementor) is a totally different question.
|> I believe that inability to destroy what you've just created
|> is a symptom of some very serious problem, for which terminate() is an
|> appropriate solution. However, standards should not prohibit
|> things only because they are ``bad style'' -- there must be other reasons.

Agreed.  In the internet protocol suite, there is a ``robustness
principle'' which states: "Be liberal in what you accept, and
conservative in what you send."  I like to apply this generally: a user
should not allow exceptions to escape from destructors, but the
implementaion (and the language definition) should work when the case
occurs.  (Note that unless the implementation does go on to free the
memory, auto_ptr is not exception safe under its current definition.)
-- 
James Kanze           (+33) 88 14 49 00          email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils, itudes et rialisations en logiciel orienti objet --
              -- A la recherche d'une activiti dans une region francophone
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Re: Exceptions in destructors
#2713
Author: Herb Sutter
Date: Sat, 25 Nov 1995 19:20
68 lines
3708 bytes
In article <494oog$hmf@gabi.gabi-soft.fr>,
   kanze@gabi-soft.fr (J. Kanze) wrote:
>Fergus Henderson (fjh@munta.cs.mu.OZ.AU) wrote:
>|> schuenem@informatik.tu-muenchen.de (Ulf Schuenemann) writes:
>|> >How about saying that dtors have an empty exception-specification 
>|> >(either by default or always) ?
>
>|> How does that help?

I think he wants unexpected() to be called, rather than an inelegant 
terminate(), when a dtor throws during unwinding.

>|> >Then there are 3 cases possible:
>|> >- The dtor causes no eception.
>|> >- The dtor catches all exceptions and continues - save,
>|> >  but what happens to client-provided exception handling ?
>|> >- The dtor does not handle an exception that happend to be
>|> >  be raised - unexpected() is called.
>
>|> Yes, and what if the programmer has defined unexpected()
>|> to rethrow the exception?  (In most circumstances, doing so
>|> is probably a much better idea than just crashing via
>|> a call to abort().)
>
>1. How can it know what the exception was to rethrow it?  Is a throw
>without an argument legal here?  (I don't think so, but I'm not sure.)
	
I was about to say that it must be legal -- this is precisely what Cline and 
Lomow suggest in their FAQ book (FAQ #271).  However:

>2. This behavior doesn't seem conform to the required behavior in
>18.6.1.2.  I believe that when a user provided routine violates required
>behavior, undefined behavior results.

[Note: 18.6.1.2 in the April draft is now 18.6.2.2 in the Sept WP.]

It seems you're right, since the unexpected_handler isn't supposed to throw 
the same (unexpected) exception again.  Relevant section follows (from the 
Sept WP; the April draft looks identical):

---8<---8<----------------------------------------------------------------
  Required behavior:
    an  unexpected_handler  shall either throw an exception or terminate
    execution of the program without returning to the caller.  An  unexΒ­
    pected_handler may perform any of the following:

  --throw an exception that satisfies the exception specification;
  --throw a bad_exception exception;
  --call terminate();
  --call either abort() or exit();

  Default behavior:
    The implementation's default unexpected_handler calls terminate().
---8<---8<----------------------------------------------------------------

Is disallowing a rethrow deliberate, or something that could be relaxed to 
allow rethrowing as Cline and Lomow suggest?

Herb


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Herb Sutter                 2228 Urwin, Ste 102         voice (416) 618-0184
Connected Object Solutions  Oakville ON Canada L6L 2T2    fax (905) 847-6019

[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Re: Exceptions in destructors
#2731
Author: Fergus Henderson
Date: Sun, 26 Nov 1995 16:40
71 lines
3715 bytes
kanze@gabi-soft.fr (J. Kanze) writes:

>Fergus Henderson (fjh@munta.cs.mu.OZ.AU) wrote:
>|> schuenem@informatik.tu-muenchen.de (Ulf Schuenemann) writes:
>
>|> >How about saying that dtors have an empty exception-specification 
>|> >(either by default or always) ?
>
>|> How does that help?
>
>|> >Then there are 3 cases possible:
>|> >- The dtor causes no eception.
>|> >- The dtor catches all exceptions and continues - save,
>|> >  but what happens to client-provided exception handling ?
>|> >- The dtor does not handle an exception that happend to be
>|> >  be raised - unexpected() is called.
>
>|> Yes, and what if the programmer has defined unexpected()
>|> to rethrow the exception?  (In most circumstances, doing so
>|> is probably a much better idea than just crashing via
>|> a call to abort().)
>
>1. How can it know what the exception was to rethrow it?  Is a throw
>without an argument legal here?  (I don't think so, but I'm not sure.)

Yes, a `throw;' statement is legal within an unexpected() handler.
15.1/5-6 is quite clear:

| 5 A  throw-expression  with no operand rethrows the exception being han-
|   dled without copying it.  
| 
| 6 The exception thrown is the one most recently caught and not finished.
|   An  exception is considered caught when initialization is complete for
|   the formal parameter of the corresponding catch clause, or when termi-
|   nate()  or  unexpected()  is  entered due to a throw.  An exception is
|   considered finished when the corresponding catch clause exits.

On entering unexpected(), the exception is considered handled, but not
finished, so `throw;' is fine.

The ARM contains an example of the use of `throw;' in an unexpected
handler (pages 365-366).

>2. This behavior doesn't seem conform to the required behavior in
>18.6.1.2.  I believe that when a user provided routine violates required
>behavior, undefined behavior results.

Hmm, you're right - according to 18.6.1.2, an unexpected handler
must not throw the same exception.  This would seem to imply that
`throw;' would be ok in an unexpected handler only if the exception didn't
escape, e.g.

	void my_unexpected_handler() { try { throw; } catch(...) {} }

and also that the example in the ARM has undefined behaviour.
On the other hand, 15.5.2 definitely seems to assume that an
unexpected handler can throw an exception not in the exception
specification, and carefully describes the behaviour in that case.

So the draft is self-contradictory here.  I think all you can really
say is that one of 15.5.2 and 18.6.1.2 is wrong.

--
Fergus Henderson             	WWW: http://www.cs.mu.oz.au/~fjh
fjh@cs.mu.oz.au              	PGP: finger fjh@128.250.37.3
I will have little or no net access from Nov 30 until Dec 25.
Please email me a copy of any follow-ups.

[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Re: Exceptions in destructors
#2747
Author: J. Kanze
Date: Mon, 27 Nov 1995 12:54
75 lines
4334 bytes
Herb Sutter (herbs@interlog.com) wrote:
|> In article <494oog$hmf@gabi.gabi-soft.fr>,
|>    kanze@gabi-soft.fr (J. Kanze) wrote:
|> >Fergus Henderson (fjh@munta.cs.mu.OZ.AU) wrote:
|> >|> schuenem@informatik.tu-muenchen.de (Ulf Schuenemann) writes:
|> >|> >How about saying that dtors have an empty exception-specification 
|> >|> >(either by default or always) ?
|> >
|> >|> How does that help?

|> I think he wants unexpected() to be called, rather than an inelegant 
|> terminate(), when a dtor throws during unwinding.

|> >|> >Then there are 3 cases possible:
|> >|> >- The dtor causes no eception.
|> >|> >- The dtor catches all exceptions and continues - save,
|> >|> >  but what happens to client-provided exception handling ?
|> >|> >- The dtor does not handle an exception that happend to be
|> >|> >  be raised - unexpected() is called.
|> >
|> >|> Yes, and what if the programmer has defined unexpected()
|> >|> to rethrow the exception?  (In most circumstances, doing so
|> >|> is probably a much better idea than just crashing via
|> >|> a call to abort().)
|> >
|> >1. How can it know what the exception was to rethrow it?  Is a throw
|> >without an argument legal here?  (I don't think so, but I'm not sure.)
|> 	
|> I was about to say that it must be legal -- this is precisely what Cline
|> and Lomow suggest in their FAQ book (FAQ #271).  However:

|> >2. This behavior doesn't seem conform to the required behavior in
|> >18.6.1.2.  I believe that when a user provided routine violates required
|> >behavior, undefined behavior results.

|> [Note: 18.6.1.2 in the April draft is now 18.6.2.2 in the Sept WP.]

|> It seems you're right, since the unexpected_handler isn't supposed to throw
|> the same (unexpected) exception again.  Relevant section follows (from the
|> Sept WP; the April draft looks identical):

|> ---8<---8<----------------------------------------------------------------
|>   Required behavior:
|>     an  unexpected_handler  shall either throw an exception or terminate
|>     execution of the program without returning to the caller.  An  unex-
|>     pected_handler may perform any of the following:

|>   --throw an exception that satisfies the exception specification;
|>   --throw a bad_exception exception;
|>   --call terminate();
|>   --call either abort() or exit();

|>   Default behavior:
|>     The implementation's default unexpected_handler calls terminate().
|> ---8<---8<----------------------------------------------------------------

|> Is disallowing a rethrow deliberate, or something that could be relaxed to 
|> allow rethrowing as Cline and Lomow suggest?

I would imagine that it must be deliberate.  The whole purpose of
exception specifications is to offer some sort of a run-time guarantee
as to which exceptions may propagate out.  Unexpected is called because
the program is attempting to violate that guarantee; if unexpected
re-throws the same exception, either it causes unexpected to be called
again (resulting in an endless loop), or the guarantee is violated (and
thus not a guarantee).
-- 
James Kanze           (+33) 88 14 49 00          email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils, itudes et rialisations en logiciel orienti objet --
              -- A la recherche d'une activiti dans une region francophone
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Re: Exceptions in destructors
#2728
Author: J. Kanze
Date: Mon, 27 Nov 1995 12:57
124 lines
7007 bytes
Fergus Henderson (fjh@munta.cs.mu.OZ.AU) wrote:
|> kanze@gabi-soft.fr (J. Kanze) writes:

|> >Fergus Henderson (fjh@munta.cs.mu.OZ.AU) wrote:
|> >|> schuenem@informatik.tu-muenchen.de (Ulf Schuenemann) writes:
|> >
|> >|> >How about saying that dtors have an empty exception-specification 
|> >|> >(either by default or always) ?
|> >
|> >|> How does that help?
|> >
|> >|> >Then there are 3 cases possible:
|> >|> >- The dtor causes no eception.
|> >|> >- The dtor catches all exceptions and continues - save,
|> >|> >  but what happens to client-provided exception handling ?
|> >|> >- The dtor does not handle an exception that happend to be
|> >|> >  be raised - unexpected() is called.
|> >
|> >|> Yes, and what if the programmer has defined unexpected()
|> >|> to rethrow the exception?  (In most circumstances, doing so
|> >|> is probably a much better idea than just crashing via
|> >|> a call to abort().)
|> >
|> >1. How can it know what the exception was to rethrow it?  Is a throw
|> >without an argument legal here?  (I don't think so, but I'm not sure.)

|> Yes, a `throw;' statement is legal within an unexpected() handler.
|> 15.1/5-6 is quite clear:

|> | 5 A  throw-expression  with no operand rethrows the exception being han-
|> |   dled without copying it.  
|> | 
|> | 6 The exception thrown is the one most recently caught and not finished.
|> |   An  exception is considered caught when initialization is complete for
|> |   the formal parameter of the corresponding catch clause, or when termi-
|> |   nate()  or  unexpected()  is  entered due to a throw.  An exception is
|> |   considered finished when the corresponding catch clause exits.

|> On entering unexpected(), the exception is considered handled, but not
|> finished, so `throw;' is fine.

|> The ARM contains an example of the use of `throw;' in an unexpected
|> handler (pages 365-366).

Great.  So what does it mean.  Suppose I write a function:

	void f() throw() ...

What I'm trying to guarantee is that this function does not throw any
exceptions; this is a very important guarantee if exception safety is to
be taken seriously.  In fact, of course, I've only guaranteed that it
won't throw any exceptions other than bad_exception (which is already a
serious compromise).

If unexpected does rethrow (or if it throws any exception other than
bad_exception), then I would expect unexpected to be called.  If the
exception specifications are to mean anything, then the exception cannot
propagate out.

(IMHO, at least in the case of throw(), even bad_exception shouldn't
propagate out.  Basically, allowing bad_exception to be an exception
means that even functions like the above can throw exceptions, which in
turn makes exception safety significantly harder; you must be prepared
to accept an exception at any time, since there is *no* way to ensure
that a given function never throws an exception.)

|> >2. This behavior doesn't seem conform to the required behavior in
|> >18.6.1.2.  I believe that when a user provided routine violates required
|> >behavior, undefined behavior results.

|> Hmm, you're right - according to 18.6.1.2, an unexpected handler
|> must not throw the same exception.  This would seem to imply that
|> `throw;' would be ok in an unexpected handler only if the exception didn't
|> escape, e.g.

|> 	void my_unexpected_handler() { try { throw; } catch(...) {} }

|> and also that the example in the ARM has undefined behaviour.
|> On the other hand, 15.5.2 definitely seems to assume that an
|> unexpected handler can throw an exception not in the exception
|> specification, and carefully describes the behaviour in that case.

|> So the draft is self-contradictory here.  I think all you can really
|> say is that one of 15.5.2 and 18.6.1.2 is wrong.

I don't really think that this is a contradiction.  15.5 is speaking
about when the C++ language considers an exception finished (and thus,
*potentially* capable of being rethrown).  This is necessary even when
the exception cannot actually be rethrown; it also defines when the
destructor of the exception is called.  Thus, for example, the
constructor of an exception could save its this pointer in some global
variable; the `unexpected' function could then access the exception for
additional information.  (And yes, I am aware that the current wording
of 15.5.2 doesn't say anything like this.  But I think it is implicit in
the fact that a rethrow *must* throw the same object, not construct a
new one.  It would be nice if there were some exact wording specifying
when the destructor of the exception will be called, however.)

The statements in 18.6.1.2 specify the semantic constraints of a
particular function.  Thus, IMHO, the language per se allows rethrow at
this particular time, but the semantic constraints of the library do
not.  (This is sort of like operator new() returning NULL.  The
`language' per se certainly allows it, but the semantic constraints
specified in the library section do not.  And while I agree that the
library is part of the language, its specification defines an additional
layer or level of constraint, distinct from the rest of the language.
IMHO, of course.)

In this regard, thus, I do not think that there is a contradiction,
although perhaps a footnote in 15.5.2, pointing out that the library
does introduce additional constraints, would be in order.  (On the other
hand, if you start adding footnotes everywhere that a different section
may introduce additional constraints, you are likely to end up with more
footnotes than standard proper.  I imagine that Andy Koenig and his
helpers have enough to keep them busy anyway.)
-- 
James Kanze           (+33) 88 14 49 00          email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils, itudes et rialisations en logiciel orienti objet --
              -- A la recherche d'une activiti dans une region francophone
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Re: Exceptions in destructors
#2733
Author: Fergus Henderson
Date: Tue, 28 Nov 1995 04:10
110 lines
6066 bytes
herbs@interlog.com (Herb Sutter) writes:

>In article <494oog$hmf@gabi.gabi-soft.fr>,
> kanze@gabi-soft.fr (J. Kanze) wrote:
>>Fergus Henderson (fjh@munta.cs.mu.OZ.AU) wrote:
>>|> schuenem@informatik.tu-muenchen.de (Ulf Schuenemann) writes:
>>|> >How about saying that dtors have an empty exception-specification 
>>|> >(either by default or always) ?
>>
>>|> How does that help?
>
>I think he wants unexpected() to be called, rather than an inelegant 
>terminate(), when a dtor throws during unwinding.

Why is calling unexpected() more elegant than calling terminate()?
By default, they will both do the same thing - call abort().

>>|> Yes, and what if the programmer has defined unexpected()
>>|> to rethrow the exception?  (In most circumstances, doing so
>>|> is probably a much better idea than just crashing via
>>|> a call to abort().)
>>
>>1. How can it know what the exception was to rethrow it?  Is a throw
>>without an argument legal here?  (I don't think so, but I'm not sure.)
>	
>I was about to say that it must be legal -- this is precisely what Cline and 
>Lomow suggest in their FAQ book (FAQ #271). 

It definitely *was* legal not so long ago.

>Is disallowing a rethrow deliberate, or something that could be relaxed to 
>allow rethrowing as Cline and Lomow suggest?

The committee made a decision that exception specifications should be
fully enforced, in the sense that a function should never be allowed to
throw an exception not in its exception specification.  This was a
change from the ARM, which allowed an unexpected handler to throw any
exception, and passed such exceptions up unchanged.  But, rather than
force the program to always abort if a function throws an incorrect
exception, `bad_exception' was allowed as an escape hatch.  The
committee decided that if an unexpected handler threw an unexpected
exception, and `bad_exception' was part of the function's exception
specification, then the thrown exception would get replaced with
bad_expection.  This is reflected in the text of 15.5.2.

|   15.5.2  The unexpected() function                  [except.unexpected]
| 
| 2 The unexpected() function shall not return, but it can throw  (or  re-
|   throw) an exception.  If it throws a new exception which is allowed by
|   the exception specification which previously was  violated,  then  the
|   search  for  another handler will continue at the call of the function
|   whose exception specification was violated. If it throws or  rethrows
|   an  exception that the exception-specification does not
|   allow then the following happens: if the exception-specification  does
|   not  include  the  name of the predefined exception bad_exception then
|   the function terminate() is called, otherwise the thrown exception  is
|   replaced by an implementation-defined object of the type bad_exception
|   and the search for another handler will continue at the  call  of  the
|   function whose exception-specification was violated.
| 
| 3 Thus,  an  exception-specification  guarantees  that  only  the listed
|   exceptions will be thrown.  If  the  exception-specification  includes
|   the  name  bad_exception  then  any  exception  not on the list may be
|   replaced by bad_exception within the function unexpected().

However, as far as I can tell, there was some confusion as to where
unexpected exceptions would get replaced by bad_exception, because
the text in 18.6.2.1 [lib.unexpected.handler] seems to supplant this,
making everything but the first two sentences in the above quoted
text useless, because it implies that if an unexpected handler throws
or rethrows an exception that the exception-specification does not allow,
then the behaviour is undefined.

Ah!  I think I understand now.  18.6.2.1 says than an unexpected
handler "may" do various things, which do not include throwing an
exception that the exception-specification does not allow.  But it does
*not* say that it may not do anything else.

|   18.6.1.2  Type unexpected_handler             [lib.unexpected.handler]
| 
|   typedef void (*unexpected_handler)();
| 
| 1 The  type  of  a  handler function to be called by unexpected() when a
|   function attempts to throw an exception not listed in  its  exception-
|   specification.
|   Required behavior:
|     an  unexpected_handler  shall either throw an exception or terminate
|     execution of the program without returning to the caller.  An  unex-
|     pected_handler may perform any of the following:
|
|   --throw an exception that satisfies the exception specification;
| 
|   --throw a bad_exception exception;
| 
|   --call terminate();
| 
|   --call either abort() or exit();

Note the difference between "shall" and "may" here.
I think the second sentence is effectively non-normative.

--
Fergus Henderson             	WWW: http://www.cs.mu.oz.au/~fjh
fjh@cs.mu.oz.au              	PGP: finger fjh@128.250.37.3
I will have little or no net access from Nov 30 until Dec 25.
Please email me a copy of any follow-ups.
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Re: Exceptions in destructors
#2752
Author: Fergus Henderson
Date: Tue, 28 Nov 1995 17:26
50 lines
2747 bytes

kanze@gabi-soft.fr (J. Kanze) writes:

>Herb Sutter (herbs@interlog.com) wrote:
>|> In article <494oog$hmf@gabi.gabi-soft.fr>,
>|>    kanze@gabi-soft.fr (J. Kanze) wrote:
>|> >2. This behavior doesn't seem conform to the required behavior in
>|> >18.6.1.2. 

Although I originally agreed with your interpretation, I now disagree.
The behaviour (throwing an exception that does not satisfy the
exception specification) does not violate any requirement.

>|> Is disallowing a rethrow deliberate, or something that could be relaxed to 
>|> allow rethrowing as Cline and Lomow suggest?

I think it is just confusing prose.  I don't think the draft disallows it.
The use of "may" with a list of options does not imply that the list
is exhaustive.

>I would imagine that it must be deliberate.  The whole purpose of
>exception specifications is to offer some sort of a run-time guarantee
>as to which exceptions may propagate out. 

Right.  And as 15.5.2/3 says, this guarantee holds.  But the reason is
*not* because an unexpected handler isn't allowed to throw an
unexpected expection -- it is. 

>Unexpected is called because
>the program is attempting to violate that guarantee; if unexpected
>re-throws the same exception, either it causes unexpected to be called
>again (resulting in an endless loop), or the guarantee is violated (and
>thus not a guarantee).

No, neither of those cases holds.  The reason that the guarnatee holds
is that if an unexpected handler does throw an unexpected exception,
the unexpected() function which called the handler will replace that
exception with a `bad_exception' exception.

--
Fergus Henderson             	WWW: http://www.cs.mu.oz.au/~fjh
fjh@cs.mu.oz.au              	PGP: finger fjh@128.250.37.3
I will have little or no net access from Nov 30 until Dec 25.
Please email me a copy of any follow-ups.

---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Re: Exceptions in destructors
#2753
Author: Fergus Henderson
Date: Tue, 28 Nov 1995 17:28
75 lines
4310 bytes

kanze@gabi-soft.fr (J. Kanze) writes:

>Fergus Henderson (fjh@munta.cs.mu.OZ.AU) wrote:
>|> The ARM contains an example of the use of `throw;' in an unexpected
>|> handler (pages 365-366).
>
>Great.  So what does it mean.  Suppose I write a function:
>
>	void f() throw() ...
>
>What I'm trying to guarantee is that this function does not throw any
>exceptions; this is a very important guarantee if exception safety is to
>be taken seriously.  In fact, of course, I've only guaranteed that it
>won't throw any exceptions other than bad_exception (which is already a
>serious compromise).

The guarantee didn't hold in ARM C++, but it *does* hold in DWP C++.
If you haven't explicitly listed bad_exception in the exception specification,
then an unexpected exception will result in a call to terminate() if
the unexpected handler rethrows the exception.

>(IMHO, at least in the case of throw(), even bad_exception shouldn't
>propagate out. Basically, allowing bad_exception to be an exception
>means that even functions like the above can throw exceptions, which in
>turn makes exception safety significantly harder; you must be prepared
>to accept an exception at any time, since there is *no* way to ensure
>that a given function never throws an exception.)

The committee agreed with you - bad_exception can't propagate out
unless explicitly listed.  However, I still don't really agree with
your point about it making exception safety easier.
That depends on how you define exception safety.  If you define
exception safety to mean "no resource leaks in the presence of exceptions",
then yes, you are right.  But this simplistic version is not a good definition.
A better definition would be "no resource leaks or calls to abort()
in the presence of exceptions" - after all, a call to abort() is worse
than a resource leak, right?  If you adopt this definition, then
exception safety is very difficult.  A more realistic definition 
of "reasonable exception safety" (rather than "perfect exception safety")
might be "no calls to abort(), and no resource leaks except in the
case of a bad_exception".  This is safer than the simplistic
version, but still doesn't require you to worry about resource
leaks in the difficult cases.  In other words, using
`foo() throw(bad_exception)' rather than `foo() throw()'
can result in better safety without any additional difficulty.

That's the theory, anyway.  I've heard it said that many people
with real experience in using exception handling in large projects
don't agree, but I have not heard *why* they don't agree.

>|> So the draft is self-contradictory here.  I think all you can really
>|> say is that one of 15.5.2 and 18.6.1.2 is wrong.

I've changed my interpretation since I wrote that.  I now think that
we all just misread 18.6.1.2, taking it to imply more than it really said.

>I don't really think that this is a contradiction.  15.5 is speaking
>about when the C++ language considers an exception finished (and thus,
>*potentially* capable of being rethrown). 

Um, I'm confused.  Are you reading a different 15.5 to me?
My 15.5 [special functions] is about unexpected() and terminate().

--
Fergus Henderson             	WWW: http://www.cs.mu.oz.au/~fjh
fjh@cs.mu.oz.au              	PGP: finger fjh@128.250.37.3
I will have little or no net access from Nov 30 until Dec 25.
Please email me a copy of any follow-ups.

---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Re: Exceptions in destructors
#2768
Author: Herb Sutter
Date: Tue, 28 Nov 1995 18:20
28 lines
1764 bytes
In article <9533212.28401@mulga.cs.mu.OZ.AU>,
   fjh@munta.cs.mu.OZ.AU (Fergus Henderson) wrote:
>>
>>if unexpected re-throws the same exception [...]
>
>No, neither of those cases holds.  The reason that the guarnatee holds
>is that if an unexpected handler does throw an unexpected exception,
>the unexpected() function which called the handler will replace that
>exception with a `bad_exception' exception.

IOW, in an unexpected handler, "throw;" is the same as "throw bad_exception;"? 
 If true, this answers my concerns about Cline's/Lomow's recommendation.  
Where is this documented?

Thanks,

Herb


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Herb Sutter                 2228 Urwin, Ste 102         voice (416) 618-0184
Connected Object Solutions  Oakville ON Canada L6L 2T2    fax (905) 847-6019

---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads