|
Boost : |
Subject: [boost] [Somewhat offtopic] Re: [C++11] If you have an old class with a throwing destructor...
From: Niall Douglas (s_sourceforge_at_[hidden])
Date: 2013-09-16 17:01:02
On 15 Sep 2013 at 18:25, Eric Niebler wrote:
> > Sorry, my unclear phrasing again ... by wrapping destructors I
> > specifically meant:
> >
> > destructor::~destructor()
> > { try {
> > ...
> > } catch(...) { /* do something useful */ } }
>
> Can you elaborate on what "something useful" is in this context?
Sure.
A bit more than a decade ago I took two years out to implement a
next-gen software platform. It conceptualised GUI components as a
distributed, adhoc, semantically generated graph of data stream
processors, so think of something like Boost.iostreams spread out
over disparate compute units and whose relationships were described
by a triplestore, and you've got a good idea (if it sounds like Plan
9, you're not wrong).
One of the big problems in the client-server relationship was
exception propagation. If I ask for an instantiation of an editing
processor for a Word file, and I am not on a Microsoft Windows
machine, the framework automatically found a nearby machine with the
requisite Word COM objects and simply plugged together the needed
bits code from the semantic graph database. If the Word COM object
threw a COM exception, that got converted into a C++ exception and
propagated over the asynchronous RPC channel such that it pops out
locally. Because it's asynchronous, it pops out only at certain
points i.e. when you next talk to the relevent item (Boost.AFIO does
exactly the same BTW).
The problem is what happens when exceptions cascade. In the above
situation, an exception from COM could cause a chain of bits of
intermediate processing to also throw exceptions, and they had a
nasty habit of "popping out" via propagation during object
destruction as often client code hadn't done anything in between to
otherwise retrieve the asynchronous exception state. In this
situation, we really *want* exception throws during object
destruction to NOT call std::terminate() because no unsafe and ill
defined program state has occurred.
And hence the solution I mentioned earlier which was a script to add
try...catch to all destructors, and if an exception was already in
flight, it appended the new exception to the existing one as a nested
exception and it retried the destructor. Later on, my additions to
the C++ runtime would spot the situation and figure out if the nested
exception throws created indeterminate program state - in which case
now we fatal exit - or if the destructor retry was successful, and
the party can continue.
Most would say a better solution would have been the make the
throwing destructors private and force people to use a virtual
Destroy() function. On the hand, that's not very C++-ish, and my
solution did work very nicely so long as you didn't mess up retryable
destructors. I also hooked management of the Python GIL into the same
framework, so one could traverse between C++ and Python and back into
C++ and so on. It worked well.
Lots more info on nested exception handling at
http://tnfox.sourceforge.net/TnFOX-svn/html/class_f_x_1_1_f_x_exceptio
n.html for those really interested.
Niall
-- Currently unemployed and looking for work. Work Portfolio: http://careers.stackoverflow.com/nialldouglas/
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk