From: David Abrahams (dave_at_[hidden])
Date: 2003-03-22 11:38:56
Terje Slettebø <tslettebo_at_[hidden]> writes:
>> Terje Slettebø <tslettebo_at_[hidden]> writes:
>> >>From: "David Abrahams" <dave_at_[hidden]>
>> >> Are you saying that you have defined an exception with a std::string
>> >> member? That's VERY bad! Throwing that exception can lead directly
>> >> to termination!
>> > You mean if the exception itself throws during construction or copying?
>> > I've tried the program below on Intel C++ 7.0 and MSVC 6, and I haven't got
>> > it to call terminate(). It may be that it doesn't handle exceptions
>> > correctly, though.
>> Because you are not running in a low-memory condition.
> What difference would that make? In the example program, it throws an
> exception from the exception constructor, effectively simulating an
> out-of-memory exception.
Replace operator new so that it throws unconditionally *after* the
exception object is constructed. If you try this on an implementation
where the exception object gets copied during unwinding, it will
terminate(). It's the copy ctor that matters in this case.
>> > As it stands, it prints "Exception - Constructor", as it throws
>> > an exception in the constructor of the Exception exception. If
>> > the throw-statement in the constructor is commented out, it
>> > prints "Exception - Exception", apparently not invoking the copy
>> > constructor when throwing the exception (which it's allowed to).
>> Irrelevant. A program that invokes undefined behavior may appear to
>> work fine also.
> You said that it may terminate the program. I assumed from that that you
> meant it would call terminate().
Yes. If the copy ctor were called, which as you say it's allowed to.
> Did you instead mean undefined behaviour?
> If so, how? And how would that be related to terminating the
> program? As you say, anything can happen with undefined behaviour,
> including the "expected" behaviour.
It's related in that there are any number of ways an incorrect program
may appear to work correctly. I assert that any program throwing
exception objects containing std::string members is incorrect (for
some reasonable definition of "incorrect").
>> >> What's wrong with char const*?
>> > You mean storing the string as a character array?
>> No, I mean not storing the string at all (char const* is not an
>> array), but storing an array is another option.
> Yes, I know that, of course. However, since if you just store a
> pointer, the string has to be allocated some other way. How would
> you allocate it?
Just use a string literal; the compiler has to allocate that as part
of the program image.
> If you use "new", won't that bring the same potential problem as
> std::string? This is why I thought you might have meant storing an
> array (as STLPort does it), rather than storing a pointer. Storing
> an array eliminates the possibility of throwing an exception at
Not exactly, but anyway...
Throwing at construction is problematic, but it's not the big
problem. The big problem happens when you throw during copying.
>> There's no guarantee you have readable names anyway. Finally, you
>> should never format exception messages at the throw point. An
>> exception whose what() needs to print type names should store the
>> typeids and put the formatting logic in what().
> A problem is that what() can't throw, either. So you'd have to wrap it in a
Yes, if you insist on trying to format a string there.
> Then there's the issue of what to return from what() if the
> formatting throws.
Yes. You'd need some fallback strategy. Big deal; either you
implement the fallback or all your users do.
> As Kevlin says in another posting, the "terminate on second
He never used that phrase. What do you mean?
> which I thought you alluded to in your first reply, and maybe you
> did, may not apply at the point of construction, since it only
> applies after the evaluation of the throw-expression. In other
> words, if the construction fails, you only loose the first
> exception, and it instead throws the new one.
I am aware of how C++ EH works, really!
> As for throwing in the copy constructor, that might be a problem,
> since the "call by value" semantics of throwing an exception may
> mean that it makes a copy of the exception, after the
> throw-expression is evaluated.
> The reason the extended error type was added, was that there has
> been requests on this list for storing the types used in the
> conversion, in the exception, to make it easier to know which
> conversion failed.
That's a good request, but you didn't do that, did you? Formatting
them into the what() string is a bad idea unless you are very careful.
-- Dave Abrahams Boost Consulting www.boost-consulting.com
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk