Boost logo

Boost :

Subject: Re: [boost] A Pimpl variant
From: Gottlob Frege (gottlobfrege_at_[hidden])
Date: 2010-04-02 01:09:39


On Thu, Apr 1, 2010 at 4:13 PM, vicente.botet <vicente.botet_at_[hidden]> wrote:
> Hi,
>
> I don't know if this is the correct ML to talk about this subject, but as there was recently a discussion about wether Boost should or not use Pimpl, I have decided to post this here.
>
> In http://www.gotw.ca/publications/mill05.htm H. Sutter

> My experience is that I add more private member functions than private member data. I have explored a different variant
> 5.         Put all private funtion and static data (but not instance data) into XImpl. XImpl needs only to store the back pointer.
>
>  While this approach doesn't encapsulates all the private members has some advantages:
> * The XImpl class has no inherent instance data, so  no space overhead
> * The class doesn't needs to store any XImpl pointer as there is no data to maintain, so no need to allocate/deallocate it.
> * Reduce the performance overhead of the Pimpl idiom.
>
> The drawbacks are:
> * Need to include the headers needed for the private instance data.
> * There is yet a minimal performance overhead on the construction of the temporary XImpl class and the derreference of the back pointer.
>

I must be missing something. In '5' what is left of Pimpl that is
worthwhile? ie I think I understand the usual benefits of Pimpl -
data hiding, changing instance data without changing the h file and
forcing client compiles, etc. Aren't all these benefits lost? What's
left.
Alternatively, how is 5 better than a normal C++ class with no Pimpl?
Is this an April Fool's thing? :-)

By the way, although Herb Sutter warns against it in that same
article, I've experimented with a 'Reckless' Pimpl that stores the
data (typelessly) in the class:

class C {
   struct impl; // forward declare
   RecklessPimpl<impl, 24> pimpl;
};

so RecklessPimpl becomes the storage for struct impl. Since the size
is not known, you supply it (eg 24).

Now, there are obvious dangers to this, and Herb's article lists them.
 Surprisingly, I think I've managed to work around them all.

ie
- once in the cpp implementation file for class C, you can check that
the size is OK:
   static_assert(sizeof(impl) == sizeof(Pimpl)); // or can use <=
- alignment can also be assured
- the constructor for RecklessPimpl can properly construct impl with inplace new
- etc

All this can be automatic in the template of RecklessPimpl, thus it
isn't really so reckless after all.

The resulting Pimpl is like a smart pointer in that calls to the impl
look like pointer calls: pimpl->function(); etc.

Of course if sizeof(impl) is increased too much, the h file needs to
change and clients need to recompile, but it still minimizes the
effects of changes, and hides private implementation details.

My main use was to avoid things like windows.h, which tends to screw
up code if it is only included _sometimes_ (ie it is more of an all
or nothing thing!). Like so:

   struct CRITICAL_SECTION; // forward declared instead of #include <windows.h>

   RecklessPimpl<CRITICAL_SECTION, 32> csPimpl;

Which worked well because the sizeof(CRITICAL_SECTION) didn't change.

If anyone was really interested, I could probably submit it to boost
when I find a bit more time (post-BoostCon).
Tony


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