Boost logo

Boost :

From: Fernando Cacciola (fernando_cacciola_at_[hidden])
Date: 2003-09-01 11:41:38


Joel de Guzman <djowel_at_[hidden]> wrote in message
news:00a701c36f63$c17399d0$64646464_at_godzilla...
> Fernando Cacciola <fcacciola_at_[hidden]> wrote:
> > Hi Mat,
> >
> > Thanks for the input.
> >
> > optional<> is now out on the field and it is the only utility of its kind
> > I've ever seen in C++, at least in real use. This has the drawback that there
> > is very little experience with it. Therefore, in a way it is a sort of
>
> There's a lot of experience with it in other languages. Why not leverage that?
> Haskell::Maybe for instance.
>
Do you know of anything else besides Haskell?
I don't, and I took the time to investigate Maybe looking at Haskell
programs using it.
As Brian pointed out, it is not so easy to port Maybe to C++ because
its usage is extensively supported by Haskell features that are
not present in C++.
The most fundamental point is that being Haskell a pure functional
language there is no possibly undefined behaviour to worry about,
so Maybe doesn't need to address this issue as optional<> does.

>
> > experiment, even though it is now a fixed part of Boost.
> > optional<> will evolve, that's for sure, as user's experience show which
> > design desicions were right and which were wrong.
> > I might even deprecate it and submit a replacement if it becomes clear that a
> > drastic change is neccesary.
> >
> > Right now I still believe that the pointer-like interface is correct, because
> > I add it specifically to solve a real problem and it did solve it.
> > My first optional (named differently then) had the problem that it was really
> > difficult to track which variables were 'optionals' and hence it was easy to
> > forget to make the neccesary steps to mantain integrity (not to use an
> > uninitialized optional). At some point I realized that I needed something at
> > the syntax level that help me keep in mind that those variables are possibly
> > uninitialized, and so I moved from value() member functions to operatos * and
> > ->
>
> If that's the only rationale for having the pointer-like interface, then pardon
> me but, I find that very flimsy. There should be other means to attack the
> problem (if it is a problem) without sacrificing the beauty of the interface.
>
I account the possibly undefined behavior of accesing an uninitialized
optional as a real and important problem.

>From now on I'll refer to this as the
Possibly Uninitialized Problem: the PU problem in short.

The interface, in one way or another, should address this.

> > The pointer-like interface is just about syntax really.
> > Instead of opt.value() you type (*opt) or opt->
> > This alone shouldn't be a problem.
>
> Right.
>
> > What I think it is a problem are other design descicions that I made in order
> > to make other parts of the interface with the pointer-like interface. Namely,
> > the lack of implicit construction and direct assignment.
>
> Sacrificing this in order to "track which variables were 'optionals'" ?, IMO,
> is not a good idea.
>
I agree.

>
> > These two points are raised here by Mat and have been raised by many others
> > in the past.
> >
> > My main argument is that if those were allowed, you could write:
> >
> > optional<int> opt ;
> > opt = 1 ;
> > int i = *opt ;
> >
> > and the assymetry didn't look right to me.
>
> If optional had value semantics, it would be a lot cleaner:
>
> optional<int> opt;
> opt = 1;
> i = opt;
>
First of all, let's not confuse syntax with semantics.
optional<> HAS strict value semantics.

If the choice of 'operators *', 'operator ->' and 'get()'
as value accessors makes the appearance that is has pointer
semantics, then it is the documentation that has to be fixed,
not the interface.

vector<>::begin returns an object with operators * and ->,
yet these objects are not pointers, and once that is learned,
people do not think they are pointers.

Also, let's not think of the pointer-like interface as a unity:
there are three items involved, namely *,-> and get().
We'll be better off considering them separately.

Direct initialization: opt = 1
seems right since this operation is never undefined.
This would mirror variant's interface.

Direct value accesing via implicit conversion: int i = opt
seems wrong because this is the operation that can lead to undefined
behaviour.

> For those concerned about "track"ability, they can use an easy to
> grep alternative:
>
> optional<int> opt;
> opt .reset(1);
> i = opt.get();
>
Addressing the PU problem via a 'get()' function returning a _reference_
seems right, but OTOH, using operator * seems even better because
the expressions of the form (*a) have a very familiar meaning
and is that meaning _exactly_ what we need here.
But don't let the rest of the optional<> interface get in the way
of this argument. If we agree that _some_ explicit function/operator
is needed as a value accesor, then in itself, operator* is IMO clearly
the best choice.

> > Now I understand that generic uses of optional<> beg for conventional
> > 'syntax' for assignment and initialization.
>
> True!
>
> > I could drop the pointer-like interface but only if replaced by some member
> > function that aids to the fact that the value being accessed may be
> > undefined, that is, I know from previous experience that the following code
> > better be invalid:
>
> That's a very reasonable solution.
>
> [snip]
>
> > Today, on the face of all the versions I've tried and the user report I have
> > now about the uses you want but are unsupported, I will carefully think about
> > adding direct initialization and implict construction yet retaining the
>
> To be honest, I'm not really so sure about implict construction.
>
Why? variant supports implicit construction, FWIW.
The key is not to provide an implicit convertion to T at the same time.

> > pointer like interface which is intended to be a familiar 'syntax' for
> > _accessing_ the optional in those contexts were uninitialized optionals have
> > undefined behavior.
>
> Is the pointer like interface really worth keeping?

I think the right question is: is each of the value accessors
operator*, operator-> and T* get() the right choices for what
they provide?
The answer you be given for each of them separately.

>That's the point Mat was trying to make, right?

Not really.
He complained about the pointer-like interface because _other_ parts
of the interface were wrong and the pointer-like part was used as an
excuse.
If optional<> supports implicit conversion and direct initialization,
as variant does, then Mat's problems are gone, and there is no need
to drop the pointer-like interface, at least not all of it.

> IMO, I can't see a reasonably balanced rationale, yet.
> The pointer like interface sacrifices a lot for a small gain:
>
Actually, the pointer-like interface per see does not sacrifice
anything.
The other related decisions do. So let's fix _that_ instead of
dropping what I believe is a useful part of the interface.

> to make it easy
> to detect automatically. For those who are concerned that this is indeed a
> problem, why son't they just use: opt.get(). Surely, that's very easy to track.
>
opt.get() is easy to track but it doesn't tell anything to the eye.
*opt and opt->foo(), OTOH, convey very clearly the existence of
possibly undefined behaviour.
The current get() is another story. I agree that it should return a
reference, but I'll address it in more detail in another post.

Fernando Cacciola


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