Boost logo

Boost :

Subject: Re: [boost] [optional_io] InputStreamable refinement, Impacts Serialization
From: Andrew Troschinetz (ast_at_[hidden])
Date: 2009-02-25 15:56:00


On Feb 25, 2009, at 8:21 AM, Alexander Nasonov wrote:

> Andrew,
> sorry for the late reply.

Don't worry about it, I seem to remember you've been otherwise busy
with something just a tad bit more important ;)

> optional<T> is about initialized versus uninitialized value, it has
> nothing
> to do with stream's state. IMO, the failbit should be set.
>
> My vision of optional<T> streaming is based on this quote from
> documentation:
>
> "optional<T> intends to formalize the notion of initialization (or
> lack of it)
> allowing a program to test whether an object has been initialized
> and stating
> that access to the value of an uninitialized object is undefined
> behavior."
>
> Thus,
> - after a successful read the stream is in a good state and the
> value is
> initialized,
> - after read failure, the failbit bit is set and the value is
> uninitialized,
> - printing of initialized optional<T> value produces the same result
> as
> printing T value,

That's what you might expect, but in fact printing optional<T> is not
the same as printing T.

Printing optional<T> is the same as printing " " + T. An extra space
is added before the value of T.

> - printing of uninitialized value sets the failbit.

Again, things are not as you might expect. Printing optional<T> when
uninitialized shows "--" and does not set the failbit. I find this
particularly odd and would certainly prefer either the failbit being
set as you say, or for optional<T> to not be OutputStreamable at all.

> My streaming proposal makes lexical_cast < optional<T> >(v)
> equivalent to
> lexical_cast<T>(v) because it would never return uninitialized value.
> I can use it as a good excuse for implementing no-throw version of
> lexical_cast < optional<T> >(v) ;-)

Sorry it's been a while, which proposal where? Wouldn't implementing a
no-throw version of lexical_cast for optional<T> necessarily couple
lexical_cast and Boost.Optional?

In related news, I was briefly contacted by Fernando Cacciola (author
of Boost.Optional) who provided me with some insight about why
optional_io.hpp is the way it is. I hesitate to post verbatim a
private email I received from him on this mailing list, but I can
summarize what I learned. Near the first of this month he informed me
that optional_io.hpp initially came from the Serialization library and
its requirements were mainly directed by that.

I had proposed (and still do propose) this change to optional_io.hpp's
operator<<():

if (in)
{
     T x;
     if (in >> x)
     {
         v = x;
     }
     else
     {
         if (in.fail () && !in.eof ())
         {
             in.clear ();
             in.get ();
         }
         v = optional<T> ();
     }
}
else
{
     v = optional<T> ();
}
return in;

He agreed that it would work given my requirements, but said he
wouldn't dare change it without bringing in Robert Ramey to the
discussion, an author of Boost.Serialization and the original author
of the code in question. I wrote Robert soon thereafter with my
concerns but unfortunately I never heard back from him.

Since it's been some time, allow me to make more clear my reasoning
for this refinement.

Does the following seem a little weird to anyone else?
     stringstream sin ("test");
     optional<string> token;
     sin >> token;
     // token remains uninitialized.
     // But if sin had " test",
     // then *token would be "test"

And this:
     optional<string> s;
     cout << s; // shows "--"
But... isn't "--" a valid string?

Essentially, I think it would be optimal if streaming optional types
behaved as much like streaming the types they wrap as possible. And
since optional types can be in an uninitialized state, it seems
reasonable that failing to extract one from a stream should simply
return an uninitialized optional type without setting the failbit on
the stream.

Think about it this way: Isn't an uninitialized optional<T> a valid
optional<T>? And the failbit got set because we attempted to extract
*optional<T>::value_type* from the stream and couldn't. Does that mean
we failed to extract an optional<T>? What we failed to extract is a T.
Except we're returning a valid optional<T> even in this case. I think
that means we actually succeeded in extracting an optional<T>! Sure
it's uninitialized, but that's ok for an optional type.

I would also like to add that I like your idea that "printing of
uninitialized value sets the failbit." That seams a very reasonable
improvement as well.

But the catch is, I have no idea how these changes would impact the
Serialization library.

Robert, if you're out there, would you care to weigh in?

--
Andrew Troschinetz
Applied Research Laboratories

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