Re: What risk of undefined behavior if destructor must throw?
Terry G wrote:
I understand that a destructor that throws is "bad".
Here's a throw from a destructor:
http://unittest-cpp.sourceforge.net/
It's in the CVS head; I don't think it's in a released version yet.
http://svn.sourceforge.net/viewvc/unittest-cpp/UnitTest%2B%2B/src/TestMacros.h?view=markup&pathrev=161
Here's the code fragment that does it, following the Obese Macro Design
Pattern:
void Test##Fixture##Name::RunImpl(UnitTest::TestResults& testResults_) const
\
{ \
char* fixtureMem = new char[sizeof(Fixture##Name##Helper)]; \
Fixture##Name##Helper* fixtureHelper; \
try { \
fixtureHelper = new(fixtureMem) Fixture##Name##Helper(m_details); \
} \
catch (...) { \
testResults_.OnTestFailure(UnitTest::TestDetails(m_details, __LINE__), \
"Unhandled exception while constructing fixture " #Fixture); \
delete[] fixtureMem; \
return; \
} \
fixtureHelper->RunTest(testResults_); \
try { \
fixtureHelper->~Fixture##Name##Helper(); \
} \
catch (...) \
testResults_.OnTestFailure(UnitTest::TestDetails(m_details,
__LINE__), \
"Unhandled exception while destroying fixture " #Fixture); \
} \
delete[] fixtureMem; \
}
Apologies for my feeb cleanup of all those lines and stuff. (My own version
of a C++ test rig does much the same thing, but doesn't put the actual
calling code into a macro!)
The code supports, eventually, a test macro like this:
TEST_FIXTURE(myFixture, myCase)
{
assert(this->works());
}
Before we wonder why myFixture's destructor might throw, we need more Unit
Test theory. Most test fixtures use setUp() and tearDown() to (literately)
construct and destruct their target objects. So suppose my test needed an
ORB; then its fixture would call orb->init(whatever) and orb->fini()
respectively.
But because orb->fini() is not a destructor, its authors allow it to throw.
Shutting down an ORB is a complex business, and every error should be
respected. (Right?)
Because the creators of UnitTest++ did not provide setUp and tearDown(),
they need to catch errors flying out of myFixture's destructor.
To do this, they in-place construct the testable object (derived from
myFixture), then use it, then in-place destruct it, then catch when its
destructor throws, then delete its memory. (But note, because they indeed do
use a macro, they could have put the memory for the test case object onto
the stack...)
I think this might leak objects that destructors of parents of myFixture
were waiting to free. If it does, is there a fix?
Can anyone think of a myFixture that would break this situation?
Note that a tearDown() would have the same issue, because the last line of
each tearDown() must therefor be a parent::tearDown() call, and these would
also leak their resources if an exception bypasses them. I suspect, for
example that you can't delete a TAO ORB that you did not first call
orb->init() on. So maybe all forms of tearDown() should aggressively trap
their own errors.
And the main reason I myself always don't throw out of destructors is the
ramifications are so gol-darn confusing. I'm, like, just a programmer; I
can't handle such complexity!
--
Phlip
http://www.greencheese.us/ZeekLand <-- NOT a blog!!!
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]