
Kris, Thanks for your input. It's much appreciated.
From: "Krzysztof Czainski" <1czajnik@gmail.com> ... 2. Pimpl should automate construction and destruction of the Impl struct. Your pimpl does that, but lacks a way of customization of the copying policy.
Have you had a chance to read through the Pimpl documentation? Currently, the described Pimpl provides two policies -- pointer_semantics and value_semantics. In the pimpl.hpp code it looks as follows: template<class T> struct pimpl { typedef pimpl_base<impl_ptr> value_semantics; typedef pimpl_base<boost::shared_ptr> pointer_semantics; }; More so, nothing is stopping us from extending it with, say, COW semantics: template<class T> struct pimpl { typedef pimpl_base<impl_ptr> value_semantics; typedef pimpl_base<boost::shared_ptr> pointer_semantics; typedef pimpl_base<cow_ptr> cow_semantics; }; Isn't it "customization of the copying policy"? Or did you mean something else?
And perhaps a way of customization of the creating policy, a factory of some sort...
I am under impression that I do not quite understand what you mean by "customization of the creating policy" either. Isn't that quality achieved by overloaded constructors? If it is so, then the developer provides the set of overloaded constructors for his/her class. Regardsless of those constructors Pimpl does not need to be modified.
3. The user should have no way of telling if MyClass uses pimpl or not. It should be totally transparent.
I find it hard to visualize what you have in mind. Would it be possible to provide an example where you believe manually-written pimpl-idiom class is more transparent than a class derived from the proposed pimpl?
With Your pimpl, MyClass inherits from pimpl_base, bringing in a lot of operations: operator*(), operator bool_type() and others. They are protected, but they still are there - potentially conflicting with operators, that MyClass might be providing for the user. Therefore I'd prefere making pimpl a member over inheriting from it.
Well, you omitted concrete examples/explanations clarifying why you prefer composition over inheritance in this particular case. Consequently, I have that nagging feeling that you are taking the "prefer composition over inheritance" a tiny bit too dogmatically. From where I stand I feel that Pimpl-derived class needs/wants to expose the complete interface of Pimpl (after all that's why those methods were introduced in Pimpl -- to re-use them). That indicates inheritance. In my view Liskov Substitution Principle is a perfect litmus test for "should I be inheriting from this type?". And in my view Pimpl fits the bill. After all, functional inheritance (as opposed to architectural inheritance) is quite common programming technique. As for your concern about "a lot of operations" in Pimpl "potentially conflicting with operators" of YourClass, then my reading of the specification is somewhat different. Namely, as I understand, a method introduced in the derived class makes *all* the methods with the same name (not just the same signature) in the base class inaccessible.
4. Customizability of pimpl: the implementer might want one of different approaches to copying: a) disallow, b) share, c) clone, d) others like COW. I'm not sure which of these Your pimpl provides. I would recommend a template parameter to choose a policy. It might be just a smart pointer owning the Impl object.
I hope I covered that while answering your #2 point.
5. Pimpl should not do more, then there is to the pimpl idiom. That is, it should hide the implementation. But it should not implement features like operator !=, when the implementer might not want those.
Again, I feel you should have another closer look at the suggested implementation... because the proposed Pimpl implements (or not) operator==() based on the supplied policy. Namely, I do not see why a pointer_semantics-based Pimpl should not provide op==() automatically (as shared_ptr does). On the other hand, the user has to provide op==() for a value_semantics-based Pimpl.
Or it should not implement the possibility of a null object - boost::optional is for that.
Well, my view on the usefullness of pimpl::null() happens to be quite different. In fact, that pimpl::null is there because I use it a *lot*. Pimpl belongs to a smart-pointer-pattern family and shared_ptr and the likes provide such functionality (in a somewhat obscure form of shared_ptr() IMHO of cource).
6. You write about pimpl and dynamic polymorphism. I think pimpl should have nothing to do with polymorphism. Any polymorphic part of a class, even a private virtual function, isn't truly private, so it shouldn't be in pimpl. And everything that is in a pimpl should be truly hidden, even from the derived classes.
Well, "pimpl should have nothing to do with polymorphism" is quite a determined statement. ;-) That mentioned functionality has its purpose -- it implements Non-Virtual Interface Idiom. If you have no need for that, that's OK. We should not be denying it to others, should we? In fact, I happen to use it to fight the complexities of architectural inheritance (where it's unavoidable). Best, V.