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

...
That requirement to have
Derived1::Derived1(implementation*)
is unreasonable as it is clearly not related to to the Derived1 *public* interface. On the other hand reset()-based approach works fine and scales fine.
...
I understand now. This will force to add an artifial *protected* constructor to Derived1. Do you really think that this is a real problem for the developer of Derived1? If I understand your library forbids this to the developper because pimpl_base do not have the following protected constructor. pimpl_base (implementation* impl) : impl_(impl) {}
I think that this will be a good adding to the pimpl_base class.
Respectfully I have to disagree. The reason is that it has been long established precedent in C++ not to provide alternative mechanisms/tools/interfaces if the existing already does that. An immediate example OTOH is that an introduction of "superclass" keyword was rejected because a typedef would do the same thing. Granted, tediously and not as elegantly but still. Here IMHO it's the same story -- we are not talking about missing functionality but rather questionale interface sugar-coating which BTW requires the user to do more than he really have to ("force to add an artifial *protected* constructor"). ...
I am not sure what that is supposed to demonstrate. You use infrastructure to help you to do your job. If you decide that the infrastructuere does not help you and discard that, you'll obviously have to re-implement whatever functionality from that infrastructure you managed to deploy. If one uses std::list and then for whatever reason goes ahead with his own list implementation or merely swaps it for std::vector, he'll likely to face the same issue. Trade-offs of the pimpl idiom are well-known -- another indirection and memory allocation. In the end, one can deal with memory-allocation by writing a custom allocator for pimpl<Foo>::implementation.
If pimpl is here to hide the implementation, why it needs to extends the
interface of the initial class? Why the user of my class should suffer from the trade-offs of the pimpl idiom at the inteface level?
Pimpl extend the interface so that the user does not have to provide those methods. Like comparison and validity check, etc. That does not come at the user's expence and, therefore, how exactly the user suffers from that escapes me.
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.
I cannot say about you but I do really think that the pimpl idiom is helping me to hide the implementation. Private inheritance restricts access but not hides the implementation.
I was talking about private inheritance from pimpl!!!
struct Base { int k; }; class Derived : Base {}
Here Base::k is not accessible in Derived but not hidden. More, if
If you try deploying pimpl in applications, you'll quickly see that you'll need public inheritance. I do. Having said that, if you insist on inheriting privately, there is nothing stopping you. We are not discussing missing pimpl functionality or crooked implementation, right? that
stuff is a commercial library, I can easily circumvent that restriction by modifying Derived declaration. Pimpl really hides the implementation. See for more about the idiom
1. Guru of the Week #24. http://www.gotw.ca/gotw/024.htm 2. Herb Sutter. Exceptional C++ (Addison-Wesley, 1999) 3. J. Carolan. Constructing bullet-proof classes. In Proceedings C++ at Work'89 (SIGS Publications, 1989) 4. James O. Coplien. Advanced C++ Programming Styles and Idioms (Addison-Wesley, 1992) 5. Eric Gamma et al. Design Patterns (Addison-Wesley,1995)
This is why I suggested to remove the null object and bool type conversion operators for pimpl<A>::value_semantics. Maybe it will be a good idea to give them protected acces.
Once we remove the public inheritance from pimpl, it is not eassy to implement all these extra functions and operators.
Then do not remove public inheritance. I am far from being convinced
insistence on private inheritance is justified.
Conveinced or not this is a way to mask the pimpl functions, and it seams to me the best way to use pimpl<A>::value_semantics. This is not the case with pimpl<A>::pointer_semantics from which it is better to inherit
I admit that "This is why" escpes me. Invalid objects are all over -- nullptr, shared_ptr(), -1, etc. Pimpl extends that concept onto actual classes and gives it uniform and proper implementation. I've been using this extensively and I do indeed need that... public. If one does not need that, he won't use that. That feature won't kick in by accident. that publically
giving acces to the null object and the bool type conversion operators. This is only a comment on how I will use it :-)
Yes, that makes sense. Although if my usage experience is of any guidance, you'll swap private for public fairly soon. I guess, we'll see. ...
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. ... In any case I don't see a real benefit of using pimpl when the base class is 3pp class and we can not change it.
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.
I cannot see anything drastically wrong with the code above even
Well, if you build a run-time polymorphic hierarchy on top of an existing 3rd-party base class, then I tend to agree with you as you have to follow the design and strategy outlined by the base class designer. I do not feel like it is a pimpl inherent weakness. Havind said that, I'd shy away from coming to your generic conclusion based on that particular case. Unless I have to write deep run-time polymorphic hierarchies (like Widgets, etc.), I tend to cut inheritance short and I believe it is the strong current programming trend in C++. Therefore, in your case I'd try pimpl<MyClass>::implementation : public A3ppClass {} then in MyClass I'd bridged to A3ppClass only the interface that MyClass needed: MyClass::foo() { (*this)->A3ppClass::foo(); } It might not be applicable in your particular case -- no one claims to have the silver bullet. ... though I
probably would not do it that way. So, I'll have to disagree with your conclusion.
Anyway, we will do the same, don't use pimpl with multiple inheritance.
I would not say that I will not. If/when the time comes for me to deploy multiple inheritance, I cannot see myself hesitating because of the pimpl. ...
I have to disagee as I find that view over-simplified and in fact incorrect. Pimpl or no pimpl in general no objects beyond value-semantics PODs can be safely (or without additional effort) put into shared memory.
I hope so, and I expect that you will do what is needed to achiev this. I tried only to help you.
And it is much appreciated.
Pointers are emulated in Boost.Interprocess with the smart pointer offet_prt. You can yet have inheritance without virtual functions.
Well, I would not insist on that without severe reservations, numerous ifs and buts.
Please, could you take a look to the Boost.Interprocess, Boost.Intrusive and Boost.MultiIndex libraries. Even if not standards they are portable. :-)
Yep, had a quick look at boost.interprocess. Seems like it takes care of shared memory hasssle. Cool. I presume he managed it for UNIXes and Windows. Super cool. And I see boost.multiindex supporting boost.interprocess allocators. Now I get it. Still I do not feel like rushing to support the feature. :-) We'll get the bridge when we get there. I'll keep it iin mingd though. Thanks for your patience and insistence. You see, I not THAT thick after all. :-)
Then, you'll have to provide custom allocators. The issue just keeps more and more complicated. Do you actually have that setting or we are talking hypothetically? If the former, then pimpl will not be your primary concern. :-) If it is the latter, then I find it too ephemeral to discuss effectively.
I insist. This is not hypothesis. I have to manage a lot of data shared between process, I use specific allocators, and synchronization mechanisms. I wanted only to know if I can hide the implementation of this classes with the pilm classes. That's all.
OK, I see you need that. :-) It'll take me sometime to get up to speed with boost.interprocess. Worse, for various reasons I cannot use un-official boost. So, go and figure.
If you do not have several processes accessing the same data and, therefore, do not care for inter-process data-access synchronization, inter-process vtbl pointer correction, etc., then I'd be wondering why you use shared memory in the first place.
Who has said that I have no several process sharing the same data?
No, you certainly did not say you did not have several processes. Have you noticed there was 'if' at the beginning of my sentence? :-)
But the data has no virtual functions. This is not a raison to don't want to hide its implementation.
Fair enough. You convinced me. Honestly, Unfortunately, or reasons beyond my influence I won't be able to extend pimpl support onto boost.interprocess in a short timeframe. Apologies. ..
Thank you very much for your time and good luck for your review. I promise I'll try your library.
Thanks for your input. Much appreciated. Thanks, Vladimir.

Hi Vladimir, From: <Vladimir.Batov@wrsa.com.au> Subject: Re: [boost] Review Request: Generalization of the Pimpl idiom
...
That requirement to have
Derived1::Derived1(implementation*)
is unreasonable as it is clearly not related to to the Derived1 *public* interface. On the other hand reset()-based approach works fine and scales fine.
...
I understand now. This will force to add an artifial *protected* constructor to Derived1. Do you really think that this is a real problem for the developer of Derived1? If I understand your library forbids this to the developper because pimpl_base do not have the following protected constructor. pimpl_base (implementation* impl) : impl_(impl) {}
I think that this will be a good adding to the pimpl_base class.
Respectfully I have to disagree. The reason is that it has been long established precedent in C++ not to provide alternative mechanisms/tools/interfaces if the existing already does that. An immediate example OTOH is that an introduction of "superclass" keyword was rejected because a typedef would do the same thing. Granted, tediously and not as elegantly but still. Here IMHO it's the same story -- we are not talking about missing functionality but rather questionale interface sugar-coating which BTW requires the user to do more than he really have to ("force to add an artifial *protected* constructor").
I don't want to force any developer to do like that, I wish only the pimpl library allows him to do in this way if he considers that it is a better choice. I don't really like to have this implementation pointer initialized to null and been reinitialized on the same constructor with the correct implementation. Note that you have already do a good work allowing to forward construct for the class inheriting directly from the pimpl (first level). By the way, do you plan to parameterize the number of arguments allowed, using maybe preprocesor metaprograming? Yes I know that this is quite ugly, but do not limit the user of the library.
I am not sure what that is supposed to demonstrate. You use infrastructure to help you to do your job. If you decide that the infrastructuere does not help you and discard that, you'll obviously have to re-implement whatever functionality from that infrastructure you managed to deploy. If one uses std::list and then for whatever reason goes ahead with his own list implementation or merely swaps it for std::vector, he'll likely to face the same issue. Trade-offs of the pimpl idiom are well-known -- another indirection and memory allocation. In the end, one can deal with memory-allocation by writing a custom allocator for pimpl<Foo>::implementation.
If pimpl is here to hide the implementation, why it needs to extends the
interface of the initial class? Why the user of my class should suffer from the trade-offs of the pimpl idiom at the inteface level?
Pimpl extend the interface so that the user does not have to provide those methods. Like comparison and validity check, etc. That does not come at the user's expence and, therefore, how exactly the user suffers from that escapes me.
The user will do as he likes. In my case I will surely use private inheritance for value_semantics in order to hide this addins.
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.
I cannot say about you but I do really think that the pimpl idiom is helping me to hide the implementation. Private inheritance restricts access but not hides the implementation.
I was talking about private inheritance from pimpl!!!
If you try deploying pimpl in applications, you'll quickly see that you'll need public inheritance. I do. Having said that, if you insist on inheriting privately, there is nothing stopping you. We are not discussing missing pimpl functionality or crooked implementation, right?
You are right, there is nothing missing. I can achiev my goal using private inheritance. Atleast I hope so.
struct Base { int k; }; class Derived : Base {}
Here Base::k is not accessible in Derived but not hidden. More, if that stuff is a commercial library, I can easily circumvent that restriction by modifying Derived declaration. Pimpl really hides the implementation. See for more about the idiom
1. Guru of the Week #24. http://www.gotw.ca/gotw/024.htm 2. Herb Sutter. Exceptional C++ (Addison-Wesley, 1999) 3. J. Carolan. Constructing bullet-proof classes. In Proceedings C++ at Work'89 (SIGS Publications, 1989) 4. James O. Coplien. Advanced C++ Programming Styles and Idioms (Addison-Wesley, 1992) 5. Eric Gamma et al. Design Patterns (Addison-Wesley,1995)
This is why I suggested to remove the null object and bool type conversion operators for pimpl<A>::value_semantics. Maybe it will be a good idea to give them protected acces.
I admit that "This is why" escpes me. Invalid objects are all over -- nullptr, shared_ptr(), -1, etc. Pimpl extends that concept onto actual classes and gives it uniform and proper implementation. I've been using this extensively and I do indeed need that... public. If one does not need that, he won't use that. That feature won't kick in by accident.
It's OK. Some users could find this operations usefull.
Once we remove the public inheritance from pimpl, it is not eassy to implement all these extra functions and operators.
Then do not remove public inheritance. I am far from being convinced that insistence on private inheritance is justified.
Conveinced or not this is a way to mask the pimpl functions, and it seams to me the best way to use pimpl<A>::value_semantics. This is not the case with pimpl<A>::pointer_semantics from which it is better to inherit publically giving acces to the null object and the bool type conversion operators. This is only a comment on how I will use it :-)
Yes, that makes sense. Although if my usage experience is of any guidance, you'll swap private for public fairly soon. I guess, we'll see.
I will let you know if this is the case. For sure.
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. ... In any case I don't see a real benefit of using pimpl when the base class is 3pp class and we can not change it.
Well, if you build a run-time polymorphic hierarchy on top of an existing 3rd-party base class, then I tend to agree with you as you have to follow the design and strategy outlined by the base class designer. I do not feel like it is a pimpl inherent weakness. Havind said that, I'd shy away from coming to your generic conclusion based on that particular case. Unless I have to write deep run-time polymorphic hierarchies (like Widgets, etc.), I tend to cut inheritance short and I believe it is the strong current programming trend in C++. Therefore, in your case I'd try
pimpl<MyClass>::implementation : public A3ppClass {}
then in MyClass I'd bridged to A3ppClass only the interface that MyClass needed:
MyClass::foo() { (*this)->A3ppClass::foo(); }
It might not be applicable in your particular case -- no one claims to have the silver bullet.
Neither me.
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.
I cannot see anything drastically wrong with the code above even though I probably would not do it that way. So, I'll have to disagree with your conclusion.
Anyway, we will do the same, don't use pimpl with multiple inheritance.
I would not say that I will not. If/when the time comes for me to deploy multiple inheritance, I cannot see myself hesitating because of the pimpl.
Could you add into the documentation a complete and concrete example of multiple inheritance with pimpl? This will help the user to understand how eassy this task could be?
I have to disagee as I find that view over-simplified and in fact incorrect. Pimpl or no pimpl in general no objects beyond value-semantics PODs can be safely (or without additional effort) put into shared memory.
I hope so, and I expect that you will do what is needed to achiev this. I tried only to help you.
And it is much appreciated.
Pointers are emulated in Boost.Interprocess with the smart pointer offet_prt. You can yet have inheritance without virtual functions.
Well, I would not insist on that without severe reservations, numerous ifs and buts.
Please, could you take a look to the Boost.Interprocess, Boost.Intrusive and Boost.MultiIndex libraries. Even if not standards they are portable. :-)
Yep, had a quick look at boost.interprocess. Seems like it takes care of shared memory hasssle. Cool. I presume he managed it for UNIXes and Windows. Super cool. And I see boost.multiindex supporting boost.interprocess allocators. Now I get it. Still I do not feel like rushing to support the feature. :-) We'll get the bridge when we get there. I'll keep it iin mingd though. Thanks for your patience and insistence. You see, I not THAT thick after all. :-)
I understand now why you were reticent to use shared memory.
Then, you'll have to provide custom allocators. The issue just keeps more and more complicated. Do you actually have that setting or we are talking hypothetically? If the former, then pimpl will not be your primary concern. :-) If it is the latter, then I find it too ephemeral to discuss effectively.
I insist. This is not hypothesis. I have to manage a lot of data shared between process, I use specific allocators, and synchronization mechanisms. I wanted only to know if I can hide the implementation of this classes with the pilm classes. That's all.
OK, I see you need that. :-) It'll take me sometime to get up to speed with boost.interprocess. Worse, for various reasons I cannot use un-official boost. So, go and figure.
Do you mean that you can use only official boost. The release 1.35 delivered last week contains already Interprocess and Intrusive (see void_pointer) libraries.
If you do not have several processes accessing the same data and, therefore, do not care for inter-process data-access synchronization, inter-process vtbl pointer correction, etc., then I'd be wondering why you use shared memory in the first place.
Who has said that I have no several process sharing the same data?
No, you certainly did not say you did not have several processes. Have you noticed there was 'if' at the beginning of my sentence? :-)
But the data has no virtual functions. This is not a raison to don't want to hide its implementation.
Fair enough. You convinced me. Honestly, Unfortunately, or reasons beyond my influence I won't be able to extend pimpl support onto boost.interprocess in a short timeframe. Apologies.
Apologies accepted. Let me know if I can help you with the shared memory issues.
Thank you very much for your time and good luck for your review. I promise I'll try your library.
Thanks for your input. Much appreciated.
Thanks, Vladimir.
You are welcome. Best regards _____________________ Vicente Juan Botet Escriba
participants (2)
-
vicente.botet
-
Vladimir.Batov@wrsa.com.au