Throw from a destructor

P

Phlip

C++ers:

I have this friend who thinks C++ cannot throw from a destructor.

I think throwing from a destructor is bad Karma, and should be designed
around, but that C++ cannot strictly prevent the actual event.

(One compiler-oriented reason is a compiler might not be able to detect
that the call-tree from a destructor leads to a throw.)

Put another way, suppose I have a big object, aBigSimCity, and it has a
..shutdown() method. It must call before ~BigSimCity() calls, and it might
throw. This implies my Wrapper class, containing aBigSimCity, should never
declare a destructor like this: ~Wrapper() { aBigSimCity.shutdown(); }.

..shutdown() might throw, and I don't feel like wrapping it in
try{}catch(). ~Wrapper should not throw, hence I need a better system to
break things down.

So which of us is right?
 
P

Phlip

Phlip said:
I have this friend who thinks C++ cannot throw from a destructor.

I think throwing from a destructor is bad Karma, and should be designed
around, but that C++ cannot strictly prevent the actual event.

(One compiler-oriented reason is a compiler might not be able to detect
that the call-tree from a destructor leads to a throw.)

Put another way, suppose I have a big object, aBigSimCity, and it has a
.shutdown() method. It must call before ~BigSimCity() calls, and it might
throw. This implies my Wrapper class, containing aBigSimCity, should never
declare a destructor like this: ~Wrapper() { aBigSimCity.shutdown(); }.

.shutdown() might throw, and I don't feel like wrapping it in
try{}catch(). ~Wrapper should not throw, hence I need a better system to
break things down.

Forgot to mention why this isn't a FAQ:

http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.3

Suppose the Wrapper object contains a catch(...) that takes care of any
exceptions. So in this specific case, the Wrapper's destructor can
never be called during un-winding from a thrown exception, so its
destructor can always safely call things that might throw.

Don't do it anyway.
 
I

Ian Collins

Phlip said:
C++ers:

I have this friend who thinks C++ cannot throw from a destructor.

I think throwing from a destructor is bad Karma, and should be designed
around, but that C++ cannot strictly prevent the actual event.

(One compiler-oriented reason is a compiler might not be able to detect
that the call-tree from a destructor leads to a throw.)

Put another way, suppose I have a big object, aBigSimCity, and it has a
..shutdown() method. It must call before ~BigSimCity() calls, and it might
throw. This implies my Wrapper class, containing aBigSimCity, should never
declare a destructor like this: ~Wrapper() { aBigSimCity.shutdown(); }.

..shutdown() might throw, and I don't feel like wrapping it in
try{}catch(). ~Wrapper should not throw, hence I need a better system to
break things down.

So which of us is right?
You!

I tend to add a throw() to my destructors (especially virtual) to
indicate that they will not throw.
 
J

Joe Van Dyk

Phlip said:
C++ers:

I have this friend who thinks C++ cannot throw from a destructor.

I think throwing from a destructor is bad Karma, and should be designed
around, but that C++ cannot strictly prevent the actual event.

(One compiler-oriented reason is a compiler might not be able to detect
that the call-tree from a destructor leads to a throw.)

Put another way, suppose I have a big object, aBigSimCity, and it has a
.shutdown() method. It must call before ~BigSimCity() calls, and it might
throw. This implies my Wrapper class, containing aBigSimCity, should never
declare a destructor like this: ~Wrapper() { aBigSimCity.shutdown(); }.

.shutdown() might throw, and I don't feel like wrapping it in
try{}catch(). ~Wrapper should not throw, hence I need a better system to
break things down.

So which of us is right?

(Scott Meyer's Effective C++ 3rd edition, Item 8 addresses exactly this.)


How about something like (almost verbatim from the book):

class BigSimCity
{
void shutdown()
{
// do stuff that may throw
closed_ = true;
}
~BigSimCity() // would this have an empty throw() specification?
{
if (!closed)
{
try { shutdown(); }
catch (...) { // log or something }
}
}
private:
bool closed;
};

You give the client the chance to shutdown() on their own if they want
catch it on their own. And if not, then shutdown()'s called by the
destructor.

Joe
 
C

Cy Edmunds

Phlip said:
C++ers:

I have this friend who thinks C++ cannot throw from a destructor.

I think throwing from a destructor is bad Karma, and should be designed
around, but that C++ cannot strictly prevent the actual event.

(One compiler-oriented reason is a compiler might not be able to detect
that the call-tree from a destructor leads to a throw.)

Put another way, suppose I have a big object, aBigSimCity, and it has a
.shutdown() method. It must call before ~BigSimCity() calls, and it might
throw. This implies my Wrapper class, containing aBigSimCity, should never
declare a destructor like this: ~Wrapper() { aBigSimCity.shutdown(); }.

.shutdown() might throw, and I don't feel like wrapping it in
try{}catch(). ~Wrapper should not throw, hence I need a better system to
break things down.

So which of us is right?

There is nothing in the language to prevent a destructor from throwing. How
could the compiler enforce such a rule? Some of the other replies mention
using throw specifications but they don't help at all. Basically you should
never allow your destructor to throw. If ~BigSimCity() can throw the guy who
wrote it screwed up, not the client of the class.

Cy
 
R

Roland Pibinger

(Scott Meyer's Effective C++ 3rd edition, Item 8 addresses exactly this.)
How about something like (almost verbatim from the book):

class BigSimCity
{
void shutdown()
{
// do stuff that may throw
closed_ = true;
}
~BigSimCity() // would this have an empty throw() specification?
{
if (!closed)
{
try { shutdown(); }
catch (...) { // log or something }
}
}
private:
bool closed;
};

You give the client the chance to shutdown() on their own if they want
catch it on their own. And if not, then shutdown()'s called by the
destructor.

The above works in some circumstances but is not a general solution.
It forces the client to write try-catch blocks, e.g.

try {
// ...
mySimCity.shutdown();
} catch (SimCityException& e) {
// do something
} catch (...) {
// do somethig else
}

In general, when the shutdown function produces meaningful output
(exception or return value) for the user it cannot be called in the
destructor.
Take as an example (good and bad) iostreams. When the file is opened
read-only then you can ignore the return value from close(). But when
the file is opened for write you cannot ignore the return value from
close(). Iostreams do not distinguish that cases and use the wrong
default.
Your example may be re-written in the following way:

class BigSimCity
{
bool commit()
{
// do stuff that may throw
closed_ = true;
return closed_;
}

void rollback() throw()
{
// do stuff that cannot throw or for which you
// can ignore exceptions and return values
}

~BigSimCity() throw()
{
if (!closed)
{
rollback();
}
}
private:
bool closed;
};

Best wishes,
Roland Pibinger
 
J

Jim Langston

Phlip said:
C++ers:

I have this friend who thinks C++ cannot throw from a destructor.

I think throwing from a destructor is bad Karma, and should be designed
around, but that C++ cannot strictly prevent the actual event.

(One compiler-oriented reason is a compiler might not be able to detect
that the call-tree from a destructor leads to a throw.)

Put another way, suppose I have a big object, aBigSimCity, and it has a
.shutdown() method. It must call before ~BigSimCity() calls, and it might
throw. This implies my Wrapper class, containing aBigSimCity, should never
declare a destructor like this: ~Wrapper() { aBigSimCity.shutdown(); }.

.shutdown() might throw, and I don't feel like wrapping it in
try{}catch(). ~Wrapper should not throw, hence I need a better system to
break things down.

So which of us is right?

It's extremely simple to test. Just throw from a destructor and see if it's
allowed.

The answer is, yes, you can throw from a destructor but never ever should.
The problem being, as I understand it, if there are *2* errors thrown. The
compiler/OS can't handle this and terminates.
 
P

Phlip

Jim said:
It's extremely simple to test. Just throw from a destructor and see if
it's allowed.

Please don't mislead the newbies.

Newbies: Jim is teasing. You should never rely on your compiler's current
behavior to learn how C++ works. It may always compile more leniently than
the Standard, and its undefined behavior may appear to work correctly.
 
J

James Bannon

Phlip said:
Please don't mislead the newbies.

Newbies: Jim is teasing. You should never rely on your compiler's current
behavior to learn how C++ works. It may always compile more leniently than
the Standard, and its undefined behavior may appear to work correctly.

Don't we know it! However, the Standard is usually as clear as mud and there are so
many cross-references it is very difficult to know whether the clause you're actually
reading is not modified by some other clause somewhere else (I've been bitten several
times by this - even if I have followed the cross-references and notes). Besides
this, some of the meanings of various concepts are not spelled out explicitly. For
instance I still can't find the specific definition of the term POD. I know what a
POD is (roughly) but I can't find a specific definition in the Standard other than a
note to say that the acronym POD stands for "Plain Old Data".

In answer to the OP, AFAIK the language doesn't _prevent_ throwing an exception from
a destructor but it should _never_ be done for at least two reasons that I know of:

1. Leaking an exception from a destructor during stack unwinding when processing an
exception leads to an immediate call to terminate - bang your dead! (to use Herb
Sutter's language).

2. If, for some reason, your lucky enough not to have terminate called, what do you
do with the exception when you've caught it? Re-throw if you can't recover? You're in
terminate land. In any case, how do you resuscitate or kill a partially dead object
when you have no way of knowing what state the object is in because that information
isn't conveyed to you by the exception class?

Sutter and others advise writing destructors as if they had an empty throw
specification. That's good enough for me! If you do specify it, say ~T() throw()
{...}, at least if an exception is leaked then terminate will be called immediately.
However, most people don't use throw specifications at all primarily because they're
expensive at runtime and sometimes they don't buy you very much in the way of safety
(Try analysing a piece of code and see how many exceptions it can possibly throw then
list these in the throw specifications. Can you do it consistently without any
errors? I know I can't.)

The meaning of undefined behaviour is a book in itself but broadly speaking it means
"anything is possible" from (seemingly) correct behaviour to your computer taking off
and launching itself into orbit or an invisible pink elephant suddenly appearing in
the room. Francis Glassborow relates a story in his recent book of how undefined
behaviour can bite and I once brought down a departmental server by missing out a 1
from the parameter list in a call to read() - took me 4 days to track down the
problem and I got a formal verbal warning for the mistake.

Anyway, that's my rant for the day!

Jim.
 
N

Noah Roberts

Jim said:
It's extremely simple to test. Just throw from a destructor and see if it's
allowed.

The answer is, yes, you can throw from a destructor but never ever should.
The problem being, as I understand it, if there are *2* errors thrown. The
compiler/OS can't handle this and terminates.

There are numerous problems with destructors throwing, not the least of
which is:

class RandDestThrow
{
public:
~RandDestThrow() { if (!(rand() % 2)) throw 666; }
};

RandDestThrow * arr = new RandDestThrow[666];


try
{
delete [] arr;
} catch (...)
{
// What do we do exactly?
}

It is impossible to write exception safe code if any destructor can
throw. Destructors must therefore ALWAYS have the no-throw guarantee.
If this means having catch(...) then so be it...never allow exceptions
to escape a destructor...never.
 
M

Marcus Kwok

James Bannon said:
2. If, for some reason, your lucky enough not to have terminate
called, what do you do with the exception when you've caught it?
Re-throw if you can't recover? You're in terminate land. In any case,
how do you resuscitate or kill a partially dead object when you have
no way of knowing what state the object is in because that information
isn't conveyed to you by the exception class?

Sutter and others advise writing destructors as if they had an empty
throw specification. That's good enough for me! If you do specify it,
say ~T() throw() {...}, at least if an exception is leaked then
terminate will be called immediately.

Actually, it will call unexpected(), which I believe by default will
call terminate(), but this behavior can be overridden with
std::set_unexpected().
However, most people don't use
throw specifications at all primarily because they're expensive at
runtime and sometimes they don't buy you very much in the way of
safety

http://www.gotw.ca/gotw/082.htm
seems to agree.
 
J

James Bannon

Marcus said:
Actually, it will call unexpected(), which I believe by default will
call terminate(), but this behavior can be overridden with
std::set_unexpected().


http://www.gotw.ca/gotw/082.htm
seems to agree.

You're right of course, it does call unexpected() which, if not redefined, will call
terminate. But what can you do within std::set_unexpected() if a destructor
unexpectedly throws? By the time it gets to the unexpected handler you've no way of
knowing which destructor caused the problem, so you might as well terminate anyway
although it might be a bit more graceful that a core dump.

Jim.
 
R

red floyd

James said:
For instance I still can't find the specific definition of the term POD. I
know what a POD is (roughly) but I can't find a specific definition in
the Standard other than a note to say that the acronym POD stands for
"Plain Old Data".

See 3.9/10 and 9/4
 
J

Jim Langston

James Bannon said:
Thanks red,
I have it! 9/4 is interesting as an English example of a self-referential
definition. :)

Really? I'd like to read it but don't have the standard. Is it something
like the definition of recursion?

Recursion: See recursion.
 
P

Phlip

Noah said:
It is impossible to write exception safe code if any destructor can
throw. Destructors must therefore ALWAYS have the no-throw guarantee.
If this means having catch(...) then so be it...never allow exceptions
to escape a destructor...never.

Thanks guys. Here's why I asked the question.

Some C++ test rigs support an explicit setUp() and tearDown() for their test
fixtures.

Some (despite being written by authors of books on C++) only provide the
constructor and destructor of the fixture.

I had to make a case for always providing tearDown(). For example, my
current project must call orb->shutdown() after each test case, and that
might throw. I need the throw reliably caught, without impossible
ramifications.
 
J

James Bannon

Jim said:
Really? I'd like to read it but don't have the standard. Is it something
like the definition of recursion?

Recursion: See recursion.
Something like that except it's of the form POD: see non-POD.

The wording I have is:

"...A POD-struct is an aggregate class that has no non-static data members of type
non-POD-struct, non-POD-union (or arrays of such types) or reference, and has no
user-defined copy assignment operator and no user-defined destructor...."

Hmmm.... clear enough? :)

Jim.
 
M

Marcus Kwok

James Bannon said:
You're right of course, it does call unexpected() which, if not
redefined, will call terminate. But what can you do within
std::set_unexpected() if a destructor unexpectedly throws? By the time
it gets to the unexpected handler you've no way of knowing which
destructor caused the problem, so you might as well terminate anyway
although it might be a bit more graceful that a core dump.

Well, unexpected() seems to be pretty limited.

http://www.gotw.ca/gotw/082.htm basically says you can either 1)
translate the exception into something allowed by the exception
specification, or 2) call terminate(). But IIRC if a destructor throws
an exception in the process of stack unwinding, then terminate() gets
called immediately, the rationale being that if there are two active
exceptions, it is undetermined which one should get priority, so the
program just dies.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top