Skip to content

PIMPL Idiom

The PIMPL idiom (Pointer to IMPLementation) is used in Saturn to enforce a strict separation between public interfaces and private implementation details.

This pattern reduces header dependencies, stabilizes ABI boundaries, and allows internal changes without forcing recompilation of dependent code. It is a foundational tool for maintaining large-scale C++ systems with clear ownership and long-term evolution in mind.


PIMPL is a design pattern in which a class stores a single pointer to an opaque implementation structure, typically named Impl or Implementation.

The public class:

  • Declares the interface
  • Owns a pointer to the implementation
  • Exposes no private data members

The implementation structure:

  • Contains private state
  • Optionally contains private logic
  • Is defined in the source file, not the header

Example:

class Renderer
{
private:
struct Impl;
Impl* m_impl;
public:
Renderer();
~Renderer();
void initialize();
void renderFrame();
};

In this layout:

  • Renderer defines the contract
  • Renderer::Impl defines the details
  • Consumers of Renderer cannot depend on its internal state

Saturn distinguishes between two forms of the PIMPL idiom, based on where behavior is implemented.

With structural PIMPL, only the data members are moved into the Impl. Public methods remain implemented in the owning class and directly operate on the implementation state.

Characteristics:

  • Impl stores private data only
  • Methods are implemented in the public class
  • Minimal forwarding overhead
  • Clear control flow
  • Well-suited for inheritance hierarchies

This is the default and preferred approach in Saturn.


With behavioral PIMPL, both state and logic live inside the Impl. Public methods act as thin forwarding wrappers.

Characteristics:

  • Impl owns both data and behavior
  • Public methods delegate almost entirely
  • Increased indirection and boilerplate
  • Greater flexibility at the cost of clarity

Behavioral PIMPL is used sparingly and only when implementation swapping or strict ABI isolation justifies the added complexity.


The PIMPL idiom is primarily used to address the following concerns:

  • Header dependency reduction Private headers and heavy includes remain in source files.

  • Rebuild time control Changes to implementation details do not force downstream recompilation.

  • Interface stability Public APIs remain stable as internal layouts evolve.

  • Encapsulation enforcement Internal state cannot leak through headers or inline accessors.

  • Scalability Large subsystems remain manageable as complexity increases.

In Saturn, PIMPL is not used for abstraction alone. It is a tool for enforcing architectural boundaries and preserving long-term maintainability.


When using PIMPL in Saturn:

  • Prefer structural PIMPL unless behavioral indirection is explicitly required
  • Keep public headers minimal and dependency-free
  • Avoid leaking implementation types through inline methods
  • Treat the public class as a contract, not a convenience wrapper

PIMPL introduces a small amount of indirection. That cost is intentional and accepted in exchange for clearer boundaries and more robust system evolution.