Boost logo

Boost :

From: vicente.botet (vicente.botet_at_[hidden])
Date: 2008-04-01 15:21:02


Hello Vladimir,

----- Original Message -----
From: <Vladimir.Batov_at_[hidden]>
To: <boost_at_[hidden]>
Sent: Tuesday, April 01, 2008 2:17 AM
Subject: Re: [boost] Review Request: Generalization of the Pimpl idiom

> ...skipped the beginning which appears to be duplication of the previous
> email

Sorry but my first post was sent by error.

>> Could you clarify if the base implementation pointer can or can not be
>> initialized with a pointer to the implementation of the derived class?
> There
>> is something wrong in
>>
>> Base::Base(int k) : base(new BaseImpl(k)){}
>> Derived1::Derived1(int k, int l) : Base(new Derived1Impl(k, l))
>
> No, this initialization method is not allowed. One needs to use reset()
> instead as you show below. I considered the syntax above and rejected it
> on the grounds that it does not add new functionality (as reset() already
> does that) and it is not generic enough as it won't work for "Derived2 :
> public Derived1"

Why this syntax could not work? Could you elaborate more?

>
>> Base::Base(int k) : base(null())
>> {
>> reset(new BaseImpl(k));
>> }
>>
>> Derived1::Derived1(int k, int l) : Base(null<Base>())
>> {
>> reset(new Derived1Impl(k, l));
>> }
>>
>> Could a class inherit privately from pimpl?
>>
>> class A : private pimpl<A>::value_semantics {...}
>
> Well, I guess it can... although I am yet to see a compelling case to
> actually do that. It'll deny all the functionality that is in pimpl_base
> to the derived class(es).
>
>> There is something that seams extrange to me. If the pimpl is there to
> mask
>> the implementation, doesn't adds the public inheritance of
>> pimpl<A>::value_semantics some implemtation constraints?
>
> I cannot think of any particular implementation constraints

Imagine that after using the your pimpl class which will do an extra
allocation, and due to performance I need to come back to a traditional
class. If my class inherits publicaly from pimpl I will need to provide the
same interface as when using the pimpl class.

Before
class A : public pimpl<A>::value_semantic ...

After
class A {
    public:
    // needs to implement publicaly the null function, bool_type operator,
    // and the ! operator.
    protected:
    // needs to implement all the protected part inherithed from
pimpl<A>::value_semantic

}

Do you really think that the pimpl idiom is helping me to hide my
implementation. If I want to hide my implementation I need to restrict to
private inheritance.

>> Note that public inheritance adds to the class interface the null()
>> function, bool type conversion and ! operator, and adds also all the
>> protected functions to the derevied classes.
>
> I am not sure I understand. Is it a bad thing?
>
>> Can we found always a null object for every class when pimpl is not
> used?
>
> null<T>() has boost::enable_if_c<is_pimpl<T>::value, T>::type so my
> implementation only kicks in for pimpls.

Once we remove the public inheritance from pimpl, it is not eassy to
implement all these extra functions and operators.

>> Can a class that inherits from a non pimpl class has its own private
> impl?
>>
>> class A;
>>
>> class B : A, public pimpl<B>::value_semantics {...}
>>
>> How the B implementation has access to A?
>
> I am not sure it is a good (from pimpl perspective) design. Pimpl is about
> separation of interface and implementation and implementation hiding. By
> inheriting from A we just threw all that out the window. So, doing that
> for B implementation does not seem to have much value. Having said that,
> if one indeed needs access to A inside B implementation, then keeping B*
> inside B implementation seems to solve the problem. With regard to
> handling polymorphic hierachies the Bridge description from GoF would be a
> good start. That'll be fully applicable to the proposed pimpl.

Keeping a A* inside B implementation will complicate a bit the B
implementation, isn't it?

>> maybe we need something like that
>>
>> class B : public pimpl<B, A>::value_semantics {...}
>>
>> making the implementation of B inherit from A.
>
> "making the implementation of B inherit from A" is
>
> struct pimpl<B>::implementation : public A
> {
> }
>
> That certainly makes sense (to me anyway).

How do you ensure this way that B inherits from A?

>
>> Could we inherit publicly from A and privatly from pimpl<B,
>> A>::value_semantics?
>
> Cannot comment on this one as I am not convinced we need that (see above).
Imagine A3ppClass is a 3pp class ( we can not modify it), and before using
the
your pimpl class we had:

class MyClass : public A3ppClass {};

How can we use pimpl without changing the interface of MyClass, neither
adding nor removing any function?

May be we can resume: do not use the pimpl library is you can not master the
complete inheritance hierarchy.

>> Can we manage with multiple inheritance?
>> class A : public pimpl<A>::value_semantics {...};
>> class B : public pimpl<B>::value_semantics {...};
>> class C : public A, public B {...};
>
> Sure... with all the relevant multiple-inheritance perks/drawbacks.

Imagine that I have up to now
class A;
class B;
class C : public A, public B {};

and that I have no problems with multiple-inheritance because I have no
common class on the inheritance hierarchy (even if I don't like it very
much).
Now I'd like to use your pimpl classes and now a found a common class, or
atleast some common functions or operators. Does it means that again I need
to
use private inheritance.

class A : private pimpl<A>::value_semantics {...};
class B : private pimpl<B>::value_semantics {...};
class C : public A, public B {...};

May be we can resume: do not use the pimpl library if you need multiple
inheritance.

>
>> How pimpl interacts with STL containers?
>>
>> stl::list<A> al;
>> B b;
>> al.push_back(b);
>> // ...somewhere else
>> A a =al.pop_back();
>>
>> How 'a' should behaves? like a A or like a B?
>
> I presume
>
> struct A : pimpl<A>::... {}
> struct B : public A {}
>
> Then the result depends if A is of value_semantics or pointer_semantics.
> With pointer_semantics it behaves as, say, boost::shared_ptr would behave.
> Otherwise, during copying B part will be chopped off. So, "A a" will
> behave as A.

OK, I have missed that. Does it means that only pimpl<A>::value_semantic
is sustitable for a class which was already used with STL containers?

>> The STL container user that
>> shouldn't knowns nothing about A implementation, expect that it will
> behaves
>> as a A, but it think that 'a' will behaves like a B. How can we protect
> from
>> this situation?
>
> I probably do not understand something as I still do not see what it is to
> protect from. A pimpl-based class behaves as any ordinary class (as it is
> in fact an ordinary class).
>

May be we can resume: do not use the pimpl pointer semantics if your class
has value semantics.

In order to understand better your purpose:
* Could you give an example in which the object has value semantics and the
implementation pointer semantics?
* Could you give an example in which the object has pointer semantics and
the implementation value semantics?

>> Could we store a non polymorph pimpl object in shared memory?
>> Could we store non polymorph pimpl objects in a container in shared
> memory?
>
> I am not convinced these special cases require anything from the pimpl.
> Although these certainly need more effort from the developer. Issues and
> means to deal with those seem to be the same as if one tried to store a
> pointer or std::auto_ptr or a boost::shared_ptr in shared memory. Given
> it's all in the developer's hands (implementation can be allocated
> explicitly and then associated via reset()) it'll be the developer's pain.
> This seems to be merely part of overall painful experience of dealing with
> shared memory. :-)

You are right, auto_ptr and shared_ptr do not work on shared_memory. The
difference is that as you state a pimpl is not a pointer, and so we should
not suffer from the pointer liabilities. When a user use std::auto_ptr or
boost::shared_ptr he knows that there are some pointers not too far. In the
case of the pimpl the user has nothing to be with the pimpl classes and its
pointers.

Imagine that you are using a class A in shared memory and even a container
of A in shared memory and you want now to use your pimpl class to hide the
implementation of A. What I can do?
Do not forget that you have two pointer member?

template<class Interface>
template<class Implementation>
class pimpl<Interface>::impl_ptr
{
...
    traits const* traits_;
    Implementation* p_;
};

May be you need to add some policies to your class in order to manage with
this (see for example void_pointer in Boost.Intrusive)?

May be we can resume: pimpl library can not be used for classes that can be
stored in shared memory (atleast in his current state).

>> I hope that you will find good solutions to these problems.
>
> So, do you find my answers to your satisfaction?
>

Not completly, but I hope that this discusion will serv to construct a
better
pimpl library or atleast to show the limits where this library is not really
adequated.

>> In any case, I
>> will apreciate that you include in the library documentation the
> limitations
>> of the approach, if there are any.

> Thanks,
> Vladimir.

The question is, can we subtitute a class A by a class A : public
pimpl<A>::xxx_semantics? I think that it will be better to restrict the null
object and the bool type operator to the pointer semantics.

What do you think?

Best regards
_____________________
Vicente Juan Botet Escriba


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