Boost logo

Boost :

From: Fernando Cacciola (fcacciola_at_[hidden])
Date: 2001-09-13 17:48:47


----- Original Message -----
From: Gennadiy E. Rozental <rogeeff_at_[hidden]>
To: <boost_at_[hidden]>
Sent: Tuesday, September 04, 2001 3:21 PM
Subject: [boost] Re: Proposal: statefull objects

> --- In boost_at_y..., "Fernando Cacciola" <fcacciola_at_g...> wrote:
> >
> > ----- Original Message -----
> > From: Gennadiy E. Rozental <rogeeff_at_m...>
> > To: <boost_at_y...>
> > Sent: Monday, September 03, 2001 10:45 PM
> > Subject: [boost] Re: Proposal: statefull objects
> >
> >
> > > > > Hi, Fernando
> > > > >
> > > > > >
> > > > > > Hi Gennadity,
> > > > > >
> > > > > > > Hi, Fernando
> > > > > > >
> > > > > > [SNIP]
> > >
> > > If optional is implemented using pointers because type T does not
> > > have default constructor, I do not see a reason to use it at all.
> I
> > > would rather use plain pointer, where NULL would meen absent
> value.
> > > While for build-in types default constructor exist. If user-
> defined
> > > class T has a default constructor it is also worth while using
> > > optional<T> (value implemented) instead T*. You pay with default
> > > constructor call. You buy value access efficiency.
> > >
> > Actually, the point is not if T provides or not a default
> constructor. The
> > point is that you may want to bypass it because it might be too
> expensive.
> > Typically, you would use a true pointer in this situation, as you
> expressed,
> > but that involves an extra allocation/deallocation which is even
> worse than
> > the extra dereferencing:
> >
> > Consider:
> >
> > class font : windows::LOGFONT
> > {
> > font() ; // initialize with sensible defaults.
> > } ;
> >
> > Now, suppose you need an array of font records, but you don't want
> to
> > initialize every element since you will assign each element with a
> sensible
> > value later.
> > You have 3 choices:
> >
> > 1) vector<font> v(1000);
> > This calls 1000 unnecessary default constructors.
> >
> > 2) vector< shared_ptr<font> > v(1000)
> > You will have to call allocation/deallocation for each element
> later.
> >
> > 3) vector< optional<font> > v(1000)
> > Here, you skip the 1000 ctor calls and the
> allocation/deallocation
> > altogether.
>
> The difference between case 2 and 3 is much more subtle. Both version
> differ call for the constructor and destructor. The only real
> difference is that in case 3 you preallocate N*sizeof(T) bytes for
> future use, while in case 2 you allocate memory dynamically. It is
> not obvious what strategy is better and more efficient. Even more -
> using appropriate allocator policy you could have at least the same
> memory allocation strategy as with your optional. But also you can
> have different strategies.
>
But optional<> doesn't allocate anything (look at the implementation, it
uses placement new to copy the object in its own storage).

Its storage is managed entirely as a piece by the user.

Consider:

void foo()
{
   optional<font> array[1000] ;
}

There is really no allocation at all here (except for the stack frame).
So I don't think any dynamic allocation can compete with optional<> w.r.t to
memory management, not to mention the bargain of dealing with object
lifetimes (even usage of shared_ptr must be done carefully).

> >
> > Anyway, I understand your concern that for builtin and trivial PODs
> the
> > pointer implementation will add unnecessary overhead, but on the
> other hand,
> > with non trivial objects optional<> is better than smart_ptr<> in
> that you
> > skip the allocation/deallocation.
> >
> > Since I don't know how to support both versions without
> complicating things
> > (I don't like the policy-based approach, at least not yet),
>
> Why? Policies are supposed to be used to be able to encapsulate
> different implementation decisions: like kind of synchronization to be
> used, memory allocation strategy, value storage strategy any other
> behavioral decisions( like with iterator_adaptor - policy used to be
> able change iterator behavior). So even I do not see to much use for
> pointer based implementation for optional, this decision could be
> encapsulated in some kind of policy.
>
I wish it were so simple. But it actually isn't.
I can think of at least these problems:

1.
How do you defer these decisions and keep a consistent and well defined
behavior?
Currently, any policy class is inherently *an open door* which is difficult
to keep
constrained.

2.
How does a user write code to deal with optional<T> without having to get in
the trouble of considering all the possible variations? Remember that
optional<T,PolicyA> and optional<T,PolicyB> are two different classes, and
the only mechanism currently available to relate both is partial
specialization.

I still think a policy based approach will be too complex for the user.

Anyway, I come out with something that I think could address both concerns.
Look at my other message 'More on the optional class'

> > > > > > > [SNIP]
> > >
> > > > > C++ allows you to write int i = 3.14. If there is conversion
> > > between
> > > > > U and T ( so you can write T t = u;), we should be able to do
> > > this:
> > > > > optional<T> t; *t = u; In other case it will become even more
> > > strict
> > > > > limitation then DefaultContructable.
> > > > >
> > > > Actually, there is no limitation here. If there is a valid
> > > conversion
> > > > between U and T you can
> > > > always do: optional<T> t ; *t = (T)u ;
> > >
> > > First of all in this form it is 2 times more work. You will need
> to
> > > call T(U) and than T(T).
> > > That by itself sometimes could be
> > > inacceptable.
> > True. I note this as argument (1).
> >
> > > Another issue is that the difference is handwritten, th
> > > at will prevent usage of optional in some cases. Example,
> > >
> > > Imagine you have existent template function like this:
> > > template<typename T, typename U>
> > > foo( T const& t, U const& u ) {
> > > ...
> > > if( t != u )
> > > t = u;
> > > ...
> > > }
> > >
> > > optional<T> t; // was T* t; where NULL meant absent value
> > > U u;
> > >
> > > ...
> > >
> > > if( !t )
> > > foo( *t, u );
> > >
> > > Last line won't compile.
> > >
> > I note this as argument (2).
> >
> > Now,
> > argument (2) is a template code that performs implicit conversion.
> It is
> > essentially equivalent to a templated assignment or copy
> constructor.
> > Though this code has the advantage that skips the extra ctor used
> in the
> > explicit cast (argument (1)), it is also a *big* source of
> potential errors
> > precisely because of the implicit conversion used.
>
> You see, my concern is the back-compatibility. Let imagine we have a
> programmer that was using pointer T* to manage value that can be
> uninitialized. He has some existent code base. Now he want to switch
> from T* to optional<T> to enchance a value access time. But some of
> his functions where using expressions like *t = u. What do you think
> he will be doing? Change all over the places to add probably costly
> explicit cast (some of them could be inaccessable or third party) or
> change optional.hpp to support implicit coversions?
>
> I know what I would do.

On one hand, IMO, writing *t=appropriate_cast<T>(u) instead of *t=u doesn't
involve any design or implementation tradeoff; that is, the second form
offers nothing but less typing. Therefore, I consider it bad practice since
it sole benefit is to save a few keystrokes while its drawbacks are too
many - because the implicit conversion used may occur in unexpected places
(precisely because it is implicit)-.
I don't think is a good idea to support bad practice just to accommodate
unproperly written code.

AFAICT, implicit conversions are useful *only* in some overload resolution
scenarios. I don't foresee any overload situation when the implicit
conversion can be really useful.

On the other hand, I'm not sure if a piece of code using true pointers would
be converted into optional as you have shown. The reason is that such code
must have an allocation strategy, so it is not that easy to turn it into
optional since optional doesn't require external allocation/deallocation (as
a does a pointer).

Fernando Cacciola
Sierra s.r.l.
fcacciola_at_[hidden]
www.gosierra.com


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