Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Will we ever be able to throw from a destructor?

957 views
Skip to first unread message

DeMarcus

unread,
Jun 4, 2012, 8:46:10 AM6/4/12
to
Hi,

There is always a big discussion about exceptions and when and where they can be thrown.

In Exceptional C++ by Herb Sutter, p.55, he states why throwing from destructors is a bad thing. Apparently for a lot of people in the community (including myself) it has been difficult to be convinced that the bad thing with throwing from a destructor is more than just an unsolved technical detail.

The other day I came up with an argument why we may never be able to throw from a destructor. My "proof" is as follows.

1. Our prerequisite is that we either allow throwing from destructors, or we do not. This is to conform to the programming model of least surprise.

2. We also assume that not every method will be able to provide the strong exception-safety guarantee (by Abrahams).

3. Above two points would lead to the fact that a non-trivial (read throwing) destructor will suffer from a higher probability of failing if an exception is currently thrown from any of the class's methods.

4. All points above would prevent allocation of objects in the same scope as they are used (see examples below).

Example A:

try
{
MyClass a;

a.doSomething();
}
catch( std::exception& e )
{
// If it's likely that the destructor will have problems
// after a thrown exception from doSomething(), then we will
// have double exceptions here. Even if we could chain them,
// we would have a hard time to handle the root cause.
}


Example B:

try
{
MyClass b;

try
{
b.print();
}
catch( std::exception& e )
{
// Amend the problem and rethrow.
throw;
}
}
catch( std::exception& e )
{
// If we assume that the inner try/catch amends all problems,
// the destructor could be allowed to throw but as we see we
// now lose the possibility to have ctor/dtor in the same scope
// as the method use. Therefore, the further we want to be able
// to throw the methods' exceptions, the further we must move
// ctor/dtor scope which renders exceptions useless in most
// cases.
}


Please give your comments and say whether you think my "proof" holds or not.

Regards,
Daniel


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Dave Abrahams

unread,
Jun 4, 2012, 4:13:36 PM6/4/12
to
on Mon Jun 04 2012, DeMarcus <use_my_alias_here-AT-hotmail.com> wrote:

> Hi, There is always a big discussion about exceptions and when and
> where they can be thrown.
>
> In Exceptional C++ by Herb Sutter, p.55, he states why throwing from
> destructors is a bad thing. Apparently for a lot of people in the
> community (including myself) it has been difficult to be convinced
> that the bad thing with throwing from a destructor is more than just
> an unsolved technical detail.
>
> The other day I came up with an argument why we may never be able to
> throw from a destructor. My "proof" is as follows.
>
> 1. Our prerequisite is that we either allow throwing from
> destructors, or we do not.

I agree, but isn't this tautological?

> This is to conform to the programming model of least surprise.
>
> 2. We also assume that not every method will be able to provide the
> strong exception-safety guarantee (by Abrahams).

check. s/method/function/

> 3. Above two points would lead to the fact that a non-trivial (read
> throwing) destructor will suffer from a higher probability of failing

What does "failing" mean here?

> if an exception is currently thrown from any of the class's methods.

s/of the class' methods/function/, right?

What does "currently" mean here, and what does it add to the sentence?

>
> 4. All points above would prevent allocation of objects in the same
> scope as they are used (see examples below).
>
> Example A:
>
> try
> {
> MyClass a;
>
> a.doSomething();
> }
> catch( std::exception& e )
> {
> // If it's likely that the destructor will have problems
> // after a thrown exception from doSomething(), then we will
> // have double exceptions here. Even if we could chain them,

We can.

> // we would have a hard time to handle the root cause. }
>
> Example B:
>
> try
> {
> MyClass b;
>
> try
> {
> b.print();
> }
> catch( std::exception& e )
> {
> // Amend the problem and rethrow.
> throw;
> }
> }
> catch( std::exception& e )
> {
> // If we assume that the inner try/catch amends all problems,
> // the destructor could be allowed to throw but as we see we
> // now lose the possibility to have ctor/dtor in the same scope
> // as the method use. Therefore, the further we want to be able
> // to throw the methods' exceptions, the further we must move
> // ctor/dtor scope which renders exceptions useless in most
> // cases.
> }

I don't understand anything after the "but" in that last comment.

> Please give your comments and say whether you think my "proof" holds
> or not.

HTH,

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

Francis Glassborow

unread,
Jun 4, 2012, 9:48:40 PM6/4/12
to
On 04/06/2012 13:46, DeMarcus wrote:
> Hi,
>
> There is always a big discussion about exceptions and when and where
> they can be thrown.
>
> In Exceptional C++ by Herb Sutter, p.55, he states why throwing from
> destructors is a bad thing. Apparently for a lot of people in the
> community (including myself) it has been difficult to be convinced that
> the bad thing with throwing from a destructor is more than just an
> unsolved technical detail.
>

>From my perspective it is more than that. Once the body of a dtor has
been entered the object's lifetime has ended. Now what happens when you
throw from a dtor? The object is dead but has not been properly buried.
How is the clean up supposed to happen?

I once thought throwing from a dtor was fine but as I came to think
about it I came to realise that if something happens within a dtor that
means that it is not possible to complete the clean-up then the process
needs something much more drastic than throwing an exception. Exceptions
are designed to pass problems from the place where they can be detected
to the place where they can be dealt with, but how can that apply to a
dtor, either you can handle the problem locally or the problem cannot be
handled but can only be ignored.

Francis

DeMarcus

unread,
Jun 4, 2012, 9:54:42 PM6/4/12
to
On 2012-06-04 22:13, Dave Abrahams wrote:
> on Mon Jun 04 2012, DeMarcus<use_my_alias_here-AT-hotmail.com> wrote:
>
>> Hi, There is always a big discussion about exceptions and when and
>> where they can be thrown.
>>
>> In Exceptional C++ by Herb Sutter, p.55, he states why throwing from
>> destructors is a bad thing. Apparently for a lot of people in the
>> community (including myself) it has been difficult to be convinced
>> that the bad thing with throwing from a destructor is more than just
>> an unsolved technical detail.
>>
>> The other day I came up with an argument why we may never be able to
>> throw from a destructor. My "proof" is as follows.
>>
>> 1. Our prerequisite is that we either allow throwing from
>> destructors, or we do not.
>
> I agree, but isn't this tautological?
>

Yes, I just wanted to prohibit programming "carpentry" saying that "you
are not allowed to throw from destructors unless... <fancy explanation>".

>> This is to conform to the programming model of least surprise.
>>
>> 2. We also assume that not every method will be able to provide the
>> strong exception-safety guarantee (by Abrahams).
>
> check. s/method/function/
>
>> 3. Above two points would lead to the fact that a non-trivial (read
>> throwing) destructor will suffer from a higher probability of failing
>
> What does "failing" mean here?
>

Failing here means expected failures that would cause an exception (in
the case we allow exceptions from destructors). I make an assumption
here that if the destructor is non-trivial it needs to do operations
that demands the object be consistent.

If a method with only basic guarantee throws, leaving the object in an
undetermined state, I assume that a non-trivial destructor likely will
have problems working with this state.


>> if an exception is currently thrown from any of the class's methods.
>
> s/of the class' methods/function/, right?
>
> What does "currently" mean here, and what does it add to the sentence?
>

The typical example would be a File class that uses caching before
writing to disk. If the method write() throws DiskFullException (current
exception), the destructor would also fail writing what's left in the cache.


>>
>> 4. All points above would prevent allocation of objects in the same
>> scope as they are used (see examples below).
>>
>> Example A:
>>
>> try
>> {
>> MyClass a;
>>
>> a.doSomething();
>> }
>> catch( std::exception& e )
>> {
>> // If it's likely that the destructor will have problems
>> // after a thrown exception from doSomething(), then we will
>> // have double exceptions here. Even if we could chain them,
>
> We can.
>
>> // we would have a hard time to handle the root cause. }

I wasn't very clear here. What I meant was not just chaining but also
bulking all exceptions from all destructors ran before we reach the
first catch().

>>
>> Example B:
>>
>> try
>> {
>> MyClass b;
>>
>> try
>> {
>> b.print();
>> }
>> catch( std::exception& e )
>> {
>> // Amend the problem and rethrow.
>> throw;
>> }
>> }
>> catch( std::exception& e )
>> {
>> // If we assume that the inner try/catch amends all problems,
>> // the destructor could be allowed to throw but as we see we
>> // now lose the possibility to have ctor/dtor in the same scope
>> // as the method use. Therefore, the further we want to be able
>> // to throw the methods' exceptions, the further we must move
>> // ctor/dtor scope which renders exceptions useless in most
>> // cases.
>> }
>
> I don't understand anything after the "but" in that last comment.
>

With the inner try/catch I was trying to give the print() some kind of
strong guarantee (throw->undetermined state->catch->amend->state ok)
which would allow the destructor to avoid failing due to a previous
failure in the class.

What I meant with moving the ctor/dtor scope was that with the reasoning
above we need to provide the amending try/catch before the dtor is run.
This would prohibit classes that uses the scope, like ScopedLock.

>> Please give your comments and say whether you think my "proof" holds
>> or not.

I guess a better "proof" would be:
We can't throw from destructors unless we are able to bulk up all
exceptions thrown from all the destructors until the first catch. Even
if that was possible we would need to choose between

1. Just running each destructor until it throws and then proceed with
the next destructor.
2. Running the whole destructor even if it throws (bulking it).

In 1. we would likely have resource leaks. In 2. we would likely have
undetermined behavior.


>
> HTH,
>

Thanks for your comments!

Regards,
Daniel


--

Zeljko Vrba

unread,
Jun 5, 2012, 5:40:22 AM6/5/12
to
On 2012-06-05, Francis Glassborow <francis.g...@btinternet.com> wrote:
>
> about it I came to realise that if something happens within a dtor that
> means that it is not possible to complete the clean-up then the process
> needs something much more drastic than throwing an exception. Exceptions
>
I apologize for the wording, but this doesn't make sense. If you throw from
the destructor then either

1) stack unwinding is not in progress, so you can catch the exception and
handle the error as usual (isn't the whole point of exceptions that you
can ignore WHERE it came from? -- what makes an exception thrown from a
dtor different from an exception thrown from a regular method?)

- or -

2) terminate() gets called -- isn't this drastic enough? (Remember that you
can install terminate handler!)

Note that 1) also requires that your program is prepared to handle cleanup
errors *along* with other errors. What is so special about cleanup errors,
apart from everybody assuming that they "can't happen"? But if they "can't
happen", a hypothetical throw() in a dtor will never be executed and all is
well. If the "impossible" does happen, and your program doesn't handle it
already, goto 2) above.

My view is that exceptions are a way of reporting inability to fulfill
invariants, and I'm rather convinced that this is the only useful way of
talking about exceptions as it also seems to be the only way of avoiding
long discussions about what qualifies as exceptional condition.

You can choose to report broken promises with some variant of return codes
or with exceptions.

The real question is: should destructors be used to enofrce invariants? If
yes, how do you report inability to fulfill invariants? If not, what about
RAII -- in this case the applicability of RAII suddenly gets *very* narrow.

Of course, there's also the 3rd possibility, which is the current "common
wisdom": use RAII whenever convenient, ignore the possibility of breaking
invariants in dtor and just continue running if invariant for some reason
couldn't be fulfilled.

>
> are designed to pass problems from the place where they can be detected
> to the place where they can be dealt with, but how can that apply to a
> dtor, either you can handle the problem locally or the problem cannot be
> handled but can only be ignored.
>
I feel that your perspective is skewed. Destructor gives a certain promise to
the *user* of the class, NOT to the class itself. It is true that you cannot
access the object instance once you have caught an exception thrown from a
dtor, but this is true for any other method too: once a method has thrown an
exception, you cannot retrieve the method's local state (or the throwing
object) that lead to the exception being thrown.

For example:

try { X x; int c; x.a(c); x.b(); } catch(badly_failed &e) { ... }

Regardless of whether a() or b() or ~X() fails, the handler has no access to
the state in the try-block.

Your objection was that the exception handler doesn't have the access to the
object whose destructor threw, so you can't attempt another cleanup. But note
that you can use the exception object to transport some state to the handler.
So if you wrap a file descriptor in a RAII-style object, you could code
something like this:

try { FD f("some_file"); ... }
catch(FD_destruction_error &e) { ... do something with e.fd and e.errno }

FD doesn't know how to handle failed close(), but the handler presumably knows
what that FD represented physically and can choose some strategy of dealing
with failed close (e.g., propmpting the user to clean his disk or choose
another location to save their work, etc.)

[C++ community seems to like talking a lot about things that "can't happen".]

Martin B.

unread,
Jun 5, 2012, 9:26:49 AM6/5/12
to
I think this view is too narrow.

The main problem is that in C++ dtors serve double duty.
* They are used for (resource) cleanup
* They are used as generic at-scope-exit callbacks
* And sometimes, they are used for *both* duties at once.

Personally, I haven't seen a convincing example of where pure resource
cleanup can fail meaningfully.

On the pother hand, for me the standard example of a mixed case are
DB-connections and file classes. Their destructors conveniently close
the file/the connection, but closing such a resource often involves
either a "flush" or a "commit", both operations that may well fail,
fail legitimately, and the failure of which itself may well be best be
reported directly by an exception.

The flush/commit has *nothing* to do with resource cleanup, but it is
an integral part of "closing" such an object, and since you cannot
keep the object "open" when it's destructed, there's really nothing to
do but do a "close", including the "flush" in the dtor.

Personally I think it may well be possible and also good theory to
separate these concerns and require "files" to always be "closed"
explicitly prior to destruction. It just becomes impractical and
additionally, people would then go and use a scope-guard like
mechanism where the close/flush would be done explicitly in a
destructor of the scope-guard object.

I think that most people would agree that given how exceptions and
stack unwinding work together in C++, there is little that can be
meaningfully done in the face of double-exceptions, but somehow I feel
the cases where operations in a destructor can fail are legitimate and
somehow there doesn't seem to be much help from the language or in the
form of common wisdom on what to do with the legitimate cases.

std::uncaught_exception is certainly of only little help (also see
http://www.gotw.ca/gotw/047.htm).

cheers,
Martin


--
I'm here to learn, you know.
Just waiting for someone,
to jump from the shadows,
telling me why I'm wrong.

Thomas Richter

unread,
Jun 5, 2012, 9:32:43 AM6/5/12
to
{ Reformatted; please limit your lines to 70 characters -mod/we }

Am 05.06.2012 11:40, schrieb Zeljko Vrba:

> I apologize for the wording, but this doesn't make sense. If you
> throw from the destructor then either
>
> 1) stack unwinding is not in progress, so you can catch the
> exception and handle the error as usual (isn't the whole point
> of exceptions that you can ignore WHERE it came from? -- what
> makes an exception thrown from a dtor different from an
> exception thrown from a regular method?)

Can you? What happened with the object under destruction? Apparently,
it is not properly destructed, otherwise there wouldn't be an
exception, yet you have no longer a way of accessing this object and
trying a different clean-up strategy.

>
> - or -
>
> 2) terminate() gets called -- isn't this drastic enough? (Remember
> that you can install terminate handler!)

True, but that's often the last resort - can one savely continue the
program after terminate has been called? How?

> Note that 1) also requires that your program is prepared to handle
> cleanup errors *along* with other errors. What is so special about
> cleanup errors, apart from everybody assuming that they "can't
> happen"?

That handling cleanup errors (and others) requires cleanup of objects.

> But if they "can't happen", a hypothetical throw() in a dtor will
> never be executed and all is well. If the "impossible" does happen,
> and your program doesn't handle it already, goto 2) above.

This actually means, however, that you should better replace throwing
in the destructor by an assert, or instead build an alternative
cleanup strategy into the destructor that can happen the error
locally. The problem is that you cannot delegate the handling of
cleanup errors as with other errors since the object that caused the
problem is already no longer reachable.

> My view is that exceptions are a way of reporting inability to
> fulfill invariants,

Are they? There are many error reporting algorithms, amongst them is
also assert().

> and I'm rather convinced that this is the only useful way of talking
> about exceptions as it also seems to be the only way of avoiding
> long discussions about what qualifies as exceptional condition.

If you throw an exception, the point is that there is somewhere a
catch() that can deal with the situation - otherwise, you wouldn't
throw in first place. That's quite logical. However, if by language
construct, you *cannot* deal with the situation since the object to
deal with is no longer reachable, the catch is superfluous in first
place. So you cannot throw.

> You can choose to report broken promises with some variant of return
> codes or with exceptions.

Or terminate the program, or assert(), or....

> The real question is: should destructors be used to enofrce
> invariants? If yes, how do you report inability to fulfill
> invariants? If not, what about RAII -- in this case the
> applicability of RAII suddenly gets *very* narrow.

I don't quite follow.

> Of course, there's also the 3rd possibility, which is the current
> "common wisdom": use RAII whenever convenient, ignore the
> possibility of breaking invariants in dtor and just continue running
> if invariant for some reason couldn't be fulfilled.

Is this truely the case? There are several strategies for error
handling in destructors right away, but exceptions aren't. This does
not mean that destructors aren't able to guarantee invariants. Just
that exceptions are not the right mean to deal with problems when
handling with them.

Typical example: fclose() returns a failure condition. What do you do?

Well, it depends: If I write a file to the system, a possible solution
would be to remove the file in question to ensure that the user never
sees incomplete and potentially corrupt data. When reading a file, a
solution would be to ignore it. If that is not satisfying, and the
error should be handled in a different way - well, don't put fclose()
into the destructor because then you still *need* the object to
resolve the situation, so closing and destruction are two different
things.

> I feel that your perspective is skewed. Destructor gives a certain
> promise to the *user* of the class, NOT to the class itself.

All fine, but if the class is gone, there is nothing to do about it.

> Your objection was that the exception handler doesn't have the
> access to the object whose destructor threw, so you can't attempt
> another cleanup. But note that you can use the exception object to
> transport some state to the handler. So if you wrap a file
> descriptor in a RAII-style object, you could code something like
> this: try { FD f("some_file"); ... } catch(FD_destruction_error&e) {
> ... do something with e.fd and e.errno } FD doesn't know how to
> handle failed close(), but the handler presumably knows what that FD
> represented physically and can choose some strategy of dealing with
> failed close (e.g., propmpting the user to clean his disk or choose
> another location to save their work, etc.) [C++ community seems to
> like talking a lot about things that "can't happen".]

Huh? Not at all. Just that the above program is flawed, and the design
isn't right. If you need a strategy for dealing with the failure,
don't put this operation into the destructor - otherwise the object
state for recovery is gone.

Greetings,
Thomas

Wil Evers

unread,
Jun 5, 2012, 1:07:32 PM6/5/12
to
Martin B. wrote:

> Personally I think it may well be possible and also good theory to
> separate these concerns and require "files" to always be "closed"
> explicitly prior to destruction. It just becomes impractical and
> additionally, people would then go and use a scope-guard like
> mechanism where the close/flush would be done explicitly in a
> destructor of the scope-guard object.

Which nicely illustrates why it is a bad idea to *require* that the
file is always explicitly closed before the destructor is called.
To prevent trouble when unwinding the stack because of some possibly
unrelated exception, users would have no other option then to somehow
invoke file::close() from the exception handling/stack unwinding
machinery. The resulting error handling issues are the same as those
we face when the destructor just implicitly closes the file descriptor
if it is still open.

- Wil


--

Zeljko Vrba

unread,
Jun 5, 2012, 1:26:07 PM6/5/12
to
{ Reformatted; please limit your lines to 70 characters, and
don't quote empty lines at the beginning or end of a paragraph
-mod/we }

On 2012-06-05, Thomas Richter <th...@math.tu-berlin.de> wrote:

> Can you? What happened with the object under destruction?
> Apparently, it is not properly destructed, otherwise there wouldn't
> be an exception, yet you have no longer a way of accessing this
> object and trying a different clean-up strategy.

You can wrap the relevant (not-yet-destructed) part of the object into
the exception object and try to deal with it in catch. For example,
if the object wraps a file, you would transport its file-descriptor
through the exception to the catch site.

> True, but that's often the last resort - can one savely continue the
> program after terminate has been called? How?

No, but I think that double-exception is a grave enough scenario that
continuing is not an option. x86 CPU shuts down on a triple fault for
example, so continuing isn't always an option for a CPU either.

[Side-note: the CPU shutdown condition is also signaled to external
pins and "caught" by the chipset which in turn (on desktop machines at
least) asserts the RESET pin and resets the CPU. terminate() could
try to save the relevant state and reexec the program itself in order
to "continue". This is platform-dependent, but doable. Maybe
something to learn from HW design.]

> That handling cleanup errors (and others) requires cleanup of
> objects.

I'm not sure I get it. If in `delete x;` the destructor throws, is
the memory freed? This is basically the only piece of work that the
program can't do itself.

> This actually means, however, that you should better replace
> throwing in the destructor by an assert, or instead build an
> alternative

assert() should be understood as no-op, so it's not adequate
replacement.

> cleanup strategy into the destructor that can happen the error
> locally.

In general this means that the 1) object needs to know where it was
called from, or 2) I need many subclasses which differ only in their
destructors.

> The problem is that you cannot delegate the handling of cleanup
> errors as with other errors since the object that caused the problem
> is already no longer reachable.

You can copy the relevant part of the object and pass it as the part
of the exception.

> Are they? There are many error reporting algorithms, amongst them is
> also assert().

Why do you believe that crashing on assert() [assuming it's not a
noop] is better than std::terminate being called? Neither allows you
to continue running "cleanly". (There are hacks to continue execution
-- longjmp -- but.. I don't want to go there.)

> If you throw an exception, the point is that there is somewhere a
> catch() that can deal with the situation - otherwise, you wouldn't
> throw in first place. That's quite logical. However, if by language

That may be the point, but it's not the *whole* point. For example,
some STL algorithms throw (e.g., std::string methods throw on invalid
indices), but I never write corresponding catch. End result: if my
program passes invalid index (i.e., it is buggy), it crashes, just as
it would after assert.

STL is thus an example of using exceptions as "always on" substitute
for assert().

> construct, you *cannot* deal with the situation since the object to
> deal with is no longer reachable, the catch is superfluous in first

The exception object can transport the relevant state of the deceased
object. I'm repeating myself from the last post, but it seems that
this point hasn't come across since you're repeating this.

>> The real question is: should destructors be used to enofrce
>> invariants? If yes, how do you report inability to fulfill
>> invariants? If not, what about RAII -- in this case the
>> applicability of RAII suddenly gets *very* narrow.

> I don't quite follow.

As noted in another post, RAII is often used as generic scope-guard; a
callback to be executed on scope exit. If we limit us to dtors being
used ONLY for operations that "cannot fail", this 'other' use of the
RAII idiom is invalid.

> Is this truely the case? There are several strategies for error
> handling in destructors right away, but exceptions aren't. This does
> not mean that destructors aren't able to guarantee invariants. Just
> that exceptions are not the right mean to deal with problems when
> handling with them.

Of course, but a generic class can't know how to handle the error,
which leads us to the solution of subclassing just for the sake of
defining destructor behavior in the case of failure.

> Well, it depends: If I write a file to the system, a possible
> solution would be to remove the file in question to ensure that the
> user never sees incomplete and potentially corrupt data. When
> reading a file, a

Filesystem may get corrupted so that even remove doesn't succeed.

> error should be handled in a different way - well, don't put
> fclose() into the destructor because then you still *need* the
> object to resolve the situation, so closing and destruction are two
> different things.

Agreed, BUT: many textbooks gloss over this issue: they emphasize that
it's better to use fstream instead of FILE* also because you can't
forget to close the file -- it happens automatically. So the widely-
marketed RAII style of coding

void f() {
fstream f;
// closed at scope exit
}

becomes

void f() {
fstream f;
try {
// some code which may throw
} catch(...) {
f.close(); // may throw; "forget" the other exception
// maybe wrap in try-catch and std::terminate?
throw; // in case close succeeds
}
f.close(); // duplicated; c++ doesn't have finally{}
// may throw; we don't care
}

> All fine, but if the class is gone, there is nothing to do about it.

Yes, you can transport relevant data, needed for cleanup, through the
exception.

> Huh? Not at all. Just that the above program is flawed, and the
> design isn't right. If you need a strategy for dealing with the
> failure, don't put this operation into the destructor - otherwise
> the object state for recovery is gone. Then we're back to something
> looking to me worse than the error-code style, see the example
> above.

Then we're back to something looking to me worse than the error-code
style, see the example above.

Dave Abrahams

unread,
Jun 5, 2012, 1:30:35 PM6/5/12
to
on Mon Jun 04 2012, Francis Glassborow
<francis.glassborow-AT-btinternet.com> wrote:

> From my perspective it is more than that. Once the body of a dtor
> has been entered the object's lifetime has ended. Now what happens
> when you throw from a dtor? The object is dead but has not been
> properly buried.

Personally, I find that kind of metaphor unhelpful. What has burial
got to do with anything?

> How is the clean up supposed to happen?

It all depends what "the clean up" is, and whether there truly is any
left to be done for the object whose destructor threw. The lifetime
of subobjects hasn't ended, and they will be destroyed as usual. So
if the last thing I do in my destructor body throws an exception, what
does that mean? The answer depends on how that operation was
specified and why I'm calling it. It might be benign with respect to
program state (say, if I was trying to close a temporary file).

If something throws elsewhere in my destructor body, it means,
locally, what it means for any other function: the rest of that
destructor's body will be skipped unless there's an enclosing try
block. This, too, is a manageable/understandable scenario.

> I once thought throwing from a dtor was fine but as I came to think
> about it I came to realise that if something happens within a dtor
> that means that it is not possible to complete the clean-up then the
> process needs something much more drastic than throwing an
> exception. Exceptions are designed to pass problems from the place
> where they can be detected to the place where they can be dealt
> with, but how can that apply to a dtor, either you can handle the
> problem locally or the problem cannot be handled but can only be
> ignored.

So in your view, there is a substantial difference between:

struct C { C(){throw 1;} };
void f()
{
C x;
...
}

and

struct D { ~D(){throw 1;} };
void f()
{
{D x;}
...
}

in terms of their meaningfulness or handle-ability? I'd be interested
in your explanation, if so. It seems to me that as the user of f(), I
don't care whether the exception came from C's constructor or D's
destructor.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Michael Kilburn

unread,
Jun 5, 2012, 3:51:21 PM6/5/12
to
Hi,

On Jun 4, 7:46 am, DeMarcus <use_my_alias_h...@hotmail.com> wrote:
> In Exceptional C++ by Herb Sutter, p.55, he states why throwing from
destructors is a bad thing.
>
> The other day I came up with an argument why we may never be able to
throw from a destructor.
[skip]
> Please give your comments and say whether you think my "proof" holds
or not.

Your proof is based on assumption that there is only one destructor.
Unfortunately someone decided long ago that automatic destruction of
local objects should be implemented by same function regardless how
destruction happened (via leaving scope normally or via stack
unwinding)... This led to unfortunate side-effect, namely -- you can't
tell if your object is destroyed due to exception. This becomes
painful when you code DB connection/transaction classes -- you
normally want lifetime of these objects to mirror lifetime of their
counterparts on the server and having ability to detect destruction-
due-to-exception is very handy here.

Anyway, idea is to have smth like this:

struct Foo
{
~Foo(); // called when we leave the scope normally (and
can throw)
~~Foo() throw(); // called by stack unwinding
};

If ~~Foo() is not defined -- it points (aliases) to ~Foo(), which
becomes nothrow. This is relatively simple mechanism, would be really
nice to have it in C++.

Regards,
Michael.

Wil Evers

unread,
Jun 5, 2012, 8:22:14 PM6/5/12
to
Dave Abrahams wrote:

> So in your view, there is a substantial difference between:
>
> struct C { C(){throw 1;} };
> void f()
> {
> C x;
> ...
> }
>
> and
>
> struct D { ~D(){throw 1;} };
> void f()
> {
> {D x;}
> ...
> }
>
> in terms of their meaningfulness or handle-ability? I'd be interested
> in your explanation, if so. It seems to me that as the user of f(), I
> don't care whether the exception came from C's constructor or D's
> destructor.

In C++11, in the first example, an exception will escape from f(), while
in the second example, the terminate handler gets called. To me, that
sounds like a pretty substantial difference.

- Wil


--

Thomas Richter

unread,
Jun 5, 2012, 8:28:38 PM6/5/12
to
Am 05.06.2012 19:26, schrieb Zeljko Vrba:

>> Can you? What happened with the object under destruction?
>> Apparently, it is not properly destructed, otherwise there wouldn't
>> be an exception, yet you have no longer a way of accessing this
>> object and trying a different clean-up strategy.
>
> You can wrap the relevant (not-yet-destructed) part of the object into
> the exception object and try to deal with it in catch. For example,
> if the object wraps a file, you would transport its file-descriptor
> through the exception to the catch site.

And where take you this object from? After all, its construction might
also trigger an exception, let it be due to memory exhaustion.

>> That handling cleanup errors (and others) requires cleanup of
>> objects.
>
> I'm not sure I get it. If in `delete x;` the destructor throws, is
> the memory freed? This is basically the only piece of work that the
> program can't do itself.

I don't remember what the standard has to say about that, but at least
the stack is unwound, so objects on the stack are most certainly gone.
Objects on the heap might or might not go, but what would you do with a
pointer to such an object? There are no longer any class invariants you
could depend on since the object is already destroyed by the very means
of the C++ standard.

>> This actually means, however, that you should better replace
>> throwing in the destructor by an assert, or instead build an
>> alternative
>
> assert() should be understood as no-op, so it's not adequate
> replacement.

Huh? assert() is certainly not a no-op. It is a mechanism to check for
broken class invariants due to bugs in the code.

>> cleanup strategy into the destructor that can happen the error
>> locally.
>
> In general this means that the 1) object needs to know where it was
> called from, or 2) I need many subclasses which differ only in their
> destructors.

If you need the context to handle the error, then a throwing destructor
is neither the solution since there is nothing you could do with the
object in first place. It is no longer in a valid state, so the caller
cannot do anything about it. If you need caller assistance on an error,
you need to provide a second method for cleaning up the object state,
something like "close()". Then you can ensure, by a proper object
design, that the state of the object is still such that you can do
anything about it. However, after the destructor is called, the object
is gone.

For the problem you have a throwing destructor is not the solution.

>> The problem is that you cannot delegate the handling of cleanup
>> errors as with other errors since the object that caused the problem
>> is already no longer reachable.
>
> You can copy the relevant part of the object and pass it as the part
> of the exception.

Where to?

>> Are they? There are many error reporting algorithms, amongst them is
>> also assert().
>
> Why do you believe that crashing on assert() [assuming it's not a
> noop] is better than std::terminate being called?

Whether assert() is "better" than "terminate" depends on the situation.
Assert is better for reporting errors in the program logic, i.e. a
program should run into an assertion whenever the code detects a state
that should logically not exist, but does due to some defect in the
code. It expresses this situation correctly.

terminate(), on the other hand, just terminates. Assert() could use
operating system means to report the error to the user and get some
debug output. For example a core dump. That is usually more helpful *in
such situations* that just terminating the code.

Terminate might be better in other situations. It depends. One can
barely say "one is always better than the other".

>> If you throw an exception, the point is that there is somewhere a
>> catch() that can deal with the situation - otherwise, you wouldn't
>> throw in first place. That's quite logical. However, if by language
>
> That may be the point, but it's not the *whole* point. For example,
> some STL algorithms throw (e.g., std::string methods throw on invalid
> indices), but I never write corresponding catch. End result: if my
> program passes invalid index (i.e., it is buggy), it crashes, just as
> it would after assert.

Wait a minute - that rather depends on its use. operator[] of the string
does *not need to* throw since it doesn't have bounds checking
guaranteed by the specs. If the index is out of bounds, the
implementation may do whatever it seems fit. Terminate, assert, nothing.
std::string::at, however, does throw. Thus, if you need bounds checking,
you can have it by using the appropriate function, but only then.

> STL is thus an example of using exceptions as "always on" substitute
> for assert().

Sorry - no, that's simply not correct. I don't know what your
implementation does, but that is surely not required by the specs for
operator[].

>> construct, you *cannot* deal with the situation since the object to
>> deal with is no longer reachable, the catch is superfluous in first
>
> The exception object can transport the relevant state of the deceased
> object. I'm repeating myself from the last post, but it seems that
> this point hasn't come across since you're repeating this.

I simply don't consider this a valid argument. Look at the design you
have: You delete the object, thus tell the code "please get rid of this
thing". Yet, what you get is a new instance of a partial object. So what
exactly does this mean? The object didn't want to die, so the attempt to
delete it wasn't justified. Wait a minute - we could have done that in a
simpler way: First try an orderly shutdown ("close()") which we can
handle *without* loosing the object, and then when we know that the
Parrot is really dead, bury it.


>>> The real question is: should destructors be used to enofrce
>>> invariants? If yes, how do you report inability to fulfill
>>> invariants? If not, what about RAII -- in this case the
>>> applicability of RAII suddenly gets *very* narrow.
>
>> I don't quite follow.
>
> As noted in another post, RAII is often used as generic scope-guard; a
> callback to be executed on scope exit. If we limit us to dtors being
> used ONLY for operations that "cannot fail", this 'other' use of the
> RAII idiom is invalid.

It means that you cannot use RAII where the object release may create an
exception, exactly. Why is that a problem? Typical example: RAII
includes locking a mutex. If I cannot *release* the mutex, something is
severely broken. In such a case, I should better abort the program
(here, assert() is probably not the right solution since it is not an
invariant at the specific location that created the problem, but memory
corruption somewhere, or anything major).

>> Is this truely the case? There are several strategies for error
>> handling in destructors right away, but exceptions aren't. This does
>> not mean that destructors aren't able to guarantee invariants. Just
>> that exceptions are not the right mean to deal with problems when
>> handling with them.
>
> Of course, but a generic class can't know how to handle the error,
> which leads us to the solution of subclassing just for the sake of
> defining destructor behavior in the case of failure.

No, not at all. It means that you need to design your classes carefully.
A destructor is not the location for major work that could fail - except
for bugs in the program flow. And those you should report by other means.

>> Well, it depends: If I write a file to the system, a possible
>> solution would be to remove the file in question to ensure that the
>> user never sees incomplete and potentially corrupt data. When
>> reading a file, a
>
> Filesystem may get corrupted so that even remove doesn't succeed.

We're drifting off, that's not the point. Even then, throwing an
exception in such a situation wouldn't help either.

>> error should be handled in a different way - well, don't put
>> fclose() into the destructor because then you still *need* the
>> object to resolve the situation, so closing and destruction are two
>> different things.
>
> Agreed, BUT: many textbooks gloss over this issue: they emphasize that
> it's better to use fstream instead of FILE* also because you can't
> forget to close the file -- it happens automatically.

It does happen automatically, but it cannot handle an error by a
catch(). Let's discuss this case a bit, ok? What can you do if the close
fails?

a) If the file is not important, either leave it alone or remove it.

b) If it is feasible to inform the user about the event, you could
create the error requester in the destructor - or -

c) you could create a separate close() function which does the job
whenever you need to report about the issue, and use the destructor as a
convenience operation when it is not so important.

What can do you about a failing fclose() in first place?


> So the widely-
> marketed RAII style of coding
>
> void f() {
> fstream f;
> // closed at scope exit
> }
>
> becomes
>
> void f() {
> fstream f;
> try {
> // some code which may throw
> } catch(...) {
> f.close(); // may throw; "forget" the other exception
> // maybe wrap in try-catch and std::terminate?
> throw; // in case close succeeds
> }
> f.close(); // duplicated; c++ doesn't have finally{}
> // may throw; we don't care
> }
>

Not really. First of all, this code doesn't solve the "double fault"
problem either, nor the throwing destructor. At some point, you must say
"ok, dear user, we did all we could to keep your files intact, but at
this time we need to give up". Second, the first code does not include
error handling, so it is not an argument for your point. It is a quick
solution that might be good enough - but it neither gets any better by
throwing in the destructor because you wouldn't gain anything.

Second, typically you would place this "unchecked fclose" which you have
now explicitly spelled out in the code into the destructor, exactly as
it was before, but *in addition*, would call f.close() where you *could*
deal with the problem.

So the 'we don't care about the file' solution 1)

{
fstream f;
...
}

would remain valid, but wouldn't ensure that any cleanup work is done
when the fclose fails. Might or might not be ok, depends on the situation.

If you *must* deal with errors, then

{
fstream f;

try {
... do all the file operations...
f.close(); // I'm done
} catch(...) {
// handle my errors
}
}

is exactly the right solution because you *still* have access to the
file object within the exception handler, but you do not have to spell
out the "I do not care" fclose you have above, but you need to put
somewhere in the program anyhow, no way around it.

>> Huh? Not at all. Just that the above program is flawed, and the
>> design isn't right. If you need a strategy for dealing with the
>> failure, don't put this operation into the destructor - otherwise
>> the object state for recovery is gone. Then we're back to something
>> looking to me worse than the error-code style, see the example
>> above.
>
> Then we're back to something looking to me worse than the error-code
> style, see the example above.

I afraid you still didn't get the design right. I spelled it out
explicitly how it *should* look.

Greetings,
Thomas

Dave Abrahams

unread,
Jun 6, 2012, 12:30:01 AM6/6/12
to
on Tue Jun 05 2012, Zeljko Vrba <mordor.nospam-AT-fly.srk.fer.hr> wrote:

> My view is that exceptions are a way of reporting inability to
> fulfill invariants,

s/invariants/postconditions/, please.

> and I'm rather convinced that this is the only useful way of talking
> about exceptions as it also seems to be the only way of avoiding
> long discussions about what qualifies as exceptional condition.
>
> You can choose to report broken promises with some variant of return
> codes or with exceptions.
>
> The real question is: should destructors be used to enofrce
> invariants?

Example, please. Do you have in mind an invariant like, "every file
used by the program is either closed or associated with some live
object of type File?"

It's only an invariant if it is truly invariant. If you can't
guarantee that close() will succeed then you'd better not state an
invariant like the one above, because it's a promise you can't keep.
When designing contracts, brutal honesty is a hard requirement.

> If yes, how do you report inability to fulfill invariants?

You don't. If your invariants are broken your program is broken and
can't do *anything* reliably, because the whole program has been
written with the assumption that stated invariants hold. In other
words, if you can find no choice other than to leave some stated
invariant broken, it is time to terminate the program.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Dave Abrahams

unread,
Jun 6, 2012, 12:35:17 AM6/6/12
to
on Tue Jun 05 2012, Wil Evers <bouncer-AT-dev.null> wrote:

> Dave Abrahams wrote:
>
>> So in your view, there is a substantial difference between:
>>
>> struct C { C(){throw 1;} };
>> void f()
>> {
>> C x;
>> ...
>> }
>>
>> and
>>
>> struct D { ~D(){throw 1;} };
>> void f()
>> {
>> {D x;}
>> ...
>> }
>>
>> in terms of their meaningfulness or handle-ability? I'd be
>> interested in your explanation, if so. It seems to me that as the
>> user of f(), I don't care whether the exception came from C's
>> constructor or D's destructor.
>
> In C++11, in the first example, an exception will escape from f(),
> while in the second example, the terminate handler gets called. To
> me, that sounds like a pretty substantial difference.

I think you are mistaken. Both programs have the same behavior.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Dave Abrahams

unread,
Jun 6, 2012, 12:40:01 AM6/6/12
to
on Tue Jun 05 2012, Thomas Richter <thor-AT-math.tu-berlin.de> wrote:

> { Reformatted; please limit your lines to 70 characters -mod/we }
>
> Am 05.06.2012 11:40, schrieb Zeljko Vrba:
>
>> I apologize for the wording, but this doesn't make sense. If you
>> throw from the destructor then either
>>
>> 1) stack unwinding is not in progress, so you can catch the
>> exception and handle the error as usual (isn't the whole point
>> of exceptions that you can ignore WHERE it came from? -- what
>> makes an exception thrown from a dtor different from an
>> exception thrown from a regular method?)
>
> Can you? What happened with the object under destruction?
> Apparently, it is not properly destructed, otherwise there wouldn't
> be an exception,

I think that is a leap of logic. All we know about what happened is
that the destructor emitted an exception and the object's lifetime has
ended.

> yet you have no longer a way of accessing this object and trying a
> different clean-up strategy.

That's true. But maybe you don't need a different clean-up strategy.
It's certainly possible---even easy---to design a class that is "fully
destroyed" in every important sense of the phrase even if the
destructor throws.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Gil N.

unread,
Jun 6, 2012, 7:58:16 PM6/6/12
to
On Jun 6, 12:35 am, Dave Abrahams <d...@boostpro.com> wrote:
> on Tue Jun 05 2012, Wil Evers <bouncer-AT-dev.null> wrote:
>
> > Dave Abrahams wrote:
>
> >> So in your view, there is a substantial difference between:
>
> >> struct C { C(){throw 1;} };
> >> void f()
> >> {
> >> C x;
> >> ...
> >> }
>
> >> and
>
> >> struct D { ~D(){throw 1;} };
> >> void f()
> >> {
> >> {D x;}
> >> ...
> >> }
>
> >> in terms of their meaningfulness or handle-ability? I'd be
> >> interested in your explanation, if so. It seems to me that as the
> >> user of f(), I don't care whether the exception came from C's
> >> constructor or D's destructor.
>
> > In C++11, in the first example, an exception will escape from f(),
> > while in the second example, the terminate handler gets called. To
> > me, that sounds like a pretty substantial difference.
>
> I think you are mistaken. Both programs have the same behavior.
>

yet,

C x,y;

//and

{ D x, y; }

have different behavior.

this whole thread looks like 1999 redux, we actually need solutions
not hold on to so called coding styles or argue at different levels
of understanding.

it has nothing to do with RAII or stack unwinding we simply need to
at least offer a better way to get more context on the fail path for
those that _must_ throw on destruction w/o terminating the application
(is exception caught, is exception active, am I unwinding etc).

gil


--

Zeljko Vrba

unread,
Jun 6, 2012, 8:00:49 PM6/6/12
to
On 2012-06-06, Dave Abrahams <da...@boostpro.com> wrote:
>>
>> The real question is: should destructors be used to enofrce
>> invariants?
>
> Example, please. Do you have in mind an invariant like, "every file
> used by the program is either closed or associated with some live
> object of type File?"
>
Yes.

>
> It's only an invariant if it is truly invariant. If you can't
> guarantee that close() will succeed then you'd better not state an
> invariant like the one above, because it's a promise you can't keep.
> When designing contracts, brutal honesty is a hard requirement.
>
OK, I think I get it.

>> If yes, how do you report inability to fulfill invariants?
>
> You don't. If your invariants are broken your program is broken and
> can't do *anything* reliably, because the whole program has been
>
Let's say I implement a class implementing high-precision floating
point arithmetic and also a square-root function Sqrt which promises
to return a properly rounded result of the same precision as the
input. Now I call Sqrt(-1): what should happen? The function can't
keep its promise since result isn't representable, so should I
1) throw an exception, or 2) terminate the program?


--

Zeljko Vrba

unread,
Jun 6, 2012, 8:01:24 PM6/6/12
to
On 2012-06-06, Thomas Richter <th...@math.tu-berlin.de> wrote:
>>
>> You can wrap the relevant (not-yet-destructed) part of the object
>> into the exception object and try to deal with it in catch.
>
> And where take you this object from? After all, its construction
> might also trigger an exception, let it be due to memory exhaustion.
>
We're drifting off: you can ask the same question for each throw
statement in your program.

> least the stack is unwound, so objects on the stack are most
> certainly gone. Objects on the heap might or might not go, but what

If the objects on the heap *do* get deallocated, then everything is
well: the relevant data is copied into the exception object.

>> You can copy the relevant part of the object and pass it as the
>> part of the exception.
>
> Where to?

To the catch() site.


>> STL is thus an example of using exceptions as "always on" substitute
>> for assert().
> Sorry - no, that's simply not correct. I don't know what your
> implementation does, but that is surely not required by the specs for
> operator[].

I'm not talking about operator[] -- I'm talking about many other
methods, for example, basic_string::insert overloads in gcc 4.1.2
throw std::out_of_range when given invalid indices.

> object. So what exactly does this mean? The object didn't want to
> die, so the attempt to delete it wasn't justified. Wait a minute -

The object *discovered* it didn't want to die. Agreed, this is
better handled by ordinary methods.

> What can do you about a failing fclose() in first place?

Depends. Writing data to another location might be an option,
for example.

> If you *must* deal with errors, then
>
> {
> fstream f;
>
> try {
> ... do all the file operations...
> f.close(); // I'm done
> } catch(...) {
> // handle my errors
> }
> }
>

This solution won't work if some of the "file operations" throws.
(e.g., all file-operations succeed, but you presumably do something
with that data and this "something" throws.) Where do you put the
close() that won't get executed and that might *still* throw?

DeMarcus

unread,
Jun 6, 2012, 8:02:30 PM6/6/12
to
Could you simulate that with std::uncaught_exception? Like so:

Foo::DtorFooUnwind() noexcept
{
// Do what you need to do when the stack is unwinding.
}

Foo::~Foo()
{
if( std::uncaught_exception() )
{
DtorFooUnwind();
return;
}

// Else, commit to DB or other close-down functionality.
}

See http://en.cppreference.com/w/cpp/error/uncaught_exception

Wouldn't this still cause problems for functionality expecting
destructors not to throw? Or can we with this solution rethink the way
we use RAII today?


Regards,
Daniel

Wil Evers

unread,
Jun 7, 2012, 1:28:43 AM6/7/12
to
Dave Abrahams wrote:

> on Tue Jun 05 2012, Wil Evers <bouncer-AT-dev.null> wrote:
>
>> Dave Abrahams wrote:
>>
>>> So in your view, there is a substantial difference between:
>>>
>>> struct C { C(){throw 1;} };
>>> void f()
>>> {
>>> C x;
>>> ...
>>> }
>>>
>>> and
>>>
>>> struct D { ~D(){throw 1;} };
>>> void f()
>>> {
>>> {D x;}
>>> ...
>>> }
>>>
>>> in terms of their meaningfulness or handle-ability? I'd be
>>> interested in your explanation, if so. It seems to me that as the
>>> user of f(), I don't care whether the exception came from C's
>>> constructor or D's destructor.
>>
>> In C++11, in the first example, an exception will escape from f(),
>> while in the second example, the terminate handler gets called. To
>> me, that sounds like a pretty substantial difference.
>
> I think you are mistaken. Both programs have the same behavior.

As far as I know, in C++11, destructors are noexcept by default, so D is
equivalent to

struct D { ~D() noexcept { throw 1; } };

The 'throw 1;' breaks the promise not to throw; hence the call to the
terminate handler.

Regards,

- Wil


--

Edward Rosten

unread,
Jun 7, 2012, 1:36:59 PM6/7/12
to
On Jun 6, 2:28 am, Thomas Richter <t...@math.tu-berlin.de> wrote:

> What can do you about a failing fclose() in first place?

Exactly the same as a failed write()?

If write() fails, a classic thing to do would be to throw an exception
with errno in it, and maybe the filename if you're lucky enough to
have that. A GUI might very well pop up a dialog box informing the
user that the file has filed to write because the network connection
timed out or the disk was full and then invite them to choose a
different location.

In the case of files, fclose() can fail in exactly the same way as
write, since either one of them may cause data to be pushed to the
operating system.

If the dtor can throw without problems, then the code is very simple
and easy to write. You write code with a bunch of operator<< calls and
pick up any write error exceptions in the user interface layer if
applicable.

Note also that the object can be safely removed after fclose is
called, since the file handle is no longer valid.


Without making it possible to have exceptions in the dtor, one has to
write repetitive code on every file usage to manually call close
before destructing the file in order to get proper error reporting,
which isn't something that needs to be done with other file related
calls.

To me, if you have to write repetitive code, then the language is
missing some expressiveness.

-Ed

Dave Abrahams

unread,
Jun 7, 2012, 1:38:01 PM6/7/12
to
on Wed Jun 06 2012, Zeljko Vrba <mordor.nospam-AT-fly.srk.fer.hr> wrote:

>>> If yes, how do you report inability to fulfill invariants?
>>
>> You don't. If your invariants are broken your program is broken and
>> can't do *anything* reliably, because the whole program has been
>>
> Let's say I implement a class implementing high-precision floating
> point arithmetic and also a square-root function Sqrt which promises
> to return a properly rounded result of the same precision as the
> input. Now I call Sqrt(-1): what should happen? The function can't
> keep its promise since result isn't representable, so should I
> 1) throw an exception, or 2) terminate the program?

The promise is flawed/incomplete. Start by fixing the promise. Either
make the promise conditional, (i.e. make non-negativity a precondition),
in which case termination would be one appropriate response, or make it
more complete (i.e. document that the function throws if the argument is
negative), in which case... throw.

HTH,

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Dave Abrahams

unread,
Jun 7, 2012, 1:39:42 PM6/7/12
to
Darn, I keep forgetting that amendment was made
(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3204.htm)
after noexcept was proposed. We had thought it was politically
infeasible to get that feature when we proposed noexcept, but then some
real-world usage showed that *not* having the feature was a usability
nightmare. Thanks for the correction.

OK, so, modify D's dtor so that it's noexcept(true) and my point still
stands.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Michael Kilburn

unread,
Jun 7, 2012, 2:56:55 PM6/7/12
to
On Jun 6, 7:02 pm, DeMarcus <use_my_alias_h...@hotmail.com> wrote:
> On 06/05/2012 09:51 PM, Michael Kilburn wrote:
[skip]
> > Anyway, idea is to have smth like this:
>
> > struct Foo
> > {
> > ~Foo(); // called when we leave the scope normally (and
> > can throw)
> > ~~Foo() throw(); // called by stack unwinding
> > };
>
> > If ~~Foo() is not defined -- it points (aliases) to ~Foo(), which
> > becomes nothrow. This is relatively simple mechanism, would be really
> > nice to have it in C++.
>
> Could you simulate that with std::uncaught_exception? Like so:
[skip]

No, you can not. It is a common complaint about
std::uncaught_exception -- I'll leave it to you to work/google out
why.

Michael.

Wil Evers

unread,
Jun 7, 2012, 5:09:21 PM6/7/12
to
Dave Abrahams wrote:

> on Thu Jun 07 2012, Wil Evers <bouncer-AT-dev.null> wrote:
>> Dave Abrahams wrote:
>>> on Tue Jun 05 2012, Wil Evers <bouncer-AT-dev.null> wrote:
>>>> Dave Abrahams wrote:
[snip]
>>>>> struct D { ~D(){throw 1;} };
[snip]

>> As far as I know, in C++11, destructors are noexcept by default, so
>> D is equivalent to
>>
>> struct D { ~D() noexcept { throw 1; } };
>>
>> The 'throw 1;' breaks the promise not to throw; hence the call to
>> the terminate handler.
>
> Darn, I keep forgetting that amendment was made
> (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3204.htm)
> after noexcept was proposed. We had thought it was politically
> infeasible to get that feature when we proposed noexcept, but then
> some real-world usage showed that *not* having the feature was a
> usability nightmare. Thanks for the correction.

And thank you for being so honest about this. I didn't bring this up
to prove a point; it's just that this is a breaking change from C++03
the community should be aware of.

> OK, so, modify D's dtor so that it's noexcept(true) and my point
> still stands.

Assuming you mean "noexcept(false)": absolutely.

I think everyone agrees that it would be nice if could throw from
destructors just as we can from anywhere else. However, so far,
nobody has come up with a convincing idea about what should happen
when that destructor is invoked, either directly or indirectly, from
the stack unwinding machinery. Which is why I suspect the problem is
more fundamental, and related to the semantics we associate with
destructors.

Regards,

- Wil


--

DeMarcus

unread,
Jun 7, 2012, 5:09:56 PM6/7/12
to
> Let's say I implement a class implementing high-precision floating
> point arithmetic and also a square-root function Sqrt which promises
> to return a properly rounded result of the same precision as the
> input. Now I call Sqrt(-1): what should happen? The function can't
> keep its promise since result isn't representable, so should I
> 1) throw an exception, or 2) terminate the program?
>

I reason like this.

1) This is Defensive Programming* that temporarily may help your project
continue but will be troublesome in the long run when things become
unpredictable and forces you to handle conditions in run-time that
actually are logic errors that should be fixed in compile-time.

2) I would argue that this is the correct way of doing it and rather
check the argument before I provide it to Sqrt(). In essence; don't push
your problem in front of you to other functionality.

Just as a comparison I would guess that most programmers use
std::vector::operator[] that does not throw instead of vector::at() that
throws. People make sure we don't go out of bounds and if we do we want
to know about it quickly and fix it in compile-time.

Regards,
Daniel


*) http://en.wikipedia.org/wiki/Defensive_programming

Michael Kilburn

unread,
Jun 8, 2012, 2:52:44 AM6/8/12
to
On Jun 7, 4:09 pm, Wil Evers <boun...@dev.null> wrote:
> I think everyone agrees that it would be nice if could throw from
> destructors just as we can from anywhere else. However, so far,
> nobody has come up with a convincing idea about what should happen
> when that destructor is invoked, either directly or indirectly, from
> the stack unwinding machinery.

Do you want to consider idea I proposed in parallel branch of this
topic? If you do not find it convincing -- why?

Regards, Michael.

Dave Abrahams

unread,
Jun 8, 2012, 3:05:00 PM6/8/12
to
on Thu Jun 07 2012, Wil Evers <bouncer-AT-dev.null> wrote:

> Dave Abrahams wrote:
>
>> OK, so, modify D's dtor so that it's noexcept(true) and my point
>> still stands.
>
> Assuming you mean "noexcept(false)": absolutely.

Duh. Right.

> I think everyone agrees that it would be nice if could throw from
> destructors

You can

> just as we can from anywhere else.

Aye, there's the rub. The consequences, as in

D x,y;

are a bit more drastic.

> However, so far, nobody has come up with a convincing idea about what
> should happen when that destructor is invoked, either directly or
> indirectly, from the stack unwinding machinery. Which is why I
> suspect the problem is more fundamental, and related to the semantics
> we associate with destructors.

Meh. I think it's pretty obvious that you can continue with the
exception that's currently unwinding. That's the one aborting the
current operation. Trust the programmer: a class that is throwing from
its destructor is presumably written to be "properly destroyed"
(whatever that means) once the lifetime of the whole object ends, even
if the destructor throws. IMO it would be fine if the second exception
had the current semantics for destructors that throw
not-during-unwinding, if we discarded that second exception (or chained
it, a la std::nested_exception, to the one that's unwinding) once the
lifetime of the complete object whose dtor threw had ended.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Martin B.

unread,
Jun 8, 2012, 3:14:08 PM6/8/12
to
On 07.06.2012 20:56, Michael Kilburn wrote:
> On Jun 6, 7:02 pm, DeMarcus<use_my_alias_h...@hotmail.com> wrote:
>> On 06/05/2012 09:51 PM, Michael Kilburn wrote:
> [skip]
>>> Anyway, idea is to have smth like this:
>>
>>> struct Foo
>>> {
>>> ~Foo(); // called when we leave the scope
normally (and
>>> can throw)
>>> ~~Foo() throw(); // called by stack unwinding
>>> };
>>
>>> If ~~Foo() is not defined -- it points (aliases) to ~Foo(), which
>>> becomes nothrow. This is relatively simple mechanism, would be really
>>> nice to have it in C++.
>>
>> Could you simulate that with std::uncaught_exception? Like so:
> [skip]
>
> No, you can not. It is a common complaint about
> std::uncaught_exception -- I'll leave it to you to work/google out
> why.
>

Actually I thought you can (to a certain degree).

The point and problem -- as I understood -- with uncaught_exception is
that you *cannot tell* whether it's safe to throw anyway, even if we're
in the middle of stack unwinding.

I'll post the example of Herb Sutter from GOTW 47:
+ + + +
// Why the wrong solution is wrong
//
U::~U() {
try {
T t;
// do work
} catch( ... ) {
// clean up
}
}

If a U object is destroyed due to stack unwinding during to exception
propagation, T::~T will fail to use the "code that could throw" path
even though it safely could.
+ + + +

cheers,
Martin

--
Good C++ code is better than good C code, but
bad C++ can be much, much worse than bad C code.

Dave Harris

unread,
Jun 8, 2012, 9:24:26 PM6/8/12
to
edward...@gmail.com (Edward Rosten) wrote (abridged):
> Without making it possible to have exceptions in the dtor, one has
> to write repetitive code on every file usage to manually call close
> before destructing the file in order to get proper error reporting,
> which isn't something that needs to be done with other file related
> calls.
>
> To me, if you have to write repetitive code, then the language is
> missing some expressiveness.

It may just be that you need some other language feature. For
example, we could design some class File to support:

void process( const char *filename ) {
File::with( filename, "w", []( File &file ) {
// ... write to the open file.
} );
}

In normal flow, File::with() opens the file, passes it to the
lambda, then flushes it and closes it. If the lambda throws, the
file is merely closed and deleted. The first error is preserved and eventually
rethrown; subsequent ones are ignored.

Or whatever. The point is that the implementation can do whatever
you think is best, and use whatever combination of destructors and
try/catch, and be as messy as it needs to be, and it doesn't matter
because it's all encapsulated nicely by File.

Lambda expressions are a great tool for separation of concerns. I
keep them close to the top of my toolbox.

-- Dave Harris, Nottingham, UK.

Michael Kilburn

unread,
Jun 8, 2012, 9:24:45 PM6/8/12
to
On Jun 8, 2:14 pm, "Martin B." <0xCDCDC...@gmx.at> wrote:
> The point and problem -- as I understood -- with uncaught_exception is
> that you *cannot tell* whether it's safe to throw anyway, even if we're
> in the middle of stack unwinding.

Point is that std::uncaught_exception can not tell us if given
destructor is called by stack unwinding (can't throw) or due to
leaving scope (can throw). All it tells you is that there is at least
one 'stack unwinding session' in effect right now (there can be
multiple ones).

Regards,
Michael.


--

Thomas Richter

unread,
Jun 9, 2012, 10:04:47 AM6/9/12
to
On 07.06.2012 02:01, Zeljko Vrba wrote:

>> least the stack is unwound, so objects on the stack are most
>> certainly gone. Objects on the heap might or might not go, but
>> what
>
> If the objects on the heap *do* get deallocated, then everything is
> well: the relevant data is copied into the exception object.

g++ at least in the version I tried (4.4.5) does not release objects
on the heap when an exception is thrown in the destructor. The code
simply doesn't go to the place where operator delete is called. In
other words, you have a resource leak.

>>> STL is thus an example of using exceptions as "always on"
>>> substitute for assert().
>> Sorry - no, that's simply not correct. I don't know what your
>> implementation does, but that is surely not required by the specs
>> for operator[].
>
> I'm not talking about operator[] -- I'm talking about many other
> methods, for example, basic_string::insert overloads in gcc 4.1.2
> throw std::out_of_range when given invalid indices.

That's one of the many problems I have with the STL: Its
inconsistency. It would be ok to have a design which checks
everything and throws exceptions to report violations of the
contracts. It would have been ok to depend on asserts to do the checks
if turned on at compile time, which would be closer to the C spirit,
but the current "you never know which exceptions you may get" is quite
unacceptable. operator[] does not (need to) throw - at does. Makes
sense. insert does. Why don't we have an insert(nothrow), or use the C
model of runtime checks.

So, for example, why does a division by zero *not* cause an exception?

But we're drifting off.

>> object. So what exactly does this mean? The object didn't want to
>> die, so the attempt to delete it wasn't justified. Wait a minute -
>
> The object *discovered* it didn't want to die. Agreed, this is
> better handled by ordinary methods.
>
>> What can do you about a failing fclose() in first place?
>
> Depends. Writing data to another location might be an option, for
> example.

Sure, but then this requires more knowledge than in the file object
itself in first place. So which part of the object would you like to
place in the exception? Make a complete copy of the object? Doesn't
sound feasible to me - then we should rather keep the original and
check *before* closing down. Copy of a FILE * or a file descriptor?
Leaks implementation details.

>
>> If you *must* deal with errors, then
>>
>> {
>> fstream f;
>>
>> try {
>> ... do all the file operations...
>> f.close(); // I'm done
>> } catch(...) {
>> // handle my errors
>> }
>> }
>>
>
> This solution won't work if some of the "file operations" throws.

Why not?

> (e.g., all file-operations succeed, but you presumably do something
> with that data and this "something" throws.) Where do you put the
> close() that won't get executed and that might *still* throw?

Exactly in the place where it is right now. In the destructor. Or look
at your code proposal: You *also* ignore errors in fclose() when
recovering from an exception somewhere else. What else can we do? If a
file write fails, should we also try to catch an error from a
follow-up fclose()? I don't think we could.

IOW, I don't see why fclose() should then be any different from any
other file operation to recover from. Make it explicit, so you can
explicitly recover from it and you still have the object left you
wanted to destroy in first place. If destruction can fail, the object
that doesn't want to get destroyed must logically remain existent, and
there is no way to realize this with destructors in C++ - it is the
wrong mental model to have failing destructors.


Greetings,
Thomas

Wil Evers

unread,
Jun 9, 2012, 2:11:05 PM6/9/12
to
Michael Kilburn wrote:

> On Jun 7, 4:09 pm, Wil Evers <boun...@dev.null> wrote:

>> I think everyone agrees that it would be nice if could throw from
>> destructors just as we can from anywhere else. However, so far,
>> nobody has come up with a convincing idea about what should happen
>> when that destructor is invoked, either directly or indirectly,
>> from the stack unwinding machinery.
>
> Do you want to consider idea I proposed in parallel branch of this
> topic? If you do not find it convincing -- why?

I did see your posting. Quoting from it:

> Anyway, idea is to have smth like this:
>
> struct Foo
> {
> ~Foo(); // called when we leave the scope normally (and
> can throw)
> ~~Foo() throw(); // called by stack unwinding
> };
>
> If ~~Foo() is not defined -- it points (aliases) to ~Foo(), which
> becomes nothrow. This is relatively simple mechanism, would be really
> nice to have it in C++.

First, some nit-picking. As stated before, in C++11, destructors
default to noexcept(true), so unless special action is taken, throwing
from a destructor will terminate the program. Thus, it seems to me
that *if* some kind of overloaded destructor were added to the
language, it should be the variant that is allowed to throw:

struct Foo {

~Foo(); // called from stack unwinding machinery
// defaults to noexcept(true) as in C++11

~~Foo(); // called when leaving scope normally
// defaults to noexcept(false)
};

Following your suggestion, in the absence of ~~Foo(), ~Foo() would be
called on a non-exceptional scope exit. This ensures existing classes
will work as they do in C++11. Obviously, we would need a language
rule stating when X::~~X() is implictly defined, if ever, and what
that would mean.

A more important issue is that the destructor body for some class X is
not only called when an X goes out of scope directly, but also from
a few other contexts. Off the top of my head, these are the ones I
can think of:

(1) implicit calls from the destructor(s) of classes that have an X as
a sub-object (a base class or a data member), of from one of the
constructors of such a class when that constructor throws.

(2) calls from a delete-expression on a pointer to X

(3) explicit destructor calls (p->~X(); p->X::~X())

For each of these cases (and probably some others I didn't think of),
a language rule stating which destructor overload is selected would be
required. For (1), I can see the compiler deciding that. The
generated logic would be quite complex: as soon the destructor body of
one of the sub-objects throws, the remaining sub-objects must be
destroyed using the non-throwing variant.

It seems to me that for (2) and (3), the programmer should be able to
select the desired overload. For (2), that would require some
additional syntax; that syntax seems obvious for (3) (p->~~X();
p->X::~~X()). Selecting the right overload would depend on the
context, and complicate the code to write, especially when X
is (or depends on) a template argument.

In short, I think the impact of adding a destructor overload to the
language should not be underestimated. It might well be comparable,
both in terms of complicating the language even further, and in terms
of implementation effort, to the introduction of move semantics in
C++11.

To justify that, the benefits must be sufficiently clear. In this
case, I don't think they are, especially because we would still be
expected to deal with the case where the destructor should not throw,
and because workarounds exist.

Regards,

- Wil

Wil Evers

unread,
Jun 9, 2012, 2:38:22 PM6/9/12
to
Dave Abrahams wrote:

> on Thu Jun 07 2012, Wil Evers <bouncer-AT-dev.null> wrote:

>> However, so far, nobody has come up with a convincing idea about
>> what should happen when that destructor is invoked, either directly
>> or indirectly, from the stack unwinding machinery. Which is why I
>> suspect the problem is more fundamental, and related to the
>> semantics we associate with destructors.
>
> Meh. I think it's pretty obvious that you can continue with the
> exception that's currently unwinding. That's the one aborting the
> current operation. Trust the programmer: a class that is throwing
> from its destructor is presumably written to be "properly destroyed"
> (whatever that means) once the lifetime of the whole object ends,
> even if the destructor throws. IMO it would be fine if the second
> exception had the current semantics for destructors that throw
> not-during-unwinding, if we discarded that second exception (or
> chained it, a la std::nested_exception, to the one that's unwinding)
> once the lifetime of the complete object whose dtor threw had ended.

That is a indeed a convincing idea, although having to say something
like "yes, you did throw an exception, and no, it was not reported in
the usual way; it was discarded/repackaged because of some other,
possibly unrelated failure" does seem awkward. But then, some of the
alternatives are at least as awkward.

The only thing that sounds less awkward to me is simply accepting that
destructors are not supposed to report errors.

- Wil


--

Zeljko Vrba

unread,
Jun 9, 2012, 2:38:43 PM6/9/12
to
On 2012-06-09, Thomas Richter <th...@math.tu-berlin.de> wrote:

> g++ at least in the version I tried (4.4.5) does not release objects
> on the heap when an exception is thrown in the destructor. The code
> simply doesn't go to the place where operator delete is called. In
> other words, you have a resource leak.

OK. Thanks for checking this.

> That's one of the many problems I have with the STL: Its
> inconsistency. It would be ok to have a design which checks

I agree, inconsistency is a problem. Out of curiosity, what other
problems do you have with the STL?

> So, for example, why does a division by zero *not* cause an
> exception?

Well, it does cause a CPU-exception. Also, some CPUs (MIPS) don't
even check for division by zero; they just return an undefined result.
AFAIK, under Win32 you can convert OS-level exceptions to C++
exceptions. (Definitely not portable.)

>> Depends. Writing data to another location might be an option, for
>> example.
>
> Sure, but then this requires more knowledge than in the file object
> itself in first place. So which part of the object would you like to

Isn't this exactly the reason throwing an exception -- because you
can't handle the error locally?

> sound feasible to me - then we should rather keep the original and
> check *before* closing down. Copy of a FILE * or a file descriptor?
> Leaks implementation details.

Yes, a copy of the underlying OS-level object.

> IOW, I don't see why fclose() should then be any different from any
> other file operation to recover from. Make it explicit, so you can
> explicitly recover from it and you still have the object left you
> wanted to destroy in first place. If destruction can fail, the object
> that doesn't want to get destroyed must logically remain existent, and
> there is no way to realize this with destructors in C++ - it is the
> wrong mental model to have failing destructors.

I'm convinced. So it seems that we can't have all of 1) RAII with
(arbitrary) cleanup in destructors, 2) consistently use exceptions
for error-reporting, and 3) reliability [as in: no errors are
ignored].

Dave Abrahams

unread,
Jun 9, 2012, 6:17:56 PM6/9/12
to
....which might only seem less awkward because it is the status quo.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Stefan Große Pawig

unread,
Jun 10, 2012, 1:38:48 PM6/10/12
to
DeMarcus <use_my_a...@hotmail.com> writes:

> On 06/05/2012 09:51 PM, Michael Kilburn wrote:
>> Anyway, idea is to have smth like this:
>>
>> struct Foo
>> {
>> ~Foo(); // called when we leave the scope normally (and
>> can throw)
>> ~~Foo() throw(); // called by stack unwinding
>> };
>>
>> If ~~Foo() is not defined -- it points (aliases) to ~Foo(), which
>> becomes nothrow. This is relatively simple mechanism, would be really
>> nice to have it in C++.
>>
>
> Could you simulate that with std::uncaught_exception? Like so:
>
> Foo::DtorFooUnwind() noexcept
> {
> // Do what you need to do when the stack is unwinding.
> }
>
> Foo::~Foo()
> {
> if( std::uncaught_exception() )
> {
> DtorFooUnwind();
> return;
> }
>
> // Else, commit to DB or other close-down functionality.
> }

Your Foo also has to remember whether it had been constructed during
stack unwinding.

This has already been discussed some years ago, either here or over in
comp.lang.c++. I don't have a message reference, but I found the
following code lying around on my harddrive (from August 2007):

--- 8< ---
#include <iostream>
#include <exception>
#include <cassert>

struct Unwind
{
bool constructedDuringException;

Unwind() : constructedDuringException(std::uncaught_exception()) {}

bool stack_unwinding() const
{
return !constructedDuringException && std::uncaught_exception();
}
};

struct Lonely : private Unwind {
~Lonely()
{
std::cout << "~Lonely(): uncaught=" << std::uncaught_exception()
<< ", unwinding=" << stack_unwinding() << std::endl;
}
};

struct Inner : private Unwind {
~Inner()
{
std::cout << "~Inner(): uncaught=" << std::uncaught_exception()
<< ", unwinding=" << stack_unwinding() << std::endl;
assert( stack_unwinding() == false );
} // contrary to uncaught_exception

};

struct Outer : private Unwind {
~Outer()
{
std::cout << "~Outer(): uncaught=" << std::uncaught_exception()
<< ", unwinding=" << stack_unwinding() << std::endl;
assert( stack_unwinding() == true ); // like uncaught_exception
Inner in;
}
};

void fun()
{
Outer out;
throw std::exception();
}

int main()
{
Lonely l;
try { fun(); } catch (...) {}
}
--- 8< ---

Regards,
Stefan

Michael Kilburn

unread,
Jun 11, 2012, 2:57:31 AM6/11/12
to
On Jun 9, 1:11 pm, Wil Evers <boun...@dev.null> wrote:
> Michael Kilburn wrote:
> > On Jun 7, 4:09 pm, Wil Evers <boun...@dev.null> wrote:
> I did see your posting. Quoting from it:
> > Anyway, idea is to have smth like this:
> > struct Foo
> > {
> > ~Foo(); // called when we leave the scope normally (and
> > can throw)
> > ~~Foo() throw(); // called by stack unwinding
> > };
>
> > If ~~Foo() is not defined -- it points (aliases) to ~Foo(), which
> > becomes nothrow. This is relatively simple mechanism, would be really
> > nice to have it in C++.
>
> First, some nit-picking. As stated before, in C++11, destructors
> default to noexcept(true), so unless special action is taken, throwing
> from a destructor will terminate the program. Thus, it seems to me
> that *if* some kind of overloaded destructor were added to the
> language, it should be the variant that is allowed to throw:

This nitpicking is irrelevant -- obviously the idea is to modify
language by extending (or better to say 'unhiding') second destructor
type.


> struct Foo {
>
> ~Foo(); // called from stack unwinding machinery
> // defaults to noexcept(true) as in C++11
>
> ~~Foo(); // called when leaving scope normally
> // defaults to noexcept(false)
> };
>
> Following your suggestion, in the absence of ~~Foo(), ~Foo() would be
> called on a non-exceptional scope exit. This ensures existing classes
> will work as they do in C++11. Obviously, we would need a language
> rule stating when X::~~X() is implictly defined, if ever, and what
> that would mean.

Obviously.


> A more important issue is that the destructor body for some class X is
> not only called when an X goes out of scope directly, but also from
> a few other contexts. Off the top of my head, these are the ones I
> can think of:
>
> (1) implicit calls from the destructor(s) of classes that have an X as
> a sub-object (a base class or a data member), of from one of the
> constructors of such a class when that constructor throws.
>
> (2) calls from a delete-expression on a pointer to X
>
> (3) explicit destructor calls (p->~X(); p->X::~X())

For simplicity reasons we could allow ~~Foo() to be called only from
stack unwinding. This will eliminate all contexts but (1). Though it
is not really necessary -- I see no problems with other contexts, you
proposed reasonable and obvious solutions for them.


> required. For (1), I can see the compiler deciding that. The
> generated logic would be quite complex: as soon the destructor body of
> one of the sub-objects throws, the remaining sub-objects must be
> destroyed using the non-throwing variant.

Yes, but this logic does not seem complex to me...


> In short, I think the impact of adding a destructor overload to the
> language should not be underestimated. It might well be comparable,
> both in terms of complicating the language even further, and in terms
> of implementation effort, to the introduction of move semantics in
> C++11.

I doubt it... Next to new ultra-complicated move-and-init semantic of C
++11 this feature is a simpleton -- small, plain and well-contained.


> To justify that, the benefits must be sufficiently clear. In this
> case, I don't think they are, especially because we would still be
> expected to deal with the case where the destructor should not throw,
> and because workarounds exist.

There is no workaround -- there is no way to determine if given dtor
is called by stack unwinding or not.

Regards,
Michael.

goran...@gmail.com

unread,
Jun 11, 2012, 9:32:56 AM6/11/12
to
{ Reformatted; please limit your lines to 70 chars -mod/we }

On Tuesday, June 5, 2012 3:54:42 AM UTC+2, DeMarcus wrote:
> On 2012-06-04 22:13, Dave Abrahams wrote:
> > on Mon Jun 04 2012, DeMarcus<use_my_alias_here-AT-hotmail.com> wrote:
> >
> >> Hi, There is always a big discussion about exceptions and when
> >> and where they can be thrown.
> >>
> >> In Exceptional C++ by Herb Sutter, p.55, he states why throwing
> >> from destructors is a bad thing. Apparently for a lot of people
> >> in the community (including myself) it has been difficult to be
> >> convinced that the bad thing with throwing from a destructor is
> >> more than just an unsolved technical detail.
> >>
> >> The other day I came up with an argument why we may never be able
> >> to throw from a destructor. My "proof" is as follows.
> >>
> >> 1. Our prerequisite is that we either allow throwing from
> >> destructors, or we do not.
> >
> > I agree, but isn't this tautological?
>
> Yes, I just wanted to prohibit programming "carpentry" saying that
> "you are not allowed to throw from destructors unless... <fancy
> explanation>".
>
> >> This is to conform to the programming model of least surprise.
> >>
> >> 2. We also assume that not every method will be able to provide
> >> the strong exception-safety guarantee (by Abrahams).
> >
> > check. s/method/function/
> >
> >> 3. Above two points would lead to the fact that a non-trivial
> >> (read throwing) destructor will suffer from a higher probability
> >> of failing
> >
> > What does "failing" mean here?
> >
> Failing here means expected failures that would cause an exception
> (in the case we allow exceptions from destructors). I make an
> assumption here that if the destructor is non-trivial it needs to do
> operations that demands the object be consistent.
>
> If a method with only basic guarantee throws, leaving the object in
> an undetermined state, I assume that a non-trivial destructor likely
> will have problems working with this state.
>
> >> if an exception is currently thrown from any of the class's
> >> methods.
> >
> > s/of the class' methods/function/, right?
> >
> > What does "currently" mean here, and what does it add to the
> > sentence?
>
> The typical example would be a File class that uses caching before
> writing to disk. If the method write() throws DiskFullException
> (current exception), the destructor would also fail writing what's
> left in the cache.

Here's what I think: throwing from a destuctor should be considered a
design error. And I will use your very example to show why, and I will
also argue that the underlying conceptual problem has nothing to do
with destructors in particular.

The thing is: cleanup path must be a no-fail operation. If it is not,
code stinks. Exceptions do not matter.

A pure C example:

int foo(const char* fname, ...)
{
FILE* f = fopen(fname, ...);
if (!f) return EXIT_FAILURE;
if (!use(f), ...))
{
fclose(f); // This is "int fclose(...)"
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

fclose is declared as a can-fail operation, e.g. for the very reason
you state in your file class example.

So... How do you handle fclose failure? (Captain Obvious: current code
does squat)

It's easy to do if (!fclose(f)) return EXIT_FAILURE;, but that leaves
you with a resource leak. And if "f" is a function-local resource (as
it should be), there's nothing you can do about that leak. You can
change the meaning of the function and spill "f" out of "foo" if flose
fails, but:

1. that's uuuuuuugly
2. even if you do... you couldn't close it in foo, chances are you
can't do it in the caller either.

You could put "f" somehow "aside" and try to close it later, but
that's bad, too:
1. complicated
2. store it where? you already on a cleanup path, so that storing
better be a no-fail operation; if you do similar things regularly,
that's a tough call.
3. see "previous" 2.

IOW... exceptions or not, destructors or not, failure on a cleanup
path is a no-win situation.

So what do you do? You cheat, that's what! What does it mean? Well...

1. you make sure, to the best of your ability, to avoid failures on a
cleanup path; in the FILE example, you might want to reach for
(nonstandard) fpurge.
2. you
2.1 IGNORE cleanup failures
2.2 (or) merely report (log) them
2.2 (or) terminate on the spot (log first)

You DON'T, EVER, try to "return" failure on a cleanup path, either
through an exception, or a retval.

Corollary: exceptions in a dtor DO NOT happen ;-).

Goran.

Zeljko Vrba

unread,
Jun 11, 2012, 1:59:37 PM6/11/12
to
On 2012-06-11, goran...@gmail.com <goran...@gmail.com> wrote:

> So... How do you handle fclose failure? (Captain Obvious: current code
> does squat)

Write data to some other location. [Actually, regardless of whether
(f)close succeeds, the resources are freed. Return code tells you
that you might want to write data anew.]

> 2. store it where? you already on a cleanup path, so that storing
> better be a no-fail operation; if you do similar things regularly,
> that's a tough call.

A global error-stack, for example, and let the caller inspect and
clear the stack at convenience. That's what OpenSSL does; it migt
anyway be a more sane way of error-handling than exceptions. Use
destructors for convenience (RAII), use error-stack for reporting
errors, use exceptions to report errors that aren't meant to be
handled.


> You DON'T, EVER, try to "return" failure on a cleanup path, either
> through an exception, or a retval.

Misunderstanding "cleanup" may be the culprit of this discussion:
if you care about success/failure of some operation, it's not
cleanup and doesn't belong in the destructor.

DeMarcus

unread,
Jun 11, 2012, 8:34:26 PM6/11/12
to
> IOW... exceptions or not, destructors or not, failure on a cleanup
> path is a no-win situation.
>
> So what do you do? You cheat, that's what! What does it mean? Well...
>
> 1. you make sure, to the best of your ability, to avoid failures on a
> cleanup path; in the FILE example, you might want to reach for
> (nonstandard) fpurge.
> 2. you
> 2.1 IGNORE cleanup failures
> 2.2 (or) merely report (log) them
> 2.2 (or) terminate on the spot (log first)
>
> You DON'T, EVER, try to "return" failure on a cleanup path, either
> through an exception, or a retval.
>
> Corollary: exceptions in a dtor DO NOT happen ;-).
>

I struggle convincing myself how to deal with clean-up. I guess I'm on par with your ideas but I just can't find the clean wording for it. Right now I'm into one idea related to the contract of the functions.

Let's say we have a File class and a method called writeData(). Also assume that the File class could be implemented with our without caching the data to write to disk. A direct call to the writeData() function is synchronous and therefore the user should be able consider the data as written on function return even though it may be in a cache. So I agree with you that if we get problems syncing the cache to disk from the destructor, we have a broken contract and a design error.

If a cache must be used, a better design would be to provide an asynchronous method called sendData() or something where the user at least knows that everything may not have been written to disk unless confirmed so with some isDataQueueEmpty() call.

I don't know the internals of close() but as someone pointed out; resource release must be a no-fail operation. Until we have a no-fail close() we can't use that reliably in the destructor.


/Daniel

Wil Evers

unread,
Jun 11, 2012, 8:35:06 PM6/11/12
to
Michael Kilburn wrote:

> On Jun 9, 1:11 pm, Wil Evers <boun...@dev.null> wrote:
>
>> In short, I think the impact of adding a destructor overload to the
>> language should not be underestimated. It might well be
>> comparable, both in terms of complicating the language even
>> further, and in terms of implementation effort, to the introduction
>> of move semantics in C++11.
>
> I doubt it... Next to new ultra-complicated move-and-init semantic
> of C ++11 this feature is a simpleton -- small, plain and
> well-contained.

Well, destructors resemble copy constructors/assignment operators
because they (1) are (often) implictly called by the compiler and
(2) may be implicitly generated by the compiler. Just as the move
constructor/assignment operator adds an additional overload for the
compiler to call and/or generate, so does the destructor overload
you suggested.

It would require a set of rules specifying how the overloads are
recognized by the compiler, which of these is selected when, the
semantics that the compiler will assume apply to each of them, the
conditions under which they are implicitly generated, and the
signatures the compiler will emit when they are. That is about the
same set of questions the designers of C++11's move semantics had to
answer. Finally, once all of this is specified and added to the
standard, the community will have to be educated about it.

>> To justify that, the benefits must be sufficiently clear. In this
>> case, I don't think they are, especially because we would still be
>> expected to deal with the case where the destructor should not
>> throw, and because workarounds exist.
>
> There is no workaround -- there is no way to determine if given dtor
> is called by stack unwinding or not.

The obvious workaround is to put logic that may need to report an
exception in an explicitly called member function instead of in the
destructor. I know that's not a perfect solution, which is why I
called it a workaround. However, it is easy to understand and
implement, and not really that hard to use.

Regards,

- Wil

Michael Kilburn

unread,
Jun 12, 2012, 9:28:33 AM6/12/12
to
On Jun 11, 7:35 pm, Wil Evers <boun...@dev.null> wrote:
> Michael Kilburn wrote:
> > I doubt it... Next to new ultra-complicated move-and-init semantic
> > of C ++11 this feature is a simpleton -- small, plain and
> > well-contained.
>
> Well, destructors resemble copy constructors/assignment operators
> because they (1) are (often) implictly called by the compiler and
> (2) may be implicitly generated by the compiler. Just as the move
> constructor/assignment operator adds an additional overload for the
> compiler to call and/or generate, so does the destructor overload
> you suggested. It would require a set of rules specifying how the
> overloads are recognized by the compiler, which of these is selected
> when, the semantics that the compiler will assume apply to each of
> them, the conditions under which they are implicitly generated, and
> the signatures the compiler will emit when they are. That is about
> the same set of questions the designers of C++11's move semantics
> had to answer. Finally, once all of this is specified and added to
> the standard, the community will have to be educated about it.

But ~~Foo() is not an overload! It is a new function/destructor
accessible to stack unwinding machinery only (unless we want to
introduce syntax changes that will allow user to access it too). It's
implicit implementation is trivial (e.g. ~~Foo() nothrow
{ this->~Foo(); } ) I do not see anything complicated with
it. 'Selection rules' are trivial -- and we certainly do not need to
introduce new reference type.

> > There is no workaround -- there is no way to determine if given
> > dtor is called by stack unwinding or not.
>
> The obvious workaround is to put logic that may need to report an
> exception in an explicitly called member function instead of in the
> destructor. I know that's not a perfect solution, which is why I
> called it a workaround. However, it is easy to understand and
> implement, and not really that hard to use.

i.e. special 'close()' function that needs to be called manually
before every 'return' statement in given scope? It is like saying that
RAII is not required: we have a workaround -- call cleanup manually.
Or that move semantic is not required -- it can be simulated by
explicit move() method. In fact, I even written my own framework with
vector class that knows how to move it's elements around!

Regards,
Michael.

Wil Evers

unread,
Jun 13, 2012, 12:51:17 PM6/13/12
to
Michael Kilburn wrote:

> On Jun 11, 7:35 pm, Wil Evers <boun...@dev.null> wrote:
>> Michael Kilburn wrote:
>> > There is no workaround -- there is no way to determine if given
>> > dtor is called by stack unwinding or not.
>>
>> The obvious workaround is to put logic that may need to report an
>> exception in an explicitly called member function instead of in the
>> destructor. I know that's not a perfect solution, which is why I
>> called it a workaround. However, it is easy to understand and
>> implement, and not really that hard to use.
>
> i.e. special 'close()' function that needs to be called manually
> before every 'return' statement in given scope? It is like saying
> that RAII is not required: we have a workaround -- call cleanup
> manually.

Yes, the idea behind the usual workaround is to have a close() member
function that users who want an exception on an I/O error from the
underlying API should call before the object goes out of scope.

User code looks something like this:

void save()
{
outfile f(/* ctor args */);
// ...write contents...
f.close();
}

Since close() is never called from the stack unwinding machinery, it
can safely throw an exception.

An important point is that the destructor remains responsible for
making sure no resources are leaked: it just checks if close() has
already been called, and cleans up if it hasn't. But it never throws
an exception.

The reason this workaround isn't perfect is that the call to close()
becomes an inseparable part of the logic required to ensure the file's
contents are correct if no exception is reported. Users who forget
that may not be notified if the file is corrupted.

IMO, this isn't really a language defect; it is caused by an inherent
problem in any delayed output scheme, and simply reflects the design
decisions embodied in the underlying API.

Regards,

- Wil

Paul Floyd

unread,
Jun 14, 2012, 12:19:48 AM6/14/12
to
On Mon, 11 Jun 2012 06:32:56 -0700 (PDT), goran...@gmail.com
<goran...@gmail.com> wrote:
> int foo(const char* fname, ...)
> {
> FILE* f = fopen(fname, ...);
> if (!f) return EXIT_FAILURE;
> if (!use(f), ...))
> {
> fclose(f); // This is "int fclose(...)"
> return EXIT_FAILURE;
>
> fclose is declared as a can-fail operation, e.g. for the very reason
> you state in your file class example.
>
> So... How do you handle fclose failure? (Captain Obvious: current code
> does squat)

Hi

To me, the obvious thing to do is to read the man page, see what the
RETURN VALUES and the ERRORS are. In particular, if errno is EAGAIN or
EINTR, I would try to close the file again. Otherwise, what to do
depends on the error.

A+
Paul

Michael Kilburn

unread,
Jun 14, 2012, 12:33:36 AM6/14/12
to
Let me change this sample for you:

void save()
{
outfile f(/* ctor args */);
// ...write contents...
if (...)
if (...) {...};
else { ...; f.close(); return; }
else
if (...) { ...; f.close(); return; }
/* etc */

f.close();

// example of situations where you can't easily insert manual
shutdown

foo1( outfile(/* ctor args */) );
foo2( outfile(/* ctor args */), /* smth that may or may not throw
*/ );
}

If you call this a workaround, then there is no reason for RAII to
exist -- manual calls and explicit try/catch blocks is a good
substitution for it.

> An important point is that the destructor remains responsible for
> making sure no resources are leaked: it just checks if close() has
> already been called, and cleans up if it hasn't. But it never
> throws an exception.

Last time I had to wrap C-style interface of database access library I
really missed ability to detect that my destructor is called by stack-
unwinding: class representing DB connection did not close connection
in dtor -- instead connection was pooled. But you can't pool
connection handled by object that was destroyed due to exception --
that connection is likely to have live transaction/tmp tables/etc that
normally gets destroyed by normal execution path via RAII-based
mechanisms.

And you know what? 'Typical workaround' does not really work here:

void save()
{
connection c(/* ctor args */);

{
TemporaryTable t("XXX", c); // drops table in dtor
...

// bad, table has to be dropped before allow_pooling() call
if (...) { ...; c.allow_pooling(); return; }
...

// better, but do you really expect user to do this?
if (...) { ...; t.drop(); c.allow_pooling(); return; }

// now imagine that I had five temporary tables, one
connection
// setting and two nested transactions in this scope...
}

c.allow_pooling();
}

There is simply no way user will be doing this (not mentioning 'tmp
object' situation where you can't really insert manual shutdown call).

> The reason this workaround isn't perfect is that the call to close()
> becomes an inseparable part of the logic required to ensure the
> file's contents are correct if no exception is reported. Users who
> forget that may not be notified if the file is corrupted.

I know exactly why this workaround is not perfect. See examples above.

> IMO, this isn't really a language defect; it is caused by an
> inherent problem in any delayed output scheme, and simply reflects
> the design decisions embodied in the underlying API.

Of course it is not a language defect... neither is absence of move
semantics in C++03 or classes in C -- in both cases 'typical
workarounds' exist that allow developer to do what he wants to. But
for some reason we really want these features.

Regards,
Michael.

goran...@gmail.com

unread,
Jun 14, 2012, 12:58:03 PM6/14/12
to
On Thursday, June 14, 2012 6:19:48 AM UTC+2, Paul Floyd wrote:
> On Mon, 11 Jun 2012 06:32:56 -0700 (PDT),
> <> wrote:
> > int foo(const char* fname, ...)
> > {
> > FILE* f = fopen(fname, ...);
> > if (!f) return EXIT_FAILURE;
> > if (!use(f), ...))
> > {
> > fclose(f); // This is "int fclose(...)"
> > return EXIT_FAILURE;
> >
> > fclose is declared as a can-fail operation, e.g. for the very reason
> > you state in your file class example.
> >
> > So... How do you handle fclose failure? (Captain Obvious: current code
> > does squat)
>
> Hi
>
> To me, the obvious thing to do is to read the man page, see what the
> RETURN VALUES and the ERRORS are. In particular, if errno is EAGAIN or
> EINTR, I would try to close the file again. Otherwise, what to do
> depends on the error.

Yes, however, this is a particular solution for a general problem, and in
some other situation a solution might not exist.

And I would further argue that the solution is going to be dubious.

E.g. this manpage for fclose (http://linux.die.net/man/3/fclose) does not
mention either of the codes. But even if it did, how do you plan executing
your your idea, for EAGAIN? Spin a loop until it succeeds? That seems
dubious. E.g. what if file is on a network, and network is gone for a long
time? Do you really want to block, inside cleanup, for a pretty
indeterminate amount of time?

Goran.

Wil Evers

unread,
Jun 14, 2012, 7:44:42 PM6/14/12
to
Michael Kilburn wrote:

> On Jun 13, 11:51 am, Wil Evers <boun...@dev.null> wrote:
>
>> Yes, the idea behind the usual workaround is to have a close()
>> member function that users who want an exception on an I/O error
>> from the underlying API should call before the object goes out of
>> scope.
>>
>> User code looks something like this:
>>
>> void save()
>> {
>> outfile f(/* ctor args */);
>> // ...write contents...
>> f.close();
>> }
>>
>> Since close() is never called from the stack unwinding machinery, it
>> can safely throw an exception.
>
> Let me change this sample for you:
>
> void save()
> {
> outfile f(/* ctor args */);
> // ...write contents...
> if (...)
> if (...) {...};
> else { ...; f.close(); return; }
> else
> if (...) { ...; f.close(); return; }
> /* etc */
>
> f.close();
[snip]
> }

No, let's keep both. Here's an edited version of yours, with the
problematic calls to f.close() removed, but still the same complicated
internal structure:

void write_contents(outfile& f)
{
if (...)
if (...) {...};
else { ...; return; }
else
if (...) { ...; return; }
/* etc */
}

And here's mine, slightly edited too. It just calls yours:

void save()
{
outfile f(/* ctor args */);
write_contents(f);
f.close();
}

Do we really need a language change for this? I rest my case.

Regards,

- Wil

sunseraphic

unread,
Jun 14, 2012, 7:45:32 PM6/14/12
to
Sorry for short reply :p

In RAII/RRID, destructor plays a role of "finally block" as in the other language.

How do you think about to throw another exception in the finally block? :)

You've pointed out some actual problems.

But to throw an execption in destructor is not the way to solve them.


On Monday, June 4, 2012 8:46:10 PM UTC+8, DeMarcus wrote:
> Hi,
>
> There is always a big discussion about exceptions and when and where they can be thrown.
>
> In Exceptional C++ by Herb Sutter, p.55, he states why throwing from destructors is a bad thing. Apparently for a lot of people in the community (including myself) it has been difficult to be convinced that the bad thing with throwing from a destructor is more than just an unsolved technical detail.
>
> The other day I came up with an argument why we may never be able to throw from a destructor. My "proof" is as follows.
>
> 1. Our prerequisite is that we either allow throwing from destructors, or we do not. This is to conform to the programming model of least surprise.
>
> 2. We also assume that not every method will be able to provide the strong exception-safety guarantee (by Abrahams).
>
> 3. Above two points would lead to the fact that a non-trivial (read throwing) destructor will suffer from a higher probability of failing if an exception is currently thrown from any of the class's methods.
>
> 4. All points above would prevent allocation of objects in the same scope as they are used (see examples below).
>
> Example A:
>
> try
> {
> MyClass a;
>
> a.doSomething();
> }
> catch( std::exception& e )
> {
> // If it's likely that the destructor will have problems
> // after a thrown exception from doSomething(), then we will
> // have double exceptions here. Even if we could chain them,
> // we would have a hard time to handle the root cause.
> }
>
>
> Example B:
>
> try
> {
> MyClass b;
>
> try
> {
> b.print();
> }
> catch( std::exception& e )
> {
> // Amend the problem and rethrow.
> throw;
> }
> }
> catch( std::exception& e )
> {
> // If we assume that the inner try/catch amends all problems,
> // the destructor could be allowed to throw but as we see we
> // now lose the possibility to have ctor/dtor in the same scope
> // as the method use. Therefore, the further we want to be able
> // to throw the methods' exceptions, the further we must move
> // ctor/dtor scope which renders exceptions useless in most
> // cases.
> }
>
>
> Please give your comments and say whether you think my "proof" holds or not.
>
> Regards,
> Daniel

Zeljko Vrba

unread,
Jun 15, 2012, 2:31:15 PM6/15/12
to
On 2012-06-14, Wil Evers <bou...@dev.null> wrote:
>
> And here's mine, slightly edited too. It just calls yours:
>
> void save()
> {
> outfile f(/* ctor args */);
> write_contents(f);
> f.close();
> }
>
> Do we really need a language change for this? I rest my case.
>
Of course not, but do we need RAII at allthen ? And why is RAII
then so often cited as one of main advantages of C++ when it is
so easily emulated?

DeMarcus

unread,
Jun 15, 2012, 2:37:35 PM6/15/12
to
On 06/15/2012 01:45 AM, sunseraphic wrote:
> Sorry for short reply :p
>
> In RAII/RRID, destructor plays a role of "finally block" as in the
> other language.
>
> How do you think about to throw another exception in the finally
> block? :)

I'm not too familiar with finally blocks but I guess that to be able
to throw from them you would need some kind of exception bulking (see
below).

> You've pointed out some actual problems.
>
> But to throw an execption in destructor is not the way to solve
> them.

No, you are correct. I have always had the feeling that throwing from
a destructor is a bad idea, but I've never seen a proof that it will
be impossible even with future C++. Therefore I tried to state such
"proof".

With the input from the people here I managed to come up with an even
better "proof" (at least in regards to convince myself). It's the
following.

We can't throw from destructors unless we are able to bulk up all
exceptions thrown from all the destructors until the first catch. Even
if that was possible we would need to choose between

1. Just running each destructor until it throws and then proceed with
the next destructor.

2. Running the whole destructor even if it throws (bulking it).

In 1. we would likely have resource leaks. In 2. we would likely have
undetermined behavior.

Zeljko Vrba

unread,
Jun 15, 2012, 2:27:55 PM6/15/12
to
On 2012-06-14, goran...@gmail.com <goran...@gmail.com> wrote:
>
> time? Do you really want to block, inside cleanup, for a pretty
> indeterminate amount of time?
>
This actually happens with NFS, on the _kernel_ level when the
network disappears and the FS is mounted in "hard" mode (which
is the default, AFAIK, since most programs don't handle EAGAIN
on read or write).

Your argument basically boils down to "error handling is difficult,
so let's not do it." Whether something is "dubious" or not depend
entirely on the context, as does most error handling.

Michael Kilburn

unread,
Jun 15, 2012, 2:43:59 PM6/15/12
to
On Jun 14, 6:44 pm, Wil Evers <boun...@dev.null> wrote:
> Michael Kilburn wrote:
So, In my example (with db connection/etc) you propose to move scope
of life for every object (that needs a shutdown call) with a function?
This is definitely no going to fly when there are multiple objects
like this -- N objects means N nested functions, if function at the
bottom needs access to a variable living in topmost scope -- are you
going pass a reference to it through entire chain of function calls?

Here, try fixing this one without turning it into something that looks
like poorly written Lisp code:

void save()
{
int k = ...;
MyClass m = ...;

connection c(/* ctor args */);

{
TemporaryTable t1("XXX", c); // drops table in dtor
...
Transaction tran(c);

...
m.do_smth();

// better, but do you really expect user to do this?
if (...) { ...; tran.shutdown(); t1.shutdown(); c.shutdown();
return; }
k = ...;

TemporaryTable t2("YYY", c); // drops table in dtor
...
if (...) { ...; t2.shutdown(); tran.shutdown();
t1.shutdown(); c.shutdown(); return; }

}
c.shutdown();
}

After you've done nesting functions, think about modifying this code
-- moving code around is very simple within single scope, but when you
split this scope into bunch of functions it becomes quite
inconvenient.


> Do we really need a language change for this? I rest my case.

What this supposed to mean? That you somehow won the argument by
providing a workaround for trivial case and ignoring more complicated
ones? O_o

Your logic boils down to this -- if there is a workaround (regardless
how inconvenient it is), then there is no need for language change.
But this could be said about almost every change to C++ introduced
since times of C...


Regards,
Michael.

Zeljko Vrba

unread,
Jun 15, 2012, 2:21:09 PM6/15/12
to
On 2012-06-14, sunseraphic <sunse...@gmail.com> wrote:

> How do you think about to throw another exception in the finally
block? :)

This is supported in Java. What happens, apparently (concluded from
some quick net searches instead of reading the spec), is that the
first exception disappears and exception from finally is propagated.
(The question also arises: what happens if return is executed in the
middle of the finally block?)

Incidentally, this very same search turns up advice about 1) not
throwing in finally and 2) not letting exceptions propagate from
finally. But it is not absolutely discouraged; it is legitimate
to throw from finally in certain cases.

Francis Glassborow

unread,
Jun 16, 2012, 2:23:09 AM6/16/12
to
On 15/06/2012 19:21, Zeljko Vrba wrote:
> On 2012-06-14, sunseraphic<sunse...@gmail.com> wrote:
>
>> How do you think about to throw another exception in the finally
> block? :)
>
> This is supported in Java. What happens, apparently (concluded from
> some quick net searches instead of reading the spec), is that the
> first exception disappears and exception from finally is propagated.
> (The question also arises: what happens if return is executed in the
> middle of the finally block?)
>
> Incidentally, this very same search turns up advice about 1) not
> throwing in finally and 2) not letting exceptions propagate from
> finally. But it is not absolutely discouraged; it is legitimate
> to throw from finally in certain cases.
>
>
And as far as C++ is concerned it is legitimate to throw from a dtor,
nonetheless it is strongly discouraged because it is usually a faulty
design.

One of the main the problems is how to provide commit or roll-back
semantics in C++. If only we had a way to reliably identify when we are
in the process of stack unwinding as a result of an exception, this
could often be done within a dtor, throwing an exception in the
roll-back case.

Francis

Zeljko Vrba

unread,
Jun 16, 2012, 6:42:01 AM6/16/12
to
On 2012-06-16, Francis Glassborow <francis.g...@btinternet.com> wrote:

> On 15/06/2012 19:21, Zeljko Vrba wrote:
> And as far as C++ is concerned it is legitimate to throw from a dtor,
> nonetheless it is strongly discouraged because it is usually a faulty
> design.

This is becoming OT, but for the sake of completeness: Java runtime
will not forcibly terminate the program if a finally block throws
during stack unwinding. C++ runtime will call std::terminate.

Wil Evers

unread,
Jun 16, 2012, 2:04:06 PM6/16/12
to
Zeljko Vrba wrote:

> On 2012-06-14, Wil Evers <bou...@dev.null> wrote:
>>
>> And here's mine, slightly edited too. It just calls yours:
>>
>> void save()
>> {
>> outfile f(/* ctor args */);
>> write_contents(f);
>> f.close();
>> }
>>
>> Do we really need a language change for this? I rest my case.
>>
> Of course not, but do we need RAII at allthen ? And why is RAII
> then so often cited as one of main advantages of C++ when it is
> so easily emulated?

There is RAII in this example: we rely on the guarantee that f's
destructor is called, even if an exception is thrown from the call to
write_contents(f).

In that case, the stack unwinding machinery takes control, so f.close()
is *not* called, and that is deliberate. It implies that f.close()
is free to throw an exception if it detects a problem, without risking a
call to terminate().

As I tried to explain before, even if f.close() is not called, there is
no resource leak, because the f's destructor simply releases any
resources that haven't been released before. However, it will never
throw.

Regards,

- Wil

Dave Abrahams

unread,
Jun 17, 2012, 4:24:43 PM6/17/12
to
on Fri Jun 15 2012, DeMarcus <use_my_alias_here-AT-hotmail.com> wrote:

> I have always had the feeling that throwing from
> a destructor is a bad idea, but I've never seen a proof that it will
> be impossible even with future C++. Therefore I tried to state such
> "proof".
>
> With the input from the people here I managed to come up with an even
> better "proof" (at least in regards to convince myself). It's the
> following.

I don't understand the drive to convince yourself that the idea of
throwing from destructors is somehow fundamentally broken.

> We can't throw from destructors unless we are able to bulk up all
> exceptions thrown from all the destructors until the first catch.

Either that, or throw out information from secondary exceptions.

> Even if that was possible we would need to choose between
>
> 1. Just running each destructor until it throws and then proceed with
> the next destructor.
>
> 2. Running the whole destructor even if it throws (bulking it).

I don't unknw what either of these things mean. C++ already has
well-defined semantics for what happens when a dtor throws, but not during
stack unwinding. It's easy to extend these semantics for exceptions
thrown during unwinding if you're willing to chain up multiple
exceptions or throw out secondary exception information.

> In 1. we would likely have resource leaks. In 2. we would likely have
> undetermined behavior.

These conclusions seem flimsy at best and completely unsupported at
worst. If you code to account for the semantics of exceptions thrown
from destructors, you can throw them. If you don't code to account for
those semantics, throwing from a destructor is likely to do something
undesirable. This is exactly the same as the way the use of any other
feature (e.g. placement new) works if you account for its semantics, and
will get you in trouble if you code as though it hadn't happened.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Dave Harris

unread,
Jun 18, 2012, 4:49:03 PM6/18/12
to
crusad...@gmail.com (Michael Kilburn) wrote (abridged):
> So, In my example (with db connection/etc) you propose to move scope
> of life for every object (that needs a shutdown call) with a
> function?

It's often a good idea for each function to do one thing, rather than three
different things (open, use, close).


> This is definitely no going to fly when there are multiple objects
> like this -- N objects means N nested functions, if function at the
> bottom needs access to a variable living in topmost scope -- are you
> going pass a reference to it through entire chain of function calls?

In C++11 we can use lambda expressions (as I mentioned a while back).
Or are they what you mean by "poorly written Lisp code"?


> void save()
> {
> int k = ...;
> MyClass m = ...;
>
> connection c(/* ctor args */);
>
> {
> TemporaryTable t1("XXX", c); // drops table in dtor
> ...
> Transaction tran(c);
>
> ...
> m.do_smth();
>
> // better, but do you really expect user to do this?
> if (...) { ...; tran.shutdown(); t1.shutdown();
> c.shutdown();
> return; }
> k = ...;
>
> TemporaryTable t2("YYY", c); // drops table in dtor
> ...
> if (...) { ...; t2.shutdown(); tran.shutdown();
> t1.shutdown(); c.shutdown(); return; }
>
> }
> c.shutdown();
> }

void save() {
int k = ...;
MyClass m = ...;

connection c(/* ctor args */);
c.open( [&] {
TemporaryTable t1("XXX", c);
t1.open( [&] {
Transaction tran(c);
tran.open( [&] {
m.do_smth();
if (...)
return;
k = ...;

TemporaryTable t2("YYY", c);
t2.open( [&] {
...
if (...)
return;
} );
} );
} );
} );
}

Or somesuch. Where each "open" is like:

void Transaction::open( const std::function<void()> &fn ) {
open();
fn();
close(); // Or shutdown or whatever.
}


> After you've done nesting functions, think about modifying this code
> -- moving code around is very simple within single scope, but when
> you split this scope into bunch of functions it becomes quite
> inconvenient.

Huge, monolithic functions that have many different concerns are always
going to be difficult to maintain, especially if they have meaningless
variable names like "c" and "t1", but I don't think my version is any
worse than yours.

-- Dave Harris, Nottingham, UK.

DeMarcus

unread,
Jun 18, 2012, 11:25:45 PM6/18/12
to
On 2012-06-17 22:24, Dave Abrahams wrote:
> on Fri Jun 15 2012, DeMarcus<use_my_alias_here-AT-hotmail.com> wrote:
>
>> I have always had the feeling that throwing from
>> a destructor is a bad idea, but I've never seen a proof that it will
>> be impossible even with future C++. Therefore I tried to state such
>> "proof".
>>
>> With the input from the people here I managed to come up with an even
>> better "proof" (at least in regards to convince myself). It's the
>> following.
>
> I don't understand the drive to convince yourself that the idea of
> throwing from destructors is somehow fundamentally broken.
>

It's just my interest within programming. Right now I'm into exploring
failure handling, and I try to see what exception handling could look
like in ten years from now.

>> We can't throw from destructors unless we are able to bulk up all
>> exceptions thrown from all the destructors until the first catch.
>
> Either that, or throw out information from secondary exceptions.
>
>> Even if that was possible we would need to choose between
>>
>> 1. Just running each destructor until it throws and then proceed with
>> the next destructor.
>>
>> 2. Running the whole destructor even if it throws (bulking it).
>
> I don't unknw what either of these things mean. C++ already has
> well-defined semantics for what happens when a dtor throws, but not during
> stack unwinding. It's easy to extend these semantics for exceptions
> thrown during unwinding if you're willing to chain up multiple
> exceptions or throw out secondary exception information.
>

I apologize. I realize that I'm not clear with what I write so I'll try
to explain better.

If we allow throwing from destructors and want to be able to handle that
scenario, we must be prepared to take care of all exceptions that can be
thrown during unwinding. Right now we terminate if we throw an exception
while another one is active.

Let's look at scenario 1) above.

One way to allow throwing from destructors (that works with current C++)
is to chain exceptions to avoid termination, i.e. if you need to throw
yet another exception during stack unwinding, you catch the active
exception and chain it with the new exception and throw that.

Even though we have circumvented the termination problem with this
solution we still face the resource leak problem since the destructors
most often must release the resources the very last thing they do.

Now let's look at scenario 2) above (never mind the bulking, I'll come
to that).

You may be able (even with current C++) to queue up exceptions in a
destructor, chain them all in the end of the destructor and throw. Now
you have solved the resource release problem but most certainly the
destructor will have undetermined behavior since code after an exception
should not be executed.


Above is convincing enough for me to not throw from destructors.
Nevertheless, if one would try anyway we hit another problem.

The exception chaining will chain every exception it encounters. The
idea (as I see it) with chaining is that you should be able to follow
the exception and what caused it, look at its parent exception in the
chain and its parent to finally see the root cause. This could be good
in a logging system.

Now if you are unfortunate, a destructor during unwinding may throw an
exception that is independent of the active exception, but will still be
part of the chain, causing confusion if the whole chain is written to a log.

That is what I meant with bulking exceptions. Bulking is a pure
hypothetical non-C++ way of bulking up several exception chains to be
thrown if several unrelated problems arises during unwinding. As I write
I think it may even be feasible to have a main exception that contains a
list of exception chains, but we will still suffer from resource leaks
or undefined behavior.


>> In 1. we would likely have resource leaks. In 2. we would likely have
>> undetermined behavior.
>
> These conclusions seem flimsy at best and completely unsupported at
> worst. If you code to account for the semantics of exceptions thrown
> from destructors, you can throw them. If you don't code to account for
> those semantics, throwing from a destructor is likely to do something
> undesirable. This is exactly the same as the way the use of any other
> feature (e.g. placement new) works if you account for its semantics, and
> will get you in trouble if you code as though it hadn't happened.
>

You write "If you code to account for the semantics of exceptions thrown
from destructors, you can throw them". That is what I try to say is
close to impossible. Let's make a simple example.

SomeClass::~SomeClass()
{
int* i = new int;
doSomethingWithInt( &i );

if( i == NULL )
throw "Error";
*i = 4711;

delete i;
}

If you choose 1) from above, you will have a resource leak.
If you choose 2) from above, (assuming a future C++ would queue the
exception for you) you would crash the row after queuing the exception.


Regards,
Daniel


--

Michael Kilburn

unread,
Jun 19, 2012, 3:46:59 AM6/19/12
to
Very good... Here is how it would looks like if C++ allowed my
destructor to detect stack-unwinding:

void save() {
connection c(/* ctor args */);
TemporaryTable t1("XXX", c);
Transaction tran(c);

MyClass m = ...;
m.do_smth();
if (...) return;

int k = ...;
TemporaryTable t2("YYY", c);
...
if (...) return;
}

And note: it is easy to read, it is trivial to move stuff around or
add/remove smth. Also (unless lambdas are completely inlined), this
code will perform better.

In addition, look at this from the other angle -- why I should be
explicit about something that should happen on normal execution path?
(i.e. shutdown() call should be default behaviour, *not* calling
shutdown call in case of exception -- this is that needs to be
emphasized). Using this workaround turns normal logic inside out.


> > After you've done nesting functions, think about modifying this code
> > -- moving code around is very simple within single scope, but when
> > you split this scope into bunch of functions it becomes quite
> > inconvenient.
>
> Huge, monolithic functions that have many different concerns are always
> going to be difficult to maintain, especially if they have meaningless
> variable names like "c" and "t1", but I don't think my version is any
> worse than yours.

Look at your function and mine -- which one is bigger and more
monolithic? In fact all those nested lambdas remind me C# code :-D

I did not invent all this, I had genuine need for an ability to detect
stack unwinding. And sample code above does not belong to realm of
fantasy, it is typical in realm where framework uses C++ stack
machinery to model stuff happening on the server side -- and if there
is an exception, it means that sync between C++ logic and server-side
logic is lost and we need to follow another execution path, i.e. drop
connection, reset it, etc.

Regards,
Michael.

Dave Abrahams

unread,
Jun 22, 2012, 3:17:39 PM6/22/12
to
on Mon Jun 18 2012, DeMarcus <use_my_alias_here-AT-hotmail.com> wrote:

> On 2012-06-17 22:24, Dave Abrahams wrote:
>> on Fri Jun 15 2012, DeMarcus<use_my_alias_here-AT-hotmail.com> wrote:
>>
>>> I have always had the feeling that throwing from a destructor is a
>>> bad idea, but I've never seen a proof that it will be impossible
>>> even with future C++. Therefore I tried to state such "proof".
>>>
>>> With the input from the people here I managed to come up with an
>>> even better "proof" (at least in regards to convince myself). It's
>>> the following.
>>
>> I don't understand the drive to convince yourself that the idea of
>> throwing from destructors is somehow fundamentally broken.
>
> It's just my interest within programming. Right now I'm into
> exploring failure handling, and I try to see what exception handling
> could look like in ten years from now.

I think chances are good that it will be fine to throw from
destructors. In fact, I intend to submit a proposal to make it so.
There's no new "resource leak problem." Anytime you have an
un-managed resource you have to watch out for exceptions. Any
resources managed directly by an object become un-managed when you
enter the destructor body. Either don't throw an exception from
destructors of such objects (status quo), or (better) delegate
resource management to a sub-object.

> Now let's look at scenario 2) above (never mind the bulking, I'll
> come to that).
>
> You may be able (even with current C++) to queue up exceptions in a
> destructor, chain them all in the end of the destructor and
> throw. Now you have solved the resource release problem but most
> certainly the destructor will have undetermined behavior since code
> after an exception should not be executed.

What do you mean by "undetermined behavior?" The behavior of dtors in
the presence of exceptions is well-specified, and actually pretty
reasonable: when not already unwinding, the rest of the current dtor's
body is skipped but all of the other sub-objects are destroyed as part
of unwinding. If you don't like that behavior, well, don't throw from
your destructor. But if you want to throw from your destructor you
can easily work with these semantics to get a useful "determined"
result.

> Above is convincing enough for me to not throw from destructors.
> Nevertheless, if one would try anyway we hit another problem.

I'll bite (seeing as there's no problem so far)...

> The exception chaining will chain every exception it encounters. The
> idea (as I see it) with chaining is that you should be able to
> follow the exception and what caused it, look at its parent
> exception in the chain and its parent to finally see the root
> cause. This could be good in a logging system.

Actually, we plan to propose a handler (like the standard unexpected
handler) that can do anything it wants with the secondary exception,
including logging or chaining it. And if you do chain it, the root
cause of unwinding should stay at the head of the chain.

> Now if you are unfortunate, a destructor during unwinding may throw
> an exception that is independent of the active exception, but will
> still be part of the chain, causing confusion if the whole chain is
> written to a log.

Reading logs is not for the faint-of-heart already. I don't see how
this is going to make anything worse.

> That is what I meant with bulking exceptions. Bulking is a pure
> hypothetical non-C++ way of bulking up several exception chains to
> be thrown if several unrelated problems arises during unwinding. As
> I write I think it may even be feasible to have a main exception
> that contains a list of exception chains, but we will still suffer
> from resource leaks or undefined behavior.

No, those are imaginary problems, unless you choose to both

a) throw from your destructor, and
b) ignore the specified semantics of throwing from destructors

> You write "If you code to account for the semantics of exceptions
> thrown from destructors, you can throw them". That is what I try to
> say is close to impossible. Let's make a simple example.
>
> SomeClass::~SomeClass()
> {
> int* i = new int;
> doSomethingWithInt( &i );
>
> if( i == NULL )
> throw "Error";
> *i = 4711;
>
> delete i;
> }
>
> If you choose 1) from above, you will have a resource leak.

It's not at all clear from what you wrote above where there's a
resource leak. Since i is a raw pointer, ownership semantics are not
communicated by the interface. It's always possible that
doSomethingWithInt deletes the pointer before setting it to NULL. Try
making an example that uses unique_ptr instead. It's pretty hard to
generate a resource leak in that case.

Of course, it's still possible, but that misses the point: the fact
that you can write an example that has a resource leak doesn't prove
anything other than that you didn't "code to account for the semantics
of exceptions thrown from destructors." It's easy enough to do that,
especially if you follow standard best practices of delegating
ownership to sub-objects and locals.

> If you choose 2) from above, (assuming a future C++ would queue the
> exception for you) you would crash the row after queuing the
> exception.

row?

I don't see any rows here.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Rani Sharoni

unread,
Jun 24, 2012, 8:23:28 AM6/24/12
to
On Jun 22, 10:17 pm, Dave Abrahams <d...@boostpro.com> wrote:

> The behavior of dtors in the presence of exceptions is
> well-specified, and actually pretty reasonable: when not already
> unwinding, the rest of the current dtor's body is skipped but all of
> the other sub-objects are destroyed as part of unwinding.

Will the following memory be freed if the dtor of 'A' throws:
delete pA; // pA is of type A for which ~A might throw

I'm not sure what the standard says about such... (hence how the
implementation of given smart-ptr should 'safely' handle such).
(I know that for 'new A' there is special handling in which memory
gets freed if the ctor throws).

Thanks,
Rani


--

DeMarcus

unread,
Jun 24, 2012, 8:27:56 AM6/24/12
to
> There's no new "resource leak problem." Anytime you have an
> un-managed resource you have to watch out for exceptions. Any
> resources managed directly by an object become un-managed when you
> enter the destructor body. Either don't throw an exception from
> destructors of such objects (status quo), or (better) delegate
> resource management to a sub-object.

Ok, I start to understand. Put all resources in their own managed RAII
objects.

>> You may be able (even with current C++) to queue up exceptions in a
>> destructor, chain them all in the end of the destructor and
>> throw. Now you have solved the resource release problem but most
>> certainly the destructor will have undetermined behavior since code
>> after an exception should not be executed.
>
> What do you mean by "undetermined behavior?" The behavior of dtors
> in the presence of exceptions is well-specified, and actually pretty
> reasonable: when not already unwinding, the rest of the current
> dtor's body is skipped but all of the other sub-objects are
> destroyed as part of unwinding. If you don't like that behavior,
> well, don't throw from your destructor. But if you want to throw
> from your destructor you can easily work with these semantics to get
> a useful "determined" result.

My undetermined behavior idea will be obsolete with managed resources,
so I skip explaining that further.

>> The exception chaining will chain every exception it
>> encounters. The idea (as I see it) with chaining is that you should
>> be able to follow the exception and what caused it, look at its
>> parent exception in the chain and its parent to finally see the
>> root cause. This could be good in a logging system.
>
> Actually, we plan to propose a handler (like the standard unexpected
> handler) that can do anything it wants with the secondary exception,
> including logging or chaining it. And if you do chain it, the root
> cause of unwinding should stay at the head of the chain.

Ok good, but how do you come to the conclusion that you want the root
cause at the /head/?

Let's say you get an out-of-bounds exception, someone catches it and
chains it with an XML-error exception, throws that to someone that
catches it and chains it with a PreferenceFile-read-error exception and
throws that. What is the catcher of that chained exception going to do
with an out-of-bounds exception?

I claim that the most recent exception must be what is caught at the
next catch().

>> Now if you are unfortunate, a destructor during unwinding may throw
>> an exception that is independent of the active exception, but will
>> still be part of the chain, causing confusion if the whole chain is
>> written to a log.
>
> Reading logs is not for the faint-of-heart already. I don't see how
> this is going to make anything worse.

You have convinced me that managed resources solve resource leaks but
I'm still not convinced about the independent exceptions. Strong-heart
people may be able to read cryptic logs (not that I would prefer that)
but how are they going to be able to choose which of the independent
exceptions shall take the head in the chain?

>> That is what I meant with bulking exceptions. Bulking is a pure
>> hypothetical non-C++ way of bulking up several exception chains to
>> be thrown if several unrelated problems arises during unwinding. As
>> I write I think it may even be feasible to have a main exception
>> that contains a list of exception chains, but we will still suffer
>> from resource leaks or undefined behavior.
>
> No, those are imaginary problems, unless you choose to both
>
> a) throw from your destructor, and
> b) ignore the specified semantics of throwing from destructors

We agree on a), but in b) the specified semantics would involve
prioritizing one independent exception from another. That
prioritization is not obvious and I claim it must not be left as a
side task.


Regards,
Daniel


--

Daniel Krügler

unread,
Jun 24, 2012, 1:29:10 PM6/24/12
to
Am 24.06.2012 14:23, schrieb Rani Sharoni:
> On Jun 22, 10:17 pm, Dave Abrahams <d...@boostpro.com> wrote:
>
>> The behavior of dtors in the presence of exceptions is
>> well-specified, and actually pretty reasonable: when not already
>> unwinding, the rest of the current dtor's body is skipped but all of
>> the other sub-objects are destroyed as part of unwinding.
>
> Will the following memory be freed if the dtor of 'A' throws:
> delete pA; // pA is of type A for which ~A might throw
>
> I'm not sure what the standard says about such... (hence how the
> implementation of given smart-ptr should 'safely' handle such).
> (I know that for 'new A' there is special handling in which memory
> gets freed if the ctor throws).

This was clarified by

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#353

The deallocation function will be called regardless whether the
destructor throws.

I would like to add in regard to this discussion that the behaviour is
undefined once a deallocation function terminates via an exception, see
[basic.stc.dynamic.deallocation] p3.

HTH & Greetings from Bremen,

Daniel Krügler

Dave Abrahams

unread,
Jun 24, 2012, 1:30:15 PM6/24/12
to
on Sun Jun 24 2012, Rani Sharoni <ranisharoni75-AT-gmail.com> wrote:

> On Jun 22, 10:17 pm, Dave Abrahams <d...@boostpro.com> wrote:
>
>> The behavior of dtors in the presence of exceptions is
>> well-specified, and actually pretty reasonable: when not already
>> unwinding, the rest of the current dtor's body is skipped but all of
>> the other sub-objects are destroyed as part of unwinding.
>
> Will the following memory be freed if the dtor of 'A' throws:
> delete pA; // pA is of type A for which ~A might throw
>
> I'm not sure what the standard says about such... (hence how the
> implementation of given smart-ptr should 'safely' handle such).

I don't know for sure either, but the answer has to be "yes" (and if it
isn't, that must be fixed). The lifetime of *pA has ended, so there's
absolutely no reason to keep the memory around.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Dave Abrahams

unread,
Jun 24, 2012, 1:34:52 PM6/24/12
to
It's like this: the initial operation requested by the "user" failed
because of the root cause. If the destructor hadn't thrown anything,
that's what we'd hear about. Secondary failures during cleanup are
*typically* resource-release failures, which are *typically* fairly
benign and of less interest to the caller; they don't describe why the
requested operation failed. Ignoring them might even be a reasonable
approach. So it doesn't seem reasonable to replace the head of the
chain.

> Let's say you get an out-of-bounds exception, someone catches it and
> chains it with an XML-error exception, throws that to someone that
> catches it and chains it with a PreferenceFile-read-error exception and
> throws that. What is the catcher of that chained exception going to do
> with an out-of-bounds exception?

Exactly what he'd have done had there been no failure during cleanup:
deal with the original cause of the failure.

> I claim that the most recent exception must be what is caught at the
> next catch().

I disagree. Suppose you have a recovery action for the most recent
exception, and you take it, and retry? The original reason for failure
persists, and the operation still fails. Not good.

>>> Now if you are unfortunate, a destructor during unwinding may throw
>>> an exception that is independent of the active exception, but will
>>> still be part of the chain, causing confusion if the whole chain is
>>> written to a log.
>>
>> Reading logs is not for the faint-of-heart already. I don't see how
>> this is going to make anything worse.
>
> You have convinced me that managed resources solve resource leaks but
> I'm still not convinced about the independent exceptions. Strong-heart
> people may be able to read cryptic logs (not that I would prefer that)
> but how are they going to be able to choose which of the independent
> exceptions shall take the head in the chain?

Huh? Log readers don't make that choice. It's up to the person
writing the log whether they want to even represent that information,
and if they represent it, how they do it.

>>> That is what I meant with bulking exceptions. Bulking is a pure
>>> hypothetical non-C++ way of bulking up several exception chains to
>>> be thrown if several unrelated problems arises during unwinding. As
>>> I write I think it may even be feasible to have a main exception
>>> that contains a list of exception chains, but we will still suffer
>>> from resource leaks or undefined behavior.
>>
>> No, those are imaginary problems, unless you choose to both
>>
>> a) throw from your destructor, and
>> b) ignore the specified semantics of throwing from destructors
>
> We agree on a), but in b) the specified semantics would involve
> prioritizing one independent exception from another.

Agreeing on one part of that statement is meaningless: a and b go
together inextricably. My point is that (a) you have a choice about
whether to throw from a dtor, and (b) the semantics of throwing from
dtors is well-specified and not even particularly hard to code with. So
if you (a) choose to throw from dtors, don't (b) ignore the specified
semantics and you'll see no resource leaks or undefined behavior (and I
stipulate that avoiding unwinding/termination requires a language
change). To agree with (a) by itself would be like saying, "if you
choose throw from a destructor" but then stopping there without saying
anything else.

AFAICT, you are making a different claim, about whether it makes sense
to gather exceptions into a chain and how they should be dealt with when
they are chained. That is a completely separate issue from undefined
behavior and resource leaks. However, what claim you are actually
making is pretty vague.

> That prioritization is not obvious and I claim it must not be left as
> a side task.

What do you mean by a "side task?"

My plan is to leave it up to the author of the hook function for dealing
with nested exceptions. My claim is that "ignore secondary exceptions"
might be a reasonable default, "chain the secondary exception to the
first" might be better, "terminate" would be what you'd choose for
absolute backward compatibility, and if you are are foolish enough to
disagree with me about what should be at the head of the chain then
you're free to do it the "wrong" way ;-)

Regards,

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Wil Evers

unread,
Jun 24, 2012, 5:09:11 PM6/24/12
to
Dave Abrahams wrote:

> I think chances are good that it will be fine to throw from
> destructors. In fact, I intend to submit a proposal to make it so.

That's an interesting development, to say the least. It's also kind
of surprising: by introducing the implicit noexcept in C++11, the
committee seems to have been moving away from the idea of throwing
exceptions from destructors.

[snip]

> There's no new "resource leak problem." Anytime you have an
> un-managed resource you have to watch out for exceptions. Any
> resources managed directly by an object become un-managed when you
> enter the destructor body. Either don't throw an exception from
> destructors of such objects (status quo), or (better) delegate
> resource management to a sub-object.

[snip]

> The behavior of dtors in the presence of exceptions is
> well-specified, and actually pretty reasonable: when not already
> unwinding, the rest of the current dtor's body is skipped but all of
> the other sub-objects are destroyed as part of unwinding. If you
> don't like that behavior, well, don't throw from your destructor.
> But if you want to throw from your destructor you can easily work
> with these semantics to get a useful "determined" result.

Agreed. Another idea would be to only throw from a destructor body
after any directly owned resources have been released. In any case,
what we're doing here is making sure the destructor still keeps its
promise to release any resources, even if it reports an exception.

But that immediately raises a question: if the destructor is able to
meet that obligation, then why does it need to throw an exception? It
can only mean that the destructor is expected to do more than just
resource management.

And that, I think, is the heart of the matter: is it a good idea to
change the language to provide better support for these other uses?
Even if it is technically feasible to do so, what will we gain?

The idea that destructors are meant for automatic resource management
is the key design principle behind RAII; the idea that destructors
don't throw is a hard requirement in many parts of the standard
library, and I suspect lots of user code is based on these assumptions
too. Why make it easier to break the rules?

Regards,

- Wil


--

Dave Abrahams

unread,
Jun 24, 2012, 8:48:07 PM6/24/12
to
on Sun Jun 24 2012, Daniel Krügler <daniel.kruegler-AT-googlemail.com> wrote:

> Am 24.06.2012 14:23, schrieb Rani Sharoni:
>> On Jun 22, 10:17 pm, Dave Abrahams <d...@boostpro.com> wrote:
>>
>>> The behavior of dtors in the presence of exceptions is
>>> well-specified, and actually pretty reasonable: when not already
>>> unwinding, the rest of the current dtor's body is skipped but all of
>>> the other sub-objects are destroyed as part of unwinding.
>>
>> Will the following memory be freed if the dtor of 'A' throws:
>> delete pA; // pA is of type A for which ~A might throw
>>
>> I'm not sure what the standard says about such... (hence how the
>> implementation of given smart-ptr should 'safely' handle such).
>> (I know that for 'new A' there is special handling in which memory
>> gets freed if the ctor throws).
>
> This was clarified by
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#353
>
> The deallocation function will be called regardless whether the
> destructor throws.
>
> I would like to add in regard to this discussion that the behaviour is
> undefined once a deallocation function terminates via an exception, see
> [basic.stc.dynamic.deallocation] p3.

Yeah... so that could be loosened too, I suppose... but I think that
doesn't pose any problems for the case of delete pA where ~A throws,
since the deallocation function terminates normally and then the runtime
propagates the exception.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Dave Abrahams

unread,
Jun 24, 2012, 8:48:29 PM6/24/12
to
on Sun Jun 24 2012, Wil Evers <bouncer-AT-dev.null> wrote:

> Dave Abrahams wrote:
>
>> I think chances are good that it will be fine to throw from
>> destructors. In fact, I intend to submit a proposal to make it so.
>
> That's an interesting development, to say the least. It's also kind
> of surprising: by introducing the implicit noexcept in C++11, the
> committee seems to have been moving away from the idea of throwing
> exceptions from destructors.

The reasons for implicit noexcept actually have nothing to do with a
bias against throwing from destructors, even though I'd be willing to
bet many committee members do have that bias.

> [snip]
>
>> There's no new "resource leak problem." Anytime you have an
>> un-managed resource you have to watch out for exceptions. Any
>> resources managed directly by an object become un-managed when you
>> enter the destructor body. Either don't throw an exception from
>> destructors of such objects (status quo), or (better) delegate
>> resource management to a sub-object.
>
> [snip]
>
>> The behavior of dtors in the presence of exceptions is
>> well-specified, and actually pretty reasonable: when not already
>> unwinding, the rest of the current dtor's body is skipped but all of
>> the other sub-objects are destroyed as part of unwinding. If you
>> don't like that behavior, well, don't throw from your destructor.
>> But if you want to throw from your destructor you can easily work
>> with these semantics to get a useful "determined" result.
>
> Agreed. Another idea would be to only throw from a destructor body
> after any directly owned resources have been released.

At heart, that's the same idea: account for the semantics of throwing
from dtors :-)

> In any case, what we're doing here is making sure the destructor still
> keeps its promise to release any resources, even if it reports an
> exception.
>
> But that immediately raises a question: if the destructor is able to
> meet that obligation, then why does it need to throw an exception? It
> can only mean that the destructor is expected to do more than just
> resource management.
>
> And that, I think, is the heart of the matter: is it a good idea to
> change the language to provide better support for these other uses?
> Even if it is technically feasible to do so, what will we gain?

Two things I can think of:

1. Removal of gratuitous brittleness. In that sense, it's the same as
the deprecation of dynamic exception-specifications. Programmers
shouldn't have to worry needlessly about gotchas.

2. Legitimacy for idioms like "throwing scopeguard"

> The idea that destructors are meant for automatic resource management
> is the key design principle behind RAII;

At this point what they were "meant for" is irrelevant; they're
primarily used that way, but they also get used for other things all the
time (e.g. logging at scope exit).

> the idea that destructors don't throw is a hard requirement in many
> parts of the standard library, and I suspect lots of user code is
> based on these assumptions too. Why make it easier to break the
> rules?

The rules are already "broken." :-) It's time they were loosened.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Wil Evers

unread,
Jun 25, 2012, 1:02:13 PM6/25/12
to
Dave Abrahams wrote:

> on Sun Jun 24 2012, Wil Evers <bouncer-AT-dev.null> wrote:
>
>> It can only mean that the destructor is expected to do more than
>> just resource management.
>>
>> And that, I think, is the heart of the matter: is it a good idea to
>> change the language to provide better support for these other uses?
>> Even if it is technically feasible to do so, what will we gain?
>
> Two things I can think of:
>
> 1. Removal of gratuitous brittleness. In that sense, it's the same
> as the deprecation of dynamic exception-specifications.
> Programmers shouldn't have to worry needlessly about gotchas.

That's an excellent point, why call terminate() if the language can do
better? However, it does seem to be at odds with C++11's implicit
noexcept rule for destructors. To be honest, I don't think I
understand the reasons behind that decision; do you think it ought
to be revisited?

> 2. Legitimacy for idioms like "throwing scopeguard"
>
>> The idea that destructors are meant for automatic resource
>> management is the key design principle behind RAII;
>
> At this point what they were "meant for" is irrelevant; they're
> primarily used that way, but they also get used for other things all
> the time (e.g. logging at scope exit).

Agreed; it makes sense for such a destructor to throw if it fails to
update the log. Of course, the repackaging you're proposing does
imply that the thrown exception cannot always be caught like we're
used to now, but I guess that's a price I'd be willing to pay.

>> the idea that destructors don't throw is a hard requirement in many
>> parts of the standard library, and I suspect lots of user code is
>> based on these assumptions too. Why make it easier to break the
>> rules?
>
> The rules are already "broken." :-) It's time they were loosened.

Just curious: does that mean you think it's possible to lift the
current ban on container elements with throwing destructors?

Thanks,

- Wil


--

Dave Abrahams

unread,
Jun 25, 2012, 5:03:15 PM6/25/12
to
on Mon Jun 25 2012, Wil Evers <bouncer-AT-dev.null> wrote:

> Dave Abrahams wrote:
>
>> on Sun Jun 24 2012, Wil Evers <bouncer-AT-dev.null> wrote:
>>
>>> It can only mean that the destructor is expected to do more than
>>> just resource management.
>>>
>>> And that, I think, is the heart of the matter: is it a good idea
>>> to change the language to provide better support for these other
>>> uses? Even if it is technically feasible to do so, what will we
>>> gain?
>>
>> Two things I can think of:
>>
>> 1. Removal of gratuitous brittleness. In that sense, it's the same
>> as the deprecation of dynamic exception-specifications.
>> Programmers shouldn't have to worry needlessly about gotchas.
>
> That's an excellent point, why call terminate() if the language can
> do better? However, it does seem to be at odds with C++11's
> implicit noexcept rule for destructors. To be honest, I don't think
> I understand the reasons behind that decision; do you think it ought
> to be revisited?

IIUC without it, noexcept(<expression>) would extremely often produce
confusing false negatives because the expression contained some
temporary that was to be destroyed. At this point I think the ship
has sailed, but in principle it might have been possible to deal with
the noexcept problem differently, e.g. by evaluating noexcept-ness
before the destruction of temporaries.

>> 2. Legitimacy for idioms like "throwing scopeguard"
>>
>>> The idea that destructors are meant for automatic resource
>>> management is the key design principle behind RAII;
>>
>> At this point what they were "meant for" is irrelevant; they're
>> primarily used that way, but they also get used for other things
>> all the time (e.g. logging at scope exit).
>
> Agreed; it makes sense for such a destructor to throw if it fails to
> update the log. Of course, the repackaging you're proposing does
> imply that the thrown exception cannot always be caught like we're
> used to now,

? I don't understand what you mean. Could you explain?

> but I guess that's a price I'd be willing to pay.
>
>>> the idea that destructors don't throw is a hard requirement in
>>> many parts of the standard library, and I suspect lots of user
>>> code is based on these assumptions too. Why make it easier to
>>> break the rules?
>>
>> The rules are already "broken." :-) It's time they were loosened.
>
> Just curious: does that mean you think it's possible to lift the
> current ban on container elements with throwing destructors?

Yes, in principle.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Edward Rosten

unread,
Jun 26, 2012, 9:12:48 AM6/26/12
to
On Jun 22, 9:17 pm, Dave Abrahams <d...@boostpro.com> wrote:

> I think chances are good that it will be fine to throw from
> destructors. In fact, I intend to submit a proposal to make it so.

Do you have any kind of public draft or work in progress?

-Ed


--

Rani Sharoni

unread,
Jun 26, 2012, 9:19:14 AM6/26/12
to
On Jun 24, 8:30 pm, Dave Abrahams <d...@boostpro.com> wrote:
> on Sun Jun 24 2012, Rani Sharoni <ranisharoni75-AT-gmail.com> wrote:
> > Will the following memory be freed if the dtor of 'A' throws:
> > delete pA; // pA is of type A for which ~A might throw
>
> > I'm not sure what the standard says about such... (hence how the
> > implementation of given smart-ptr should 'safely' handle such).
>
> I don't know for sure either, but the answer has to be "yes" (and if
> it isn't, that must be fixed). The lifetime of *pA has ended, so
> there's absolutely no reason to keep the memory around.

You are right. shortly after posting I saw that the text address this:
5.3.5/7 - [ Note: The deallocation function is called regardless of
whether the destructor for the object or some element of the array
throws an exception. —end note ]

(thanks Danial for the reference).

We can try to generalize this principle:
Destructor never fails to end the lifetime of the destroyed objects.
Throwing destructor should not result with resource leak or bugs.

Conclusion - the success of the destructor to properly end the
lifetime of an object should not be dependent on operations that might
fail (I have seen few win32 functions that doesn't respect this).

It's seems easy to apply such principle to operations like
container::pop() but it's less clear how to apply it to operations
like container::clear(). should the exception abort the cleanup of the
rest of the operations? if so then how ~container() should be handled
as it has to end the lifetime of the contained objects to avoid leak
(since it's about to die). Should the destruction continue after the
exception? what if there is another exception thrown by the next
element? should throwing dtor always check that it's not during
unwinding? Maybe this should be tackled using the built-in array
destruction rules.

The "object must die" principle is also problematic for functions like
POSIX close(). AFAIR there are some cases in which the file-handle is
still alive after close() failure (e.g. POSIX cancellation might abort
close()). In other cases, like networking breakdown when closing the
file-handle, close() might return error but nevertheless guarantee
that the object lifetime ended (i.e. local resources freed). In that
sense "remote cleanup" is such problem in general.
C++ destruction is obviously not hospitable for such behaviors.

I personally hate the thought of having throwing destructors as a
common construct like throwing ctor. It can simply break to much
cleanup code that assumes that dtors do NOT throw and might force new
dtos code to be written in some ugly fashion to assure that all of its
explicit code is being executed regardless of exceptions.

Thanks,
Rani


--

Wil Evers

unread,
Jun 26, 2012, 4:47:27 PM6/26/12
to
Dave Abrahams wrote:

> on Mon Jun 25 2012, Wil Evers <bouncer-AT-dev.null> wrote:
>
>> Dave Abrahams wrote:
>>
>>> At this point what they were "meant for" is irrelevant; they're
>>> primarily used that way, but they also get used for other things
>>> all the time (e.g. logging at scope exit).
>>
>> Agreed; it makes sense for such a destructor to throw if it fails
>> to update the log. Of course, the repackaging you're proposing
>> does imply that the thrown exception cannot always be caught like
>> we're used to now,
>
> ? I don't understand what you mean. Could you explain?

I think you suggested a user-installed handler that would be called
when a second exception escapes from a destructor invoked while
unwinding because of some other exception. Such a handler can either
(1) terminate the program (which is the current state of affairs),
(2) propagate one of the exceptions while ignoring the other, or (3)
somehow link them together.

I'm worried about the way the calling code would be expected to deal
with either (2) or (3). (2) implies that a thrown exception is lost,
while it seems to me that (3) could cause at least one of the
exceptions to bypass a catch block meant to handle it.

Regards,

- Wil


--

DeMarcus

unread,
Jun 27, 2012, 4:40:32 PM6/27/12
to
>> Ok good, but how do you come to the conclusion that you want the root
>> cause at the /head/?
>
> It's like this: the initial operation requested by the "user" failed
> because of the root cause. If the destructor hadn't thrown anything,
> that's what we'd hear about. Secondary failures during cleanup are
> *typically* resource-release failures, which are *typically* fairly
> benign and of less interest to the caller; they don't describe why the
> requested operation failed. Ignoring them might even be a reasonable
> approach. So it doesn't seem reasonable to replace the head of the
> chain.
>

Ok, agree. The trick here will be to distinguish clean-up failures
related to the active exception and clean-up failures independent of the
active exception.


>> I claim that the most recent exception must be what is caught at the
>> next catch().
>
> I disagree. Suppose you have a recovery action for the most recent
> exception, and you take it, and retry? The original reason for failure
> persists, and the operation still fails. Not good.
>

This is a tricky one. As you say, what's the point retrying if the root
cause persists? On the other hand, if we take a look a bit up in the
call stack, it's not likely that the code there will look for and catch
out-of-bounds exceptions caused a long road down the call stack.



>> That prioritization is not obvious and I claim it must not be left as
>> a side task.
>
> What do you mean by a "side task?"
>

I mean that in order to deal with secondary exceptions you must really
know what you are doing and have a firm coding convention to deal with
them, otherwise we risk unsafe exception handling when people start to
throw from destructors without knowing how to deal with the several
exceptions that might be generated.


> My plan is to leave it up to the author of the hook function for dealing
> with nested exceptions. My claim is that "ignore secondary exceptions"
> might be a reasonable default, "chain the secondary exception to the
> first" might be better, "terminate" would be what you'd choose for
> absolute backward compatibility, and if you are are foolish enough to
> disagree with me about what should be at the head of the chain then
> you're free to do it the "wrong" way ;-)
>

I think we agree on most parts now, but I'm not sure that I've managed
to explain what I mean with independent failures.

There is a small risk, small but still a risk, that one destructor
throws a secondary exception for some completely independent reason when
another exception is active (some other function threw). Then "ignore
secondary exceptions" would be the same as

try
{
fnc();
}
catch( SecondaryException& )
{
// Swallow the secondary exception and do nothing!
}

I don't agree that it's a reasonable default.

That is what I meant with bulking. Somehow, you must be able to deal
with /multiple/ exception chains. Just putting a completely independent
secondary exception in the same chain as the active exception chain
will, in best case, lead to that the secondary exception is missed and
lost. In worst case, the application or person reading the log will
think it was part of the active failure and take action from that.


Allowing secondary exceptions may lead to something better, but I think
there must be some recommendations and guidelines how to use it.


Regards,
Daniel


--

Dave Abrahams

unread,
Jun 28, 2012, 4:37:08 PM6/28/12
to
Unspecified. clear() would be a basic guarantee operation.

> if so then how ~container() should be handled as it has to end the
> lifetime of the contained objects to avoid leak (since it's about to
> die). Should the destruction continue after the exception?

Yes.

> what if there is another exception thrown by the next element? should
> throwing dtor always check that it's not during unwinding?

No.

> Maybe this should be tackled using the built-in array destruction
> rules.

The rules should be the same.

> The "object must die" principle is also problematic for functions like
> POSIX close(). AFAIR there are some cases in which the file-handle is
> still alive after close() failure (e.g. POSIX cancellation might abort
> close()).

If it's problematic, you don't have to throw.

> In other cases, like networking breakdown when closing the
> file-handle, close() might return error but nevertheless guarantee
> that the object lifetime ended (i.e. local resources freed). In that
> sense "remote cleanup" is such problem in general. C++ destruction is
> obviously not hospitable for such behaviors.

I don't see why not. What else can be done?

> I personally hate the thought of having throwing destructors as a
> common construct like throwing ctor.

I dislike it also, but that's not a good enough reason to make whole
programs brittle if there's an exception in a dtor, IMO.

> It can simply break to much cleanup code that assumes that dtors do
> NOT throw and might force new dtos code to be written in some ugly
> fashion to assure that all of its explicit code is being executed
> regardless of exceptions.

I disagree that this is an issue. The "ugly" rewrite could consist
of refactoring cleanups into subobjects, which is usually a pretty
attractive solution.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Dave Abrahams

unread,
Jun 28, 2012, 4:48:46 PM6/28/12
to
on Tue Jun 26 2012, Edward Rosten <edward.rosten-AT-gmail.com> wrote:

> On Jun 22, 9:17 pm, Dave Abrahams <d...@boostpro.com> wrote:
>
>> I think chances are good that it will be fine to throw from
>> destructors. In fact, I intend to submit a proposal to make it so.
>
> Do you have any kind of public draft or work in progress?

Not yet. I'll post here when we have something.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Dave Abrahams

unread,
Jun 29, 2012, 2:33:58 PM6/29/12
to
on Tue Jun 26 2012, Wil Evers <bouncer-AT-dev.null> wrote:

> Dave Abrahams wrote:
>
>> on Mon Jun 25 2012, Wil Evers <bouncer-AT-dev.null> wrote:
>>
>>> Dave Abrahams wrote:
>>>
>>>> At this point what they were "meant for" is irrelevant; they're
>>>> primarily used that way, but they also get used for other things
>>>> all the time (e.g. logging at scope exit).
>>>
>>> Agreed; it makes sense for such a destructor to throw if it fails
>>> to update the log. Of course, the repackaging you're proposing
>>> does imply that the thrown exception cannot always be caught like
>>> we're used to now,
>>
>> ? I don't understand what you mean. Could you explain?
>
> I think you suggested a user-installed handler that would be called
> when a second exception escapes from a destructor invoked while
> unwinding because of some other exception.

Exactly

> Such a handler can either (1) terminate the program (which is the
> current state of affairs), (2) propagate one of the exceptions while
> ignoring the other, or (3) somehow link them together.
>
> I'm worried about the way the calling code would be expected to deal
> with either (2) or (3). (2) implies that a thrown exception is
> lost, while it seems to me that (3) could cause at least one of the
> exceptions to bypass a catch block meant to handle it.

Meh.

The information in an exception is very rarely of much importance to
the correct functioning of a program; if an exception is already in
flight, the same unwinding path will be followed at least until such a
time as the exception needs to be reported or translated.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Dave Abrahams

unread,
Jun 29, 2012, 2:39:41 PM6/29/12
to
on Wed Jun 27 2012, DeMarcus <use_my_alias_here-AT-hotmail.com> wrote:

>>> I claim that the most recent exception must be what is caught at
>>> the next catch().
>>
>> I disagree. Suppose you have a recovery action for the most recent
>> exception, and you take it, and retry? The original reason for
>> failure persists, and the operation still fails. Not good.
>
> This is a tricky one. As you say, what's the point retrying if the
> root cause persists? On the other hand, if we take a look a bit up
> in the call stack, it's not likely that the code there will look for
> and catch out-of-bounds exceptions caused a long road down the call
> stack.

It's not likely that any code will look for and catch out-of-bounds
exceptions, since they usually represent programming errors, and
nobody can maintain his sanity while coding as though the program is
broken.

>>> That prioritization is not obvious and I claim it must not be left
>>> as a side task.
>>
>> What do you mean by a "side task?"
>
> I mean that in order to deal with secondary exceptions you must
> really know what you are doing and have a firm coding convention to
> deal with them, otherwise we risk unsafe exception handling when
> people start to throw from destructors without knowing how to deal
> with the several exceptions that might be generated.

Meh. The program is already "on the unwinding path." I don't see how
the secondary exception is going to meaninfully impact safety.

>> My plan is to leave it up to the author of the hook function for
>> dealing with nested exceptions. My claim is that "ignore secondary
>> exceptions" might be a reasonable default, "chain the secondary
>> exception to the first" might be better, "terminate" would be what
>> you'd choose for absolute backward compatibility, and if you are
>> are foolish enough to disagree with me about what should be at the
>> head of the chain then you're free to do it the "wrong" way ;-)
>>
>
> I think we agree on most parts now, but I'm not sure that I've
> managed to explain what I mean with independent failures.
>
> There is a small risk, small but still a risk, that one destructor
> throws a secondary exception for some completely independent reason
> when another exception is active (some other function threw).

Yes.

> Then "ignore secondary exceptions" would be the same as
>
> try
> {
> fnc();
> }
> catch( SecondaryException& )
> {
> // Swallow the secondary exception and do nothing!
> }

No, it wouldn't be the same, because the stack is already unwinding.

> I don't agree that it's a reasonable default.
>
> That is what I meant with bulking. Somehow, you must be able to deal
> with /multiple/ exception chains. Just putting a completely
> independent secondary exception in the same chain as the active
> exception chain will, in best case, lead to that the secondary
> exception is missed and lost.

There's no loss; the chains I'm talking about can be walked and
inspected by exception handling code.

> In worst case, the application or person reading the log will think
> it was part of the active failure and take action from that.
>
> Allowing secondary exceptions may lead to something better, but I
> think there must be some recommendations and guidelines how to use
> it.

Of course there must be guidelines. You just don't seem to agree that
one reasonable guideline is "just ignore that one."

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Rani Sharoni

unread,
Jun 29, 2012, 2:46:37 PM6/29/12
to
On Jun 28, 11:37 pm, Dave Abrahams <d...@boostpro.com> wrote:
> on Tue Jun 26 2012, Rani Sharoni <ranisharoni75-AT-gmail.com> wrote:
> > It's seems easy to apply such principle to operations like
> > container::pop() but it's less clear how to apply it to operations
> > like container::clear(). should the exception abort the cleanup of the
> > rest of the operations?
>
> Unspecified. clear() would be a basic guarantee operation.

This seems inconsistent with the "object must die" principle hence
hard to program with (e.g. what should a caller do if it can't
unregister its callback before unloading its module). Should there be
another "full_clear()" operations that loops around clear() in order
to guarantee full cleanup? Unlike basic guarantee assign(range), I
don't see any gain by leaving such operations as unspecified.

> > Maybe this should be tackled using the built-in array destruction
> > rules.
>
> The rules should be the same.

I will see if I can at least find the current array rules (Daniel?).

> > The "object must die" principle is also problematic for functions like
> > POSIX close(). AFAIR there are some cases in which the file-handle is
> > still alive after close() failure (e.g. POSIX cancellation might abort
> > close()).
>
> If it's problematic, you don't have to throw.

But in the close() case the underlying object (file-handle) is still
alive hence retry is required to assure cleanup.
POSIX close doc: "If close() is interrupted by a signal that is to be
caught, it shall return -1 with errno set to [EINTR] and the state of
fildes is unspecified." (i.e. basic guarantee single object
destruction).

> > In other cases, like networking breakdown when closing the
> > file-handle, close() might return error but nevertheless guarantee
> > that the object lifetime ended (i.e. local resources freed). In
> > that sense "remote cleanup" is such problem in general. C++
> > destruction is obviously not hospitable for such behaviors.
>
> I don't see why not. What else can be done?

close() can fail to destruct the object and IMHO this can't be
supported by the C++ destruction model that doesn't allow the object
to stay alive even after the dtor throws. One can try to handle the
close() case by deferring the cleanup of the underlying file-handle
but the classic dtor calling close() will not work here (and I see no
way the language can tackle such).

> > It can simply break to much cleanup code that assumes that dtors do
> > NOT throw and might force new dtos code to be written in some ugly
> > fashion to assure that all of its explicit code is being executed
> > regardless of exceptions.
>
> I disagree that this is an issue. The "ugly" rewrite could consist
> of refactoring cleanups into subobjects, which is usually a pretty
> attractive solution.

IMHO allowing non empty dtors bodies (i.e. allow explicit destruction)
is more important than allowing dtors to throw hence the tradeoff in
which throwing dtor will always be something to be avoided.

FWIW, many of the bugs I find using fault-injection is related to dtor
no being called when ctor throws...

Rani


--

Dave Abrahams

unread,
Jun 30, 2012, 11:25:31 AM6/30/12
to
on Fri Jun 29 2012, Rani Sharoni <ranisharoni75-AT-gmail.com> wrote:

> On Jun 28, 11:37 pm, Dave Abrahams <d...@boostpro.com> wrote:
>> on Tue Jun 26 2012, Rani Sharoni <ranisharoni75-AT-gmail.com> wrote:
>> > It's seems easy to apply such principle to operations like
>> > container::pop() but it's less clear how to apply it to
>> > operations like container::clear(). should the exception abort
>> > the cleanup of the rest of the operations?
>>
>> Unspecified. clear() would be a basic guarantee operation.
>
> This seems inconsistent with the "object must die" principle

Could you please spell out that principle? I don't think I'm familiar
with it.

> hence hard to program with (e.g. what should a caller do if it can't
> unregister its callback before unloading its module).

Err... I don't know. Maybe you should spell out what you mean.

> Should there be another "full_clear()" operations that loops around
> clear() in order to guarantee full cleanup? Unlike basic guarantee
> assign(range), I don't see any gain by leaving such operations as
> unspecified.

Oh, I think I see what you're getting at. If you want to use clear()
to close things down, you should at *least* have a guarantee that you
make progress towards emptiness.

Well, clear() could give the guarantee that the container is empty
even upon exiting via an exception. I don't see any point in a
separate full_clear() unless this guarantee poses some kind of
efficiency problem.

>> > Maybe this should be tackled using the built-in array destruction
>> > rules.
>>
>> The rules should be the same.
>
> I will see if I can at least find the current array rules (Daniel?).

Awww, man, that's just lazy. We shouldn't use Daniel as our
voice-recognition-enabled intelligent copy of the standard... Scratch
that; I just spent 20 minutes looking for the text on this and
couldn't find it. Daniel?

;-)

>> > The "object must die" principle is also problematic for functions
>> > like POSIX close(). AFAIR there are some cases in which the
>> > file-handle is still alive after close() failure (e.g. POSIX
>> > cancellation might abort close()).
>>
>> If it's problematic, you don't have to throw.
>
> But in the close() case the underlying object (file-handle) is still
> alive hence retry is required to assure cleanup.

But you can't "assure cleanup" if the cleanup operations can fail.

> POSIX close doc: "If close() is interrupted by a signal that is to
> be caught, it shall return -1 with errno set to [EINTR] and the
> state of fildes is unspecified." (i.e. basic guarantee single object
> destruction).

I don't think I'd want to handle cases like this with an exception.
Maybe a tight loop around close().

>> > In other cases, like networking breakdown when closing the
>> > file-handle, close() might return error but nevertheless
>> > guarantee that the object lifetime ended (i.e. local resources
>> > freed). In that sense "remote cleanup" is such problem in
>> > general. C++ destruction is obviously not hospitable for such
>> > behaviors.
>>
>> I don't see why not. What else can be done?
>
> close() can fail to destruct the object

close() doesn't destruct/destroy any objects. It just does what the
POSIX doc says.

> and IMHO this can't be supported by the C++ destruction model that
> doesn't allow the object to stay alive even after the dtor
> throws. One can try to handle the close() case by deferring the
> cleanup of the underlying file-handle but the classic dtor calling
> close() will not work here (and I see no way the language can tackle
> such).

It depends what you think "working" means. For many applications,
failure to close is benign. If it isn't benign and you need to take
more drastic measures, well, take them.

>> > It can simply break to much cleanup code that assumes that dtors
>> > do NOT throw and might force new dtos code to be written in some
>> > ugly fashion to assure that all of its explicit code is being
>> > executed regardless of exceptions.
>>
>> I disagree that this is an issue. The "ugly" rewrite could consist
>> of refactoring cleanups into subobjects, which is usually a pretty
>> attractive solution.
>
> IMHO allowing non empty dtors bodies (i.e. allow explicit
> destruction)

non-empty dtor body:

~foo::foo() { non_empty(); }

explicit destruction:

p->~foo();

I don't see the relationship here.

> is more important than allowing dtors to throw hence the tradeoff in
> which throwing dtor will always be something to be avoided.

Having non-empty dtor bodies is not in conflict with allowing throwing
dtors, any more than having non-empty ctor bodies is in conflict with
allowing throwing ctors, is it?

> FWIW, many of the bugs I find using fault-injection is related to
> dtor no being called when ctor throws...

Certainly that must be a common error for people who don't follow "The
Dimov Rule" http://lists.boost.org/boost-users/2003/07/4662.php

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


Daniel Krügler

unread,
Jun 30, 2012, 3:32:39 PM6/30/12
to
Am 30.06.2012 17:25, schrieb Dave Abrahams:
> on Fri Jun 29 2012, Rani Sharoni <ranisharoni75-AT-gmail.com> wrote:
[..]
>>>> Maybe this should be tackled using the built-in array destruction
>>>> rules.
>>>
>>> The rules should be the same.
>>
>> I will see if I can at least find the current array rules
>> (Daniel?).
>
> Awww, man, that's just lazy. We shouldn't use Daniel as our
> voice-recognition-enabled intelligent copy of the standard...
> Scratch that; I just spent 20 minutes looking for the text on this
> and couldn't find it. Daniel?
>
> ;-)

Some explicit wording was added as part of

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1165

so [except.ctor] p2 seems the right place (respecting now arrays as
well):

"An object of any storage duration whose initialization or destruction
is terminated by an exception will have destructors executed for all
of its fully constructed subobjects (excluding the variant members of
a union-like class), that is, for subobjects for which the principal
constructor (12.6.2) has completed execution and the destructor has
not yet begun execution."

plus [class.dtor] p8:

"Destructors for elements of an array are called in reverse order of
their construction (see 12.6)."

HTH & Greetings from Bremen,

Daniel Krügler


--

Wil Evers

unread,
Jul 1, 2012, 2:45:51 AM7/1/12
to
Dave Abrahams wrote:

> on Tue Jun 26 2012, Wil Evers <bouncer-AT-dev.null> wrote:

>> I think you suggested a user-installed handler that would be called
>> when a second exception escapes from a destructor invoked while
>> unwinding because of some other exception.
>
> Exactly
>
>> Such a handler can either (1) terminate the program (which is the
>> current state of affairs), (2) propagate one of the exceptions while
>> ignoring the other, or (3) somehow link them together.
>>
>> I'm worried about the way the calling code would be expected to deal
>> with either (2) or (3). (2) implies that a thrown exception is
>> lost, while it seems to me that (3) could cause at least one of the
>> exceptions to bypass a catch block meant to handle it.
>
> Meh.
>
> The information in an exception is very rarely of much importance to
> the correct functioning of a program; if an exception is already in
> flight, the same unwinding path will be followed at least until such a
> time as the exception needs to be reported or translated.

I disagree. Consider, for example, a loop that listens on a network
connection for incoming request messages, answering each with a
corresponding reply message. In such a loop, some exceptions (such as
a syntax error in the request) can be handled by simply answering with
an error reply, while other exceptions (such as a failure to allocate
enough buffer space to compose a valid reply) are so serious that the
loop needs to be exited and the connection closed.

>From experience, I can assure you that the critical task of determining
the proper cause of action here can get quite hairy, even with today's
exception semantics. That will only get harder if we have to take
either lost exceptions (the (2) above) or exceptions reported as linked
to some other exception (the (3) above) into account.

In short, it seems to me your proposal trades complexity on the
throwing side for complexity on the catching side. That may very well
be a reasonable tradeoff, but at the very least, we should be aware of
its pros and cons.

- Wil


--

Rani Sharoni

unread,
Jul 1, 2012, 2:46:06 AM7/1/12
to
On Jun 30, 10:32 pm, Daniel Krügler <daniel.krueg...@gmail.com> wrote:
> Am 30.06.2012 17:25, schrieb Dave Abrahams:
> >> I will see if I can at least find the current array rules
> >> (Daniel?).
>
> > Awww, man, that's just lazy. We shouldn't use Daniel as our
> > voice-recognition-enabled intelligent copy of the standard...
> > Scratch that; I just spent 20 minutes looking for the text on this
> > and couldn't find it. Daniel?
>
> > ;-)
>
> Some explicit wording was added as part of
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1165
>
> so [except.ctor] p2 seems the right place (respecting now arrays as
> well):
>
> "An object of any storage duration whose initialization or destruction
> is terminated by an exception will have destructors executed for all
> of its fully constructed subobjects (excluding the variant members of
> a union-like class), that is, for subobjects for which the principal
> constructor (12.6.2) has completed execution and the destructor has
> not yet begun execution."
>
> plus [class.dtor] p8:
>
> "Destructors for elements of an array are called in reverse order of
> their construction (see 12.6)."

I see that 'subobjects' also refers to array's elements:
1.8/2: Objects can contain other objects, called subobjects. A
subobject can be a member subobject (9.2), a base class subobject
(Clause 10), or an *array element*.

Thanks Daniel!

Rani Sharoni

unread,
Jul 1, 2012, 4:10:39 AM7/1/12
to
On Jun 30, 6:25 pm, Dave Abrahams <d...@boostpro.com> wrote:
> on Fri Jun 29 2012, Rani Sharoni <ranisharoni75-AT-gmail.com> wrote:
> > Should there be another "full_clear()" operations that loops
> > around clear() in order to guarantee full cleanup? Unlike basic
> > guarantee assign(range), I don't see any gain by leaving such
> > operations as unspecified.
>
> Oh, I think I see what you're getting at. If you want to use
> clear() to close things down, you should at *least* have a guarantee
> that you make progress towards emptiness.
>
> Well, clear() could give the guarantee that the container is empty
> even upon exiting via an exception. I don't see any point in a
> separate full_clear() unless this guarantee poses some kind of
> efficiency problem.

Sorry for not being clear enough but I think that you get my point.
cleanup function should guarantee local resources cleanup/cancellation
regardless of exceptions (i.e. behave like they succeeded). Remote
resources cleanup is the responsibility of the the remote process
(e.g. using some keep-alive and rundown mechanisms).

> > POSIX close doc: "If close() is interrupted by a signal that is to
> > be caught, it shall return -1 with errno set to [EINTR] and the
> > state of fildes is unspecified." (i.e. basic guarantee single
> > object destruction).
>
> I don't think I'd want to handle cases like this with an exception.
> Maybe a tight loop around close().

That might end up with infinite loop... (hence such resource might
stay around until process teardown).
Ok, I must say it - POSIX close() is poorly defined since it implies
that there is no way to guarantee resource cleanup beside of by nuking
the process (it's even more severe since it's related to cancellation
of async operations). One should avoid writing such cleanup functions.

> > is more important than allowing dtors to throw hence the tradeoff
> > in which throwing dtor will always be something to be avoided.
>
> Having non-empty dtor bodies is not in conflict with allowing
> throwing dtors, any more than having non-empty ctor bodies is in
> conflict with allowing throwing ctors, is it?

Not always since dtor code might not correspond to ctor code hence
there is no need to call it if the later fails (e.g. construct empty
object and than insert stuff into it). Another example is related to
async operations initiated from ctors like thread creation. properly
coded dtor should explicitly wait for such outstanding operation or
otherwise they might call the object after it was destructed.

It might be interesting to perform some survey to see how much dtors
have non-empty bodies hence might be impacted from throwing dtors (I
personally guess that there are plenty of such dtors).

> > FWIW, many of the bugs I find using fault-injection is related to
> > dtor no being called when ctor throws...
>
> Certainly that must be a common error for people who don't follow
> "The Dimov Rule" http://lists.boost.org/boost-users/2003/07/4662.php

"manage every newly-allocated resource immediately with a *named*
resource manager object". This is not exactly the same since the
common defect I mentioned is related for the false (intuitive?)
assumption/oversight that dtors are called even if the ctor fails.
Such problem doesn't exist when not exceptions are not being used so
people that switch to throwing code often forget the new limitation.

Thanks,
Rani


--

DeMarcus

unread,
Jul 1, 2012, 6:03:44 PM7/1/12
to
> It's not likely that any code will look for and catch out-of-bounds
> exceptions, since they usually represent programming errors, and
> nobody can maintain his sanity while coding as though the program is
> broken.
>

Imagine that a corrupt configure file (e.g. XML) generates the out-of-bounds exception, then it's not a programming error but rather a corrupt or invalid file.


>> That is what I meant with bulking. Somehow, you must be able to deal
>> with /multiple/ exception chains. Just putting a completely
>> independent secondary exception in the same chain as the active
>> exception chain will, in best case, lead to that the secondary
>> exception is missed and lost.
>
> There's no loss; the chains I'm talking about can be walked and
> inspected by exception handling code.
>

I try to emphasize the same problem that Wil Evers does when he discusses the alternatives for a handler; (1) terminate, (2) propagate and ignore secondary, (3) link.

I'm out of good examples to illustrate the problem but one use case I will investigate myself is the following.

1. Create two RAII objects A and B, that with 50% chance each throw from the destructor. B throws BException and A throws AException.
2. Write a consistent exception handling that prints AException to std::cout and retries B until it succeeds.


>> Allowing secondary exceptions may lead to something better, but I
>> think there must be some recommendations and guidelines how to use
>> it.
>
> Of course there must be guidelines. You just don't seem to agree that
> one reasonable guideline is "just ignore that one."
>

Definitely not. Just ignoring an exception is not an option to me unless the software requirement allows it.


Regards,
Daniel


--

Dave Abrahams

unread,
Jul 2, 2012, 9:17:22 AM7/2/12
to
on Sun Jul 01 2012, Rani Sharoni <ranisharoni75-AT-gmail.com> wrote:

> On Jun 30, 10:32 pm, Daniel Krügler <daniel.krueg...@gmail.com> wrote:
>
>> "Destructors for elements of an array are called in reverse order of
>> their construction (see 12.6)."
>
> I see that 'subobjects' also refers to array's elements:

Yeah, that was the piece I was missing also. Good to know.

> 1.8/2: Objects can contain other objects, called subobjects. A
> subobject can be a member subobject (9.2), a base class subobject
> (Clause 10), or an *array element*.
>
> Thanks Daniel!

Quite so!

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


0 new messages