GotW #24

Home Blog Talks Books & Articles Training & Consulting

On the
blog
RSS feed November 4: Other Concurrency Sessions at PDC
November 3
: PDC'09: Tutorial & Panel
October 26: Hoare on Testing
October 23
: Deprecating export Considered for ISO C++0x

This is the original GotW problem and solution substantially as posted to Usenet. See the book Exceptional C++ (Addison-Wesley, 2000) for the most current solutions to GotW issues #1-30. The solutions in the book have been revised and expanded since their initial appearance in GotW. The book versions also incorporate corrections, new material, and conformance to the final ANSI/ISO C++ standard.

Compilation Firewalls
Difficulty: 6 / 10

Using the Pimpl Idiom can dramatically reduce code interdependencies and build times. But what should go into a pimpl_ object, and what is the safest way to use it?

Problem

In C++, when anything in a class definition changes (even private members) all users of that class must be recompiled. To reduce these dependencies, a common technique is to use an opaque pointer to hide some of the implementation details:

    class X {
    public:
      /* ... public members ... */
    protected:
      /* ... protected members? ... */
    private:
      /* ... private members? ... */
      class XImpl* pimpl_;  // opaque pointer to
                            // forward-declared class
    };

Questions

1. What should go into XImpl? There are four common disciplines, including:

- put all private data (but not functions) into XImpl;

- put all private members into XImpl;

- put all private and protected members into XImpl;

- make XImpl entirely the class that X would have been, and write X as only the public interface made up entirely of simple forwarding functions (a handle/body variant).

What are the advantages/drawbacks? How would you choose among them?

2. Does XImpl require a "back pointer" to the X object?

Solution

First, two definitions:

visible class : the class the client code sees and manipulates (here X)

pimpl : the implementation class (here XImpl) hidden behind an opaque pointer (the eponymous pimpl_) in the visible class

In C++, when anything in a class definition changes (even private members) all users of that class must be recompiled. To reduce these dependencies, a common technique is to use an opaque pointer to hide some of the implementation details:

This is a variant of the handle/body idiom. As documented by Coplien,[1] it was described as being primarily useful for reference counting of a shared implementation.

As it turns out, handle/body (in the form of what I call the "pimpl idiom" because of the intentionally pronounceable "pimpl_" pointer)[2] is also useful for breaking compile-time dependencies, as pointed out by Lakos.[3] The rest of this solution concentrates on that usage, and some of the following is not true for handle/body in general.

The major costs of this idiom are in performance:

1. Each construction must allocate memory. This can be mitigated using a custom allocator, but that's more work.

2. Each access of a hidden member requires at least one extra indirection. (If the hidden member being accessed itself uses a back pointer to call a function in the visible class, there will be multiple indirections.)

1. What should go into XImpl? There are four common disciplines, including:

- put all private data (but not functions) into XImpl;

This is a good start, because now we can forward-declare any class which only appears as a data member (rather than #include the class' actual declaration, which would make client code depend on that too). Still, we can usually do better.

- put all private members into XImpl;

This is (almost) my usual practice these days. After all, in C++, the phrase "client code shouldn't and doesn't care about these parts" is spelled "private," and privates are best hidden (except in some Scandinavian countries with more liberal laws).

There are two caveats, the first of which is the reason for my "almost" above:

1. You can't hide virtual member functions in the pimpl class, even if the virtual functions are private. If the virtual function overrides one inherited from a base class, then it must appear in the actual derived class. If the virtual function is not inherited, then it must still appear in the visible class in order to be available for overriding by further derived classes.

2. Functions in the pimpl may require a "back pointer" to the visible object if they need to use other functions, which adds another level of indirection. By convention this back pointer is usually named self_ where I've worked.

- put all private and protected members into XImpl;

Taking this extra step is actually wrong. Protected members should never go into a pimpl, since putting them there just emasculates them. After all, protected members exist specifically to be seen and used by derived classes, and so aren't nearly as useful if derived classes can't see or use them.

- make XImpl entirely the class that X would have been, and write X as only the public interface made up entirely of simple forwarding functions (a handle/body variant).

This is useful in a few restricted cases, and has the benefit of avoiding a back pointer since all services are available in the pimpl class. The chief drawback is that it normally makes the visible class useless for any inheritance, as either a base or a derived class.

2. Does XImpl require a "back pointer" to the X object?

Often, unhappily, yes. After all, what we're doing is splitting each object into two halves for the purposes of hiding one part.

Whenever a function in the visible class is called, usually some function or data in the hidden half is needed to complete the request. That's fine and reasonable. What's perhaps not as obvious at first is that often a function in the pimpl must call a function in the visible class, usually because the called function is public or virtual.

 

Notes

1. James O. Coplien. Advanced C++ Programming Styles and Idioms (Addison-Wesley, 1992).

2. I always used to write impl_. The eponymous pimpl_ was actually coined by friend and colleague Jeff Sumner, who shares my penchant for Hungarian-style "p" prefixes for pointer variables and who has an occasional taste for horrid puns.

3. J. Lakos. Large-Scale C++ Software Design (Addison-Wesley, 1996).

Copyright © 2009 Herb Sutter