Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2003-03-22 09:52:07


Kevlin Henney <kevlin_at_[hidden]> writes:

> In message <u8yv7tv6e.fsf_at_[hidden]>, David Abrahams
> <dave_at_[hidden]> writes
>>
>>> 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.
>
> Not quite: there is a difference between the initial construction and
> the copy. In an insufficient-memory condition with a compiler that
> creates the exception directly, rather than creating and then copying
> it, the exception does not become active until the constructor has
> completed.

You don't think I know this?

Terje throws an exception, which causes copying. If the copy throws,
you go directly to terminate().

> If it does not complete, I believe that there is no cause to
> call terminate. MSVC is one such compiler, but BCC is not. In an
> insufficient-memory condition the former will result in a bad_alloc
> being thrown and in the latter a call to terminate.

No, you could run out of memory after initial construction but during
copying.

> Given that the workaround in Terje's code was in response to a VC6
> limitation (static data members of templates don't initialise
> correctly), it may make sense to conditionally compile the code so
> that the current code is used for VC6 and Terje's original is used
> for more capable compilers.

I have serious doubts that this is the best approach.

>>>> 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.
>
> Storing an array is not a reasonable option in this particular case,
> given what the code is trying to achieve. It is either impractical for
> the application if the bound is too low or can cause undefined behaviour
> if the bound is too high. In other words, unless (or until) the standard
> stipulates a limit it's the wrong design whichever way you look at it
> for this situation.

A limit for what?

>>There's no guarantee you have readable names anyway.
>
> That's a purely theoretical rather than practical consideration. In
> practice, I believe that all of the compilers that lexical_cast works
> under offer something relatively meaningful.

"relatively" is a relative term ;-) I guess if you don't mind reading
GCC's mangled names then I am forced to agree.

> The original version of lexical_cast simply stated, with a string
> literal, that the conversion had failed. There was some user demand
> for supplementary information, which Terje's version provided.

That shouldn't be formatted at the throw point.

> However, the decision as to whether this should be in the 'what' string
> is perhaps one that can be revisited. It would be feasible to avoid any
> allocation issues at all by leaving the human readable string as general
> as it was before and adding type_info members that described the source
> and target types.

Yes, that was my suggestion.

>>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().
>
> On the whole this is generally good advice, but it should not be
> treated as an absolute. Both lazy and eager evaluation have their
> place in design.

Not for exception what() messages when avoidable.

> If the effect is the same, it does not matter where the string
> is formatted.

The effect is not the same though.

> In this case, Terje's original intent was to use a static member to
> hold the representation, which would have resulted in formatting the
> string before the constructor, which is the critical point of
> failure.

How can a static string hope to hold arbitrary messages containing a
variety of type names? Or were you not planning to allow lexical cast
to be used in multithreaded programs? And given that you have to
format a different message each time the exception is thrown I don't
see how you can format the string *before* the constructor runs
without user-intervention! And even if you do, your code may end up
throwing bad_alloc when the intention was just to throw
bad_lexical_cast, and that can have undesirable consequences for
users.

> The changes required to make the code work under VC have
> made the invisible visible, so the effect is not identical in all
> scenarios.
>
> This leads to a number of possible options:
>
> (1) Use the static solution, as Terje originally intended, and use the
> current solution only for VC6.

-1

> (2) Rollback the design to something closer to the original, ie a fixed-
> string literal message as the result of calling 'what', and provide
> supplementary information for handlers that care enough to catch a
> bad_lexical_cast.

+1

I note that this solution provides a lot more flexibility for someone
who wants to report the error, since she is free to inspect and
massage the type names separately from the rest of the text
message, internationalize the surrounding text, etc.

> (3) Play around with C-style memory management and allocate arrays on
> demand, being sure to trap any bad outcome.

-0

> (4) Leave as is.

-1

(5) keep the current behavior for what(), but make it do formatting
    on demand inside a try/catch(...) block with a fallback message
    in case memory runs out, and store a shared_ptr<std::string>.

+0

-- 
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