Boost logo

Boost :

From: Fernando Cacciola (fernando_cacciola_at_[hidden])
Date: 2002-11-25 16:34:02


----- Original Message -----
From: "Vincent Finn" <vincent_finn_at_[hidden]>
To: <boost_at_[hidden]>
Sent: Monday, November 25, 2002 12:16 PM
Subject: [boost] Re: Formal Review Request: class optional<>

> Fernando Cacciola wrote:
> > ----- Original Message -----
> > From: "Vincent Finn" <vincent_finn_at_[hidden]>
> >
> >>And now the question
> >>can this be used with VC6 ?
> >>
> >
> > Yes :-))
> > I've uploaded the new version which compiles and runs properly with
VC6.0
>
> Got it, thanks :-)
>
> But back to the other point
> I think it would be nice the have an optional that could be used for
> nice function arguments
> (the posts between you and Gennadiy give reaons I hadn't though of)
>
See my conclusions at the end of this post.

> I agree that it can be done using pointers and wrappers but so can the
> return value, optional is a wrapper for this type of behaviour so having
> a second wrapper for an almost identical purpose seems bad :-(
>
Actually, you cannot use pointers for optional return values unless you use
dynamic allocation and transwer ownership of the dynamic storage out of the
function.
That is, given:

T get_some_T()
{
  T result = .. ;
  return result ;
}

If you want to express that the return is optional and want to use a pointer
for this, you'll have to use something like:

T* get_some_T()
{
  T* result = new T(...) ;
  return result ;
}

Which has lifetime-management issues (the caller must remeber to call
delete), so you would actually write like this:

shared_ptr<T> get_some_T()
{
  shared_ptr<T> result ( new T(...) ) ;
  return result ;
}

Now, the above is *perfectly* fine as long as the dynamic allocation is not
an issue: that is, as long as you're not using it *just* to allow an
optional return type.
IOWs, from the user perspective, recieveing a pointer to carry a possibly
uninitialized result is quite natural an many well designed functions use
this idiom.

To express an optional *argument*, you can use a pointer (putting aside the
dereference-overhead performance problem). This is a well known consistent
idiom:

void foo ( int* optional_arg = NULL ) ;

Here, although T is a pointer, no dynamic allocation is required since the
address of a local variable can passed:

void bar()
{
  int a =2 ;
  foo(&a);
}

The wrapper (lvalue<>) I shown in my previous post is used here *just* to
accomodate callers who want to pass *literals* to optional parameters
expressed via pointers:

void bar()
{
  foo(&lvalue(2));
}

This wrapper is completely different from optional<>. It serves a completely
different purpose: to give a temporary storage to a literal so you can take
the address of it. It can be used to help passing literals to optional
parameters expressed via pointers, but can be used in many other contexts
totally unrelated to 'optional' values.

I personally think that using pointers to express optional parameters is a
very good idiom, quite used and quite well known. There is, as Gennadiy
pointed out, a subtle performance overhead, due to the indirection, for
which optional<> could be of help; but I wouldn't recommend using optional<>
for this task unless there is a real (profiled) need for the performance
boost.

So, we have a very good idiom to specify optional parameters: pointers.
But if we want to use the same idiom to *return* an object we need a
protocol to keep the object alive when it leaves the function (i.e. dynamic
allocation + lifetime management).
If the object to be returned is already allocated in such a way that can be
passed off the current function boundary, that is, if it is already
allocated on the heap and is already handled through a managed pointer (s.a.
std::auto_ptr<> or boost::shared_ptr<>), then I recommend to use the
managed pointer to return it.

auto_ptr<FredJr> get_a_fred() ;

void wilma()
{
  auto_ptr<FredJr> fred (get_a_fred());
  if ( !!fred )
    feed_him(*fred);
}

optional<> is aimed at combining the idiomatic power of a pointer with the
non-dynamic storage of local variables, it is essentially inteded to
complete this scenario for those by-value returns which would hit
performance if dynamically allocated just to express 'optionality'.
For example, one could rewrite the above snippet as:

optional<FredJr> get_a_fred() ;

void wilma()
{
  optional<FredJr> fred (get_a_fred());
  if ( !!fred )
    feed_him(*fred);
}

> Would some sort of specialisation be possible ?
> or a small wrapper class to allow the other syntax while leaving the
> current (safer) version as default?
>
Perhaps, but...

IMO,

The need for turning a "return-a-(managed)-pointer" idiom into
"return-an-optional" idiom is to avoid the dynamic allocation and the
lifetime management issues.
But OTOH, for optional arguments via pointes, there are no dynamic
allocation and lifetime management issues, so I see no benefit in using
optional<> over plain/managed pointers, except for [possibly] a performance
gain w.r.t to the pointer extra indirection.
I'm still unconvinced that using optional<> to express optional arguments is
a good idea. The potential performance gain shall be profiled, but
generally, only in those cases were the gain turned out to be significant
shall optional<> be used instead of pointers.
My feeling is that if optional<> would 'ease' its adoption as a general
idiom for expressing optional *parameters* it would quickly be used even on
those cases where the performance gain do not exist or is not too relevant.
But I tend to be too overprotective... I don't know.
As a rule of thumb, I don't recommend using complex (such as library aided)
idioms when the simpler idioms (such as based on languages primitives) work
just as well. In our case, optional<> is effectively simpler than pointers
for return types and the other usage cases I've shown, because of
dynamic-allocation and lifetime issues, but I think the same cannot be
stated for optional *arguments*.

Fernando Cacciola


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