The Pimpl Idiom
Currently I'm refactoring the input system of Nordenfelt. The reason for this is to enable mapping input from mouse, keyboard and joystick to any actions in the game. E.g. it should be possible to control the ship by WASD for movement and SHIFT for fire as well as by cursor keys for movement and any mouse button for fire. Mouse button functionality should be swappable for left-handers, etc. Briefly: players will be allowed to set their input scheme as they please.
After two days of refactoring one design guideline proved itself as well suited for refactoring: the pimpl idiom.
The pimpl idiom stands for "private implementation idiom". Modern object-oriented languages like Java or C# favour public implementation: the implementation details are intermixed with the interface declaration. There are design patterns for hiding details like interfaces, proxies or factories. Nevertheless, modern languages expose their class internals like private methods or properties in their interface. Firstly it clutters the interface with needless "information" and secondly creates unnecessary dependencies. More dependencies result in longer recompile times. Recompilation is not really an issue for modern languages. Unfortunately C++ has complex features like templates or argument dependent lookup which make compiling cpp files a time consuming task.
An easy way to avoid dependencies is writing pure interfaces which stay the same, regardless of their internals. Lets say we have the following class declaration in C++ header Character.hpp:
#include <rectangle.hpp>
#include <vector>
class Animation;
class Gun;
class Character
{
public:
Character();
~Character();
void Update();
void Render() const;
inline unsigned int GetHitpoints() const { return hitpoints; }
private:
void FireAllGuns();
bool PointCollides(float x, float y) const;
std::vector<Gun*> guns;
Animation* shoot;
Animation* run;
Animation* idle;
Animation* currentAnimation;
Rectangle shootBody;
Rectangle runBody;
Rectangle idleBody;
unsigned int hitpoints;
};
There are two hidden methods and many properties in this declaration. They are not available to foreign operations. So why should we expose them here at all? The above mentioned dependencies are a further reason for hiding the guts. What if we replace the animation/body pairs and combine them in a new class Action? The declaration of Character will change and every cpp file including Character.hpp has to be recompiled. Characters are a common unit in games so many classes would be affected.
Let's use the pimpl idiom to hide the internals:
class Character
{
public:
Character();
~Character();
void Update();
void Render() const;
unsigned int GetHitpoints() const;
private:
struct Pimpl;
Pimpl* pimpl;
};
Oh yeah, that's much better! No private detail is visible in the declaration (well, nearly) and all includes are gone. Only the class Pimpl is there. Pimpl will contain all the private stuff we kicked out. The forward declaration of Pimpl does not reveal anything about its structure, neither private nor public. As long as we keep construction, changes and deletion of pimpl in the cpp file we have no need for its interface anyway. Let's take a look on the implementation in Character.cpp:
#include <animation.hpp>
#include <gun.hpp>
#include <rectangle.hpp>
#include <vector>
struct Character::Pimpl
{
~Pimpl()
{
delete shoot;
delete run;
delete idle;
for(std::size_t i = 0; i < guns.size(); ++i)
delete guns[i];
}
inline void FireAllGuns()
{
for(std::size_t i = 0; i < guns.size(); ++i)
guns[i]->Fire();
}
inline bool PointCollides(float x, float y) const
{
if(currentAnimation == shoot)
return shootBody.PointCollides(x, y);
if(currentAnimation == run)
return runBody.PointCollides(x, y);
if(currentAnimation == idle)
return idleBody.PointCollides(x, y);
return false;
}
std::vector<Gun*> guns;
Animation* shoot;
Animation* run;
Animation* idle;
Animation* currentAnimation;
Rectangle shootBody;
Rectangle runBody;
Rectangle idleBody;
unsigned int hitpoints;
};
Character::Character()
:pimpl(new Pimpl)
{
...init pimpl here...
}
Character::~Character()
{
delete pimpl;
}
void Character::Update()
{
...pimpl->FireAllGuns() and pimpl->PointCollides() may be used here...
}
void Character::Render()
{
pimpl->currentAnimation->Render();
}
unsigned int Character::GetHitpoints() const
{
return pimpl->hitpoints;
}
As you can see: a pure interface is clean, has less dependencies and therefore is more refactoring-friendly. The pimpl idiom does not have additional overhead compared to writing common class declarations and it will boost your development cycle in the long run.
Code well,
Thomas

Comments
bit.ly/aQucwD
RSS feed for comments to this post.