Re: Destructor call of virtual base class - what happens with exception
spec?
James Kanze wrote:
On Sep 16, 1:13 am, Stuart Golodetz <b...@blah.com> wrote:
James Kanze wrote:
On Sep 14, 10:59 am, Vladimir Jovic <vladasp...@gmail.com> wrote:
James Kanze wrote:
On Sep 13, 9:52 pm, "Balog Pal" <p...@lib.hu> wrote:
"Johannes Schaub (litb)"
[...]
Does it matter when there is so wide consensus that dtors
shall not throw?
There is wide consensus that destructors usually should not
throw. There is even wider consensus that every rule may have
exceptions, and I have at least one class whose destructor
always throws.
Sounds like a hack. Can you explain why it always throws?
Because that's its defined semantics.
The class in question is used to support complex error messages
in exceptions. So you write something like:
error() << "xyz = " << xyz;
The function error returns an instance of the class, which
collects the output in a ostringstream, and throws the exception
with the generated output in its destructor. (It's actually
a bit more complicated, since you need support copy for the
return value of error(), and you only throw when the last copy
is destructed. You also have to check for other exceptions in
the destructor, and just let them propagate, without throwing
yours.)
Is there a good reason (other than terseness) for preferring that to e.g.
throw exception(stringbuilder() << "xyz = " << xyz);
?
Coherence with other error handling mechanisms. But you're
right that it's a bit obfuscating: someone reading the code
doesn't realize that code after this line is unreachable,
however, since the "throw" isn't visible. It's probably not
that good of an idea, although I've seen it used.
I guess it is mildly obfuscating, yes :-) I wasn't actually trying to
make a point, though, I was just curious in case there was some reason
to prefer it over what I do at the moment.
In my case, it's a bit more complicated. I have a singleton
class, ProgramStatus, which handles error output and the return
code (which should correspond to the worst error seen). For
fatal errors, I allow the programmer to define what happens: the
default is just to call exit, but in most cases, I'll replace
that with a function which throws an int. So if you call:
ProgramStatus::instance().setError( ProgramStatus::fatal )
<< ...
, the destructor will (very indirectly) throw. (When I set this
error handling, obviously, main consists of one big try block,
with a:
catch ( int returnCode ) {
return returnCode;
}
at the end. This has the advantage, compared to exit, that
destructors of local variables are called.)
Sounds quite cunning :-) So are you essentially doing something like
this (ignoring any minor syntax problems -- I'm just typing this out
rather than actually trying it)?
(I'm aware that I've probably messed something up in there, just
wondering whether that's the general gist? :-))
struct Error
{
virtual ~Error() {}
virtual void throw_me(const std::string& msg) = 0;
};
struct FatalError : Error
{
void throw_me(const std::string& msg)
{
// etc.
}
};
class ProgramStatus
{
//...
shared_ptr<Error> err_;
shared_ptr<FatalError> fatal_; // initialized elsewhere
Error& err()
{
assert(err_.get());
return *err_;
}
ThrowingStream setError(const shared_ptr<Error>& err)
{
err_ = err;
return ThrowingStream();
}
};
class ThrowingStream
{
//...
std::ostringstream os_;
template <typename T>
ThrowingStream& operator<<(const T& t)
{
os_ << t;
return *this;
}
~ThrowingStream()
{
if(!uncaught_exception())
ProgramStatus::instance().err().throw_me(os_.str());
}
};
Cheers,
Stu
--
James Kanze