I'm a PImpl Magician

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <type_traits>
#include <memory>

template<typename Derived>
struct PImplMagic
{
	PImplMagic()
	{
		static_assert(std::is_base_of<PImplMagic, Derived>::value,
		              "Template parameter must be deriving class");
	}
//protected: //has to be public, unfortunately
	struct Impl;
};

struct Test : private PImplMagic<Test>, private std::unique_ptr<PImplMagic<Test>::Impl>
{
	Test();
	void f();
};

int main()
{
	Test t;
	t.f();
}

template<>
struct PImplMagic<Test>::Impl
{
	Impl()
	{
		std::cout << "It works!" << std::endl;
	}
	int x = 7;
};

Test::Test()
: std::unique_ptr<Impl>(new Impl)
{
}

void Test::f()
{
	std::cout << (*this)->x << std::endl;
}
It works!
7
http://ideone.com/WcxJu2

Some things to note:
- The static_assert on lines 10 and 11 can't be in class scope because the compiler complains that Derived is not a complete type, so I put it in the constructor body since Derived is a complete type by then
- If you try and do this without PImplMagic and just directly inherit std::unique_ptr<Test::Impl> (and have Impl declared in Test) it will say that Test has no member named Impl (notice it doesn't say Test is an incomplete type (well, on some older versions of gcc it does)) so I have to have Impl be defined before Test - I did this with the PImplMagic class.
- As you can see, I could not make Impl private or protected - when referencing it in std::unique_ptr<PImplMagic<Test>::Impl>, it complains about Impl being protected. This is fine, though, since there's still no way to access the actual Impl instance from outside the class.

The whole point of all of this was just for fun to see if I could avoid using a separate member for the impl pointer - as you can see from line 46 the syntax is much more cool than impl->x or this->impl->x.
Minimalistic usage:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace your_namespace
{
	namespace _ //private
	{
		template<typename D>
		struct PImpl { struct Impl; };
	}
	struct Blah
	: private                 _::PImpl<Blah>
	, private std::unique_ptr<_::PImpl<Blah>::Impl>
	{
		//public interface...
	};
}
I know it's convoluted, but I kind of prefer it over the usual version.
Last edited on
User @DyP from Stack Overflow provided yet another version, which is even cooler:

http://coliru.stacked-crooked.com/a/35dcf4a7b0b7a677

EDIT: I've adapted it into this:
https://github.com/LB--/jsonpp/blob/master/src/util/PImpl.hpp
Last edited on
Topic archived. No new replies allowed.