Search code examples
c++smart-pointerspimpl-idiom

PImpl idiom using std::unique_pointer with the implementation class in a seperate source file


I am writing a simple library to handle window creation across different platforms. To abstract away the platform specific code i want to use the PImpl idiom with a std::unique_ptr and provide a platform specific private implementation with a factory function. This is what i currently have:

Window.h:

#pragma once

#include <memory>

class Window
{
public:    
    Window();
    ~Window();    
private:    
    class WindowImpl;    
    std::unique_ptr<WindowImpl> impl;
};

Window.cpp:

#include "Window.h"
#include "WindowImpl.h"

Window::Window() : impl(WindowImpl::create()) {}    
Window::~Window() = default;

WindowImpl.h:

#include "Window.h"
#include <memory>

class Window::WindowImpl
{
public:    
    static std::unique_ptr<WindowImpl> create();
};

WindowImpl.cpp:

#include "WindowImpl.h"

std::unique_ptr<Window::WindowImpl> Window::WindowImpl::create()
{
    // the plan is to return the platform specific WindowImpl implementation here
    return std::make_unique<Window::WindowImpl>();
}

This seems to do what i want. My Problem ist that i currently have to specify "Window::" in front of basicly everything in WindowImpl.cpp. Since Window is a class and not a namespace "using Window" doesn't work. The examples i found about this topic all have their full Impl class definition inside the source file of their class using it and thus didn't have this problem. But i need to have WindowImpl in a seperate header if i want to derive the platform specific implemetations from it.

Is there a way to keep WindowImpl in its own header/source file and get rid of that verbosity?


Solution

  • You could use type-aliasing, however, that will require you to make the nested class public, which may defeat the use of the PImpl idiom:

    using WImpl = typename Window::WindowImpl;
    
    std::unique_ptr<WImpl> WImpl::create(){
        //implementation goes here
    }
    

    Edit:

    Other than that, you may have to use macros to remove the verbosity:

    #define WIMPL Window::WindowImpl;
    
    std::unique_ptr<WIMPL> WIMPL::create(){
        //implementation goes here
    }