Templated Pimpl

1.1.3.1 Motivation

The "Pimpl" idiom (see a thorough description in the guru of the week) is a very useful technique to hide away the private section of a class (i.e., what should go unconditionally into the implementation file instead of the header file, were it not for the lack of consistency in the syntax and abilities of C++). Such implementation hiding has, at least, the following advantages:

  • it makes header files shorter and more ŕ propos;
  • it moves compile-time dependencies from the header file to the implementation file.

The first advantage is important because it makes your code more modular and reusable. Your code should be useable and reusable by looking only at the header files, which constitute the "software contract" of your classes and functions. If an user of your code cannot gather all the information he needs from the declarations and definitons and the accompanying comments in your header files, and has to resort to digging in your implementation files, then you have failed in your pursuit of modularity. On the other hand, not cluttering your "software contract" with details that do not belong into it will make it clearer and bring your users closer to enlightenment.

The second advantage results in reduced compilation times, and fewer files to recompile after a source code modification. It can also remove incidental compile-time cyclic dependencies.

1.1.3.2 Implementation

Having coded too many "Pimpl" devices "by hand", I have tried to extract the essence of the "Pimpl" idiom and turn it into a C++ module. Here is my small contribution to C++ elegance: the templated "pimpl".

 1: // file "pimpl.h"
 2: #ifndef PIMPL_HEADER_
 3: #define PIMPL_HEADER_
 4: 
 5: template <typename Owner, int part=0>
 6: class Detail {
 7: public:
 8:   class Implementation;
 9:   Detail(Implementation *implementation)
10:     : implementation(implementation) { }
11:   ~Detail()
12:     { delete implementation; }
13:   Implementation *operator->() const
14:     { return implementation; }
15: private:
16:   Implementation *const implementation;
17: };
18: 
19: #endif

Now, what does this small piece of code buy us? First, it allows us to think about the implementation, express it, and be read by fellow programmers at a higher level of abstractness. It is good to explicitly and unambiguously state that we are using this idiom, rather than having the human reader guess it; it can also show the way to people that do not know the idiom without having to document it explicitly. Second, it automatically removes the possibility of memory leaks in a single place (have you never forgotten to delete a "Pimpl"?). And that's all; as I said, a small contribution, nada to be gained in terms of efficiency or number of lines of code, but definitely a step forward for mankind.

1.1.3.3 How to use it

If you want to use the templated "pimpl" to declare a "pimpl" for a class named Foo, you have to

  • declare an object in the private part of Foo of type Detail<Foo>, let's name it foo_impl,
  • define the class Detail<Foo>::Implementation in the implementation file for the class Foo;
  • in the constructor initialisation list for Foo, give foo_impl a newly constructed Detail<Foo>::Implementation, (using the operator new);
  • access the implementation object using the syntax foo_impl->member, where member is any accessible attribute or method of Detail<Foo>::Implementation;
  • make sure you declare a destructor for Foo, and define it in the implementation file, even if it does nothing; this is essential since the compiler has to know how to destroy Detail<Foo>::Implementation at the point where the destructor for Foo is defined, and if it is not declared, the compiler synthesises it when it is reading the class declaration.

If for some reason you have several "pimples" for the same class, use the second template argument of Detail, giving each "pimpl" a different number.

Here is an simplistic example that will hopefully make things clearer. The header file defines the classes A and B and declares their "pimples":

 1: // file: "pimltest.h"
 2: #ifndef PIMPLTEST_HEADER_
 3: #define PIMPLTEST_HEADER_
 4: 
 5: #include "pimpl.h"
 6: 
 7: class A {
 8: public:
 9:   A(int i);
10:   ~A();
11:   int get_i() const;
12: private:
13:   Detail<A> implementation;
14: };
15: 
16: class B {
17: public:
18:   B(int i, double k);
19:   ~B();
20:   double get_i_times_k() const;
21: private:
22:   Detail<B, 1> implementation_1;
23:   Detail<B, 2> implementation_2;
24: };
25: 
26: #endif
The implementation file defines the "pimples", and the implementations of A and B access it:
 1: // file: "pimltest.cpp"
 2: #include "pimpltest.h"
 3: 
 4: template <>
 5: class Detail<A>::Implementation {
 6: public:
 7:   int i;
 8: };
 9: 
10: A::A(int i)
11:   : implementation(new Detail<A>::Implementation) {
12:   implementation->i=i;
13: }
14: 
15: A::~A() { }
16: 
17: int A::get_i() const {
18:   return implementation->i;
19: }
20: 
21: 
22: template <>
23: class Detail<B, 1>::Implementation {
24: public:
25:   int i;
26: };
27: 
28: template <>
29: class Detail<B, 2>::Implementation {
30: public:
31:   double k;
32: };
33: 
34: B::B(int i, double k)
35:   : implementation_1(new Detail<B, 1>::Implementation),
36:     implementation_2(new Detail<B, 2>::Implementation) {
37:   implementation_1->i=i;
38:   implementation_2->k=k;
39: }
40: 
41: B::~B() { }
42: 
43: double B::get_i_times_k() const {
44:   return implementation_1->i*implementation_2->k;
45: }
Just to make sure that everything works, here is a test:
 1: // file: "pimpltestmain.cpp"
 2: #include <iostream>
 3: #include "pimpltest.h"
 4: 
 5: int main() {
 6:   A a(7);
 7:   std::cout << a.get_i() << std::endl;
 8:   B b(2, 3.4);
 9:   std::cout << b.get_i_times_k() << std::endl;
10: }
and here is what it prints:
1: 7
2: 6.8

Website produced by Skribe; last make 2011-07-27.

© Miguel González Cuadrado (mgcuadrado@gmail.com)