Re: [boost] Review Request: Generalization of the Pimpl idiom

...skipped the beginning which appears to be duplication of the previous email
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"
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
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.
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.
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).
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).
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.
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.
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).
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. :-)
I hope that you will find good solutions to these problems.
In any case, I will apreciate that you include in the library documentation the
So, do you find my answers to your satisfaction? limitations
of the approach, if there are any.
Thanks, Vladimir.

Hello Vladimir, ----- Original Message ----- From: <Vladimir.Batov@wrsa.com.au> To: <boost@lists.boost.org> 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
participants (2)
-
vicente.botet
-
Vladimir.Batov@wrsa.com.au