Reduce C++ Build Times (Part 2) with the Pimpl Idiom

What is Pimpl Idiom?

Pimpl means Pointer to the IMPlementation. The Pimpl idiom technique is also referred to as an opaque pointer, handle classes, compiler firewall idiom, d-pointer, or cheshire cat. This idiom is useful because it can minimize coupling, and separates the interface from the implementation. It is a way to hide the implementation details of an interface from the clients. It is also important for providing binary code compatibility with different version of a shared library. The Pimpl idiom simplifies the interface that is created since the details can be hidden in another file.

What are the benefits of Pimpl?

Generally, whenever a header file changes, any file that includes that file will need to be recompiled. This is true even if those changes only apply to private members of the class that, by design, the users of the class cannot access. This is because of the C++ build model and because C++ assumes that callers know two main things about a class (and private members).

  1. Size and Layout: The code that is calling the class must be told the size and layout of the class (including private data members). This constraint of seeing the implementation means the callers and callees are more tightly coupled, but is very important to the C++ object model because having direct access to object by default helps C++ achieve heavily-optimized efficiency.
  2. Functions: The code that is calling the class must be able to resolve calls to member functions of the class. This includes private functions that are generally inaccessible and overload non-private functions. If a private function is a better match, the code will fail to compile.

With the Pimpl idiom, you remove the compilation dependencies on internal (private) class implementations. The big advantage is that it breaks compile-time dependencies. This means the system builds faster because Pimpl can eliminate extra includes. Also, it localizes the build impact of code changes because the implementation (parts in the Pimpl) can be changed without recompiling the client code. 1

Example: How to implement Pimpl Idiom

In this section, I am going to use a simple example of a Cow class to show how you can update your code to include the Pimpl idiom. Here is a simple Cow class that has private data members:

#include <iostream>

class Cow
{
public:
	Cow();
	~Cow();

	Cow(Cow&&);
	Cow& operator=(Cow&&);
private:
	std::string name;
	std::string color;
	double weight;
};

To implement the Pimpl idiom, I will:

  1. Put all private members into a struct or class in the header file
  2. In the class definition, declare a (smart) pointer to the class (struct) as the only private member variable
#include <memory>

class Cow
{
public:
	Cow();
	~Cow();

	Cow(Cow&&);
	Cow& operator=(Cow&&);
private:
	class cowIMPL;                                     
	std::unique_ptr<cowIMPL> pimpl; 
};

In the source (.cpp) file:

  1. Put the class definition in the .cpp file
  2. The constructors for the class need to create the class
  3. The destructor of the class is defaulted so that the destructor can see the complete definition of cowIMP
  4. The assignment and CopyConstructor need to copy the struct appropriately or else be defaulted (in this case defaulted)
#include "cow2.h"
#include <iostream>

class Cow::cowIMPL                
{
public:
	void do_setup()
	{
		name = "Betsy";
		color = "White";
		weight = 275;
	}
private:
	std::string name;
	std::string color;
	double weight;
};

Cow::Cow() : pimpl{ std::make_unique<cowIMPL>() } 
{
	pimpl->do_setup();
}

Cow::~Cow() = default;

Cow::Cow(Cow&&) = default;
Cow& Cow::operator=(Cow&&) = default;

Summary:

The Pimpl idiom is a great way to minimize coupling and break compile-time dependencies, which leads to faster build times. If you are looking for other ways to reduce compile times, read our blog post on header dependencies. If you are looking to reduce dependencies in general or would like to visualize your source code architecture, check out Lattix Architect.

1. Herb Sutter GotW#100