{sorry if this appears twice... but the last post didn't seem to go thru}

Aleksey Gurtovoy wrote:

> Fernando Cacciola wrote:
>> OK, I can see the motivation: We can have a noncopyable class
>> and need an optional object of it.
>> Following optional semantics, it would be spelled:
>>
>> boost::optional<RAII_lock> lock;
>> if ( cond )
>>   lock.reset( RAII_lock(entity) ) ;
>>
>> But there is a probem: as William pointed out, reset() needs
>> to use T::T(T const&) in order acquire its own copy of the object;
>> but in this case is noncopyable, so the above won't compile.
>
> Yep.
>
> [...]
>
>>> * Secondly, you would need a templated constructor to allow T to be
>>> constructed using another type.
>>>
>> Exactly.
>> One possibility would be add additional templated ctor and reset
>> that just forward the arguments to T's contructor (optional's interface
>> could use a trailing tag to differentiate these).
>>
>> A problem with this is that the Argument Forwarding problem is burried
>> into optional's itself.
>
> I think it's more of a language problem than anything else. I mean, there
is
>
> nothing conceptually wrong with allowing 'optional' to support something
> like 'opt.construct(a1,...,an)', IMO; only implementation ugliness.
>
Agreed.
Though the problem still exist in practice. I'll get back to this below...

>
>
>> Another approach would be to create a generic delay-factory
>> object and just let optional use it:
>>
>> Something like:
>>
>> template<class T, class A0>
>> struct in_place_factory0
>> {
>>   in_place_factory0 ( A0& a0_ ) : a0(a0_) {}
>>
>>   T* operator() ( void* address ) const { return new
>> (address) T(a0) ; }
>>
>>   A0& a0 ;
>> } ;
>>
>> template<class T, class A0>
>> in_place_factory0<T,A0> in_place ( A0& a0 ) { return
>> in_place_factory0<T,A0>(a0) ; }
>>
>> which requires the following changes in optional<>:
>>
>>   template<class T>
>>   class optional
>>   {
>>     public :
>>
>>       ....
>>
>>       template<class Factory>
>>       explicit optional ( Factory f )
>>         :
>>         m_initialized(false)
>>       {
>>         construct_inplace(f);
>>       }
>>
>>       ...
>>
>>     private :
>>
>>       template<class Factory>
>>       void construct_inplace ( Factory f )
>>        {
>>          f(m_storage.address());
>>          m_initialized = true ;
>>        }
>>   } ;
>>
>> The above would allow something like this:
>>
>> optional<RAII_lock> l( in_place<RAII_lock>(entity) );
>>
>> If the same in-place idiom is used on reset(), the complete
>> solution will look, following optional<>'s way:
>>
>> boost::optional<RAII_lock> lock;
>> if ( cond )
>>   lock.reset( in_place(entity) );
>>
>> What do you think?
>
> Well, it's a little too verbose and un-idiomatic than I would hope for.
I see.

>
> May be my original desire to re-use 'optional' in this context was
> ill-conceived, and what I really need is a separate 'optional_lock'
class -
> which, although definitely similar, will be significantly simpler than
> 'optional' itself. Although
>
>     boost::optional<scoped_lock> lock(cond, entity);
>
> definitely seemed so intuitive and close.
>
> Aleksey
>
Well, even if a separate class is better for your purposes,
you've raised a very good point, and one way or another
I'd like to support this usage in optional<>.
Thus, I'd like to further discuss the posible interfaces
(others really welcome).

I started considering the interface you suggested and even implemented
and played with it, then I spoted the following issues:

(1) I feared that the syntax could look ambiguous:

struct X : noncopyable { X ( bool, int, char const*) ; } ;
struct Y { Y ( bool ) ; } ;

void foo()
{
  optional<X> opt1(true,false,2,"hello"); // [1]
  optional<Y> opt2(true); // [2]
}

In [1], in-place construction is used, so the first parameter is the
condition and the other three are the contained object's parameters.
In [2], in-place construction is not used and instead a temporary Y is
created
and stored.

I initially thought that the in-place form would have to always take more
than one parameter: the condition and the in-place ctor argument.
If this were true, one could tell one form from the other unambiguously
(and this is important because in the in-place form the first parameter
is related to the initialization state of the optional instead of the state
of the contained object).
But it isn't: what about a non-copyable object with just a default ctor?

struct Z : noncopyable { Z() ; } ;

void bar()
{
  optional<Z> opt1(true); // in-place form
  optional<Y> opt2(true); // copy form
}

Then I thought that a solution could be to have a tag identifing the
in_place form:

void baz()
{
  optional<X> opt1(true,false,2,"hello", in_place );
  optional<Y> opt2(true);
  optional<Z> opt1(true,in_place);
}

This way, one can tell clearly when the contained object was being
constructed
in place.

{...then half my neurons died and I felt convinced that the first 'cond'
parameter could be mistaken by a T's ctor argument and decided to remove it
:-o}

But today -back on my feet- I see that the protection is silly (I'm
overprotective, am I not?),
at least with any sort of interface that clearly differentiate the in-place
and the copy forms.

But there was still another issue:

(2) Flexibility

Which forwarding technique shall I use? If the in-place ctor takes arguments
by const reference you can pass const rvalues as in:

 optional<X> opt1(true,false,2,"hello",in_place);

but then you can't pass non-const lvalues as in the lock example.

If optional's in-place ctor takes non-const references, you can use
it for cases like the optional lock:

entity e ;
optional<lock> opt(true,e,in_place);

but then you can't pass literals as before.

Additionally, how many forwarded arguments shall optional support?
... Murphy will make sure a new user will request support for yet another
argument
(though this could be solved using BOOST_PP and a user-defined limit)

(3) variant<> dependency.

We are just about to review boost::variant<>.
I was planning to wrap optional<> around variant discarding its current
implementation.
However, to support the in-place form and use variant<>, the later will have
to suppport this form itself.

Proposed solution:

I realized that from the user POV, there is little syntactical difference
between:

optional<lock> opt(true,e,in_place);

and

optional<lock> opt(true,in_place(e));

so I figured that a factory could move the forwarding problem out of
optional<>
while at the same time allow for an almost identical usage.

{...actually, I've dropped the 'cond' parameter in my original thoughts and
post,
but it was unnecesary...}

This form is just a little more verbose than the direct form
(optional<lock> opt(true,e)), but has the following nice features:

It allows to use in-place for noncopyable default-constructible objects:

optional<Z> opt(true,in_place());

It allows you to have alternative factories which support alternative
forwarding methods:

optional<X> opt(false,in_place2(2,"hello"));

And since the factory can be reused, plugging the mechanism into variant<>,
for instance, is trivial.

Notice that the verbosity is actually put in the in-place factory mainly.
I would implement and document such factory as another utility and just
use it on optional<> interface.


P.S: I'd really like to hear others opinions about this,
specially from Eric Friedman and Itay Maman
(the variant submitters).

Fernando Cacciola


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Reply via email to