Boost logo

Boost :

From: Ed Brey (brey_at_[hidden])
Date: 2000-06-08 08:46:38


From: "Beman Dawes" <beman_at_[hidden]>
> >
> >template<typename T, typename U>
> >bool stream_convert(const T& source, U& target);
> >
> Interesting, although I have only been half following the
discussion.
>
> Couple of questions:
>
> * What exactly is the returned predicate condition? Or the reverse
> question, what exactly causes a return of false?

Sorry, I was writing based on an audiance fully following the entire
discussion. I should have made the proposal more explicit.

stream_convert returns true if the conversion succeeds and false if it
fails. An implementation would be:

template<typename Source, typename Target>
inline bool stream_cast(const Source& source, Target& target)
{
    std::stringstream interpreter;
    interpreter << std::boolalpha << source;
    interpreter >> target;
    return interpreter.good();
}

> * Can you summarize the full error handling rationale? In other
> words, what is the brief rationale summary that might be included in
> some documentation to indicate why errors are detected at all, why
an
> exception isn't just thrown, and then presumably finishes with your
> three points above.

Errors need to be detected, so that if the cast fails, the program
does not continue with garbage data. One form of error detection is
to throw an exception. This is especially useful when the same
handling mechanism is used for several operations which may fail.
However, it is combersom when there is a specific failure handling
mechanisms for each cast operation. [1] One form of error detection
that gets around this problem is to reserve a certain value of the
resultant type to indicate failure. However, this does not work in
applications where a successful cast could result in any of the values
of the resultant type. To get around that problem, an independant
value is provided to indicate success or failure. There are two
primary ways to accomplish that:

Target stream_cast(const Source&, bool);
bool stream_cast(const Source&, Target&);

In each case, the boolean value is true if the conversion was
successful and false if not. The second is chosen since the
application is likely to need for its own purposes a variable for the
target, but unlikely to need a variable for the conversion status
boolean. Finally, the function is named stream_convert so the preseve
the convention that only functions returning the resultant type end in
"_cast".

[1] Consider the structure of code that handles each cast failure
specially using exceptions versus programatic access. Since each cast
has a specific error handling code, in the throwing version, separate
try-catch blocks must be used.

int result1;
try {
    result1 = stream_cast(source1);
}
catch(const bad_stream_cast&) {
    // handle error in converting result1
}
int result2;
try {
    result2 = stream_cast(source2);
}
catch(const bad_stream_cast&) {
    // handle error in converting result2
}

int result1;
if (!stream_convert(source1, result1)) {
    // handle error in converting result1
}
int result2;
if (!stream_convert(source1, result2)) {
    // handle error in converting result2
}

Some additoinal analysis:

I think that there are valid arguments to both including and excluding
non-throwing versions: "Exceptions work just fine, and its only a bit
more code." and "Boy, doing an "if (convert)" sure is convenient."

Both methods have the problem that result must be default
constructable. That problem is inherent in a "stream"_cast
regardless, however, since the underlying stringstream has the same
limitation. On the other hand, stream_convert doesn't rely in the
target being copy constructable, not that anyone cares.

How much value is there in my attemping preseve a perceived convention
that only functions returning the resultant type end in "_cast"?
Another way to think of it is that all functions that only functions
that do conversion (casting) end in "_cast", regardless of whether
they "return" the result throw the return value or via reference.
Given that this would go into the boost::cast namespace, I'm starting
to lean toward the latter.

The three summary points in my previous post:
> >1. Doesn't require a reserved value of the resulting type.
> >2. Can easily be used directly in if statement to handle error
> >condition.
> >3. Doesn't take a parameter specifying the error handling action.
are useful for comparing stream_convert to a stream_cast that takes a
fallback parameter. However, when writing documentation for the big
picture, they aren't worth specially highlighting, given a rationale
such as the one above.

For whatever implementations we come up with, we can get a small
efficiency gain by specializing for the target type being bool, and
only then use the std::boolalpha adapter.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk