2. What is pImpl?
● A design pattern that allows you to hide
implementation details from a class
interface, during compilation
● Also known as a compiler firewall
4. Why pImpl?
1. Optimize compilation time
Changes in the implementation do not require components that depend on the interface to
be recompiled
2. Hide implementation and internal dependencies completely
When distributing a precompiled library there's no "leak" of implementation details or
dependencies via private member functions that need to be in the header
3. Switch class implementations transparently to the user
Easily link with another shared library that satisfies the same interface
4. Maintain ABI compatibility
Make backward compatible changes to shared libraries without having to recompile
whatever depends on them
5. pImpl in practice - Code to refactor
// Gyroscope.h
#include "I2cCommunicationBus.h"
class Gyroscope
{
public:
Gyroscope(int temperature);
int getOrientation();
private:
const int mTemperature;
I2cCommunicationBus i2c;
};
// Gyroscope.cpp
Gyroscope::Gyroscope(int temperature)
: mTemperature{temperature}
{
}
int Gyroscope::getOrientation()
{
const auto registerValue = i2c.read(0xFF);
const auto adjustedOrientation = registerValue + mTemperature / 2;
return adjustedOrientation % 360;
}
6. pImpl implementation
// Gyroscope.h (public/shared with others)
class Gyroscope
{
public:
Gyroscope(int temperature);
~Gyroscope();
int getOrientation();
private:
class GyroscopeImpl; // Very secret
// or platform-specific
std::unique_ptr<GyroscopeImpl> mGyroscope;
};
// I2cGyroscopeImpl.cpp
#include "Gyroscope.h"
#include "I2cCommunicationBus.h"
class Gyroscope::GyroscopeImpl
{
public:
GyroscopeImpl (int temperature )
: mTemperature {temperature } {}
int getOrientation () {
// Same as before
}
private:
const int mTemperature ;
I2cCommunicationBus i2c;
};
7. pImpl implementation (continued)
// also in I2cGyroscopeImpl.cpp
Gyroscope::Gyroscope(int temperature )
:
mGyroscope {std::make_unique <GyroscopeImpl >(temperature )}
{}
Gyroscope::~Gyroscope () {
// Defined this in the .cpp file(s)
// or you will get incomplete type errors
}
int Gyroscope::getOrientation ()
{
return mGyroscope ->getOrientation ();
}
// Gyroscope.h (public/shared with others)
class Gyroscope
{
public:
Gyroscope(int temperature);
~Gyroscope();
int getOrientation();
private:
class GyroscopeImpl; // Very secret
// or platform-specific
std::unique_ptr<GyroscopeImpl> mGyroscope;
};
11. ABI vs API compatibility
ABI compatibility
● Runs correctly
● Symbols in binary
● Can run with different shared libraries
(binary)
API compatibility
● Compiles successfully
● Declarations in source code
● Can compile with different revisions of
source code (text)
12. Abstract factory example
// BrittleGyroscope.h (public)
class BrittleGyroscope
{
public:
virtual ~BrittleGyroscope() = default;
virtual int getOrientation() = 0;
};
// GyroscopeFactory.h (public)
class GyroscopeFactory
{
public:
std::unique_ptr<BrittleGyroscope> get();
};
13. Abstract factory example
// I2cBrittleGyroscope.h (private)
class I2cBrittleGyroscope : public BrittleGyroscope
{
public:
int getOrientation() override;
private:
// Some dependencies we don't want to share
};
// I2cBrittleGyroscope.cpp (private/compiled)
int I2cBrittleGyroscope ::getOrientation ()
{
// Some implementation
return 123;
}
// GyroscopeFactory.cpp (private/compiled)
#include "GyroscopeFactory.h"
#include "I2cBrittleGyroscope.h"
std::unique_ptr <BrittleGyroscope > GyroscopeFactory ::get()
{
return std::make_unique <I2cBrittleGyroscope >();
}
14. It's all fun and games
until...
You want to make backward compatible changes
to your distributed library without having to
recompile the binary that depends on it.
16. ABI compatibility across compilers?
If you want your to distribute a shared
library that works with binaries compiled
with any compiler, then ultimately you
have to fall back to a C-compatible API, no
Standard Library types exposed in the
interface.
17. Thank you
Source code: https://github.com/platisd/cpp-pimpl-tutorial
Video: https://www.youtube.com/watch?v=lETcZQuKQBs