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