Pimpl's are a source of boilerplate in a lot of C++ code. They seem like the kind of thing that a combination of macros, templates, and maybe a little external tool help could solve, but I'm not sure what the easiest way would be. I've seen templates that help do some of the lifting but not much -- you still end up needing to write forwarding functions for every method of the class that you're trying to wrap. Is there an easier way?
I'm imagining a tool used as part of the make process. You want your public headers to be pimpl'd classes, so you provide some input file, say pimpl.in, that lists the classes (implemented un-pimpl'd) that you'd like to wrap, then that file is examined, the pimpl classes are generated and only their headers (not the original class's headers) are installed during a 'make install'. The problem is I don't see any way to do this without a full blown C++ parser, something even compiler vendors can't get right. Maybe the classes 开发者_StackOverflow社区could be written in someway that makes the job of an external tool easier, but I'm sure I'd end up missing all sorts of corner cases (e.g. templated classes and/or templated member functions).
Any ideas? Has anyone else come with a solution for this problem already?
No, there isn't an easy answer. :-( I would think with nearly every OO expert saying "prefer composition over inheritance", there would be language support for making composition a whole lot easier than inheritance.
I am not saying this is good (just something that sprang to mind).
But you could experiment with overloading the operator ->
#include <memory>
#include <iostream>
class X
{
public:
void plop()
{
std::cout << "Plop\n";
}
};
class PimplX
{
public:
PimplX()
{
pimpl.reset(new X);
}
X* operator->()
{
return pimpl.get();
}
private:
PimplX(PimplX const&);
PimplX& operator=(PimplX const&);
std::auto_ptr<X> pimpl;
};
int main()
{
PimplX x;
x->plop();
}
One option is to use an interface class instead:
class ClassA {
virtual void foo() = 0;
static ClassA *create();
};
// in ClassA.cpp
class ImplA : public ClassA {
/* ... */
};
ClassA *ClassA::create() {
return new ImplA();
}
This does however add overhead from fetching the vtable function pointers.
Other than that, I can't think of any such tool. As you said, parsing C++ is non-trivial, and there are other languages out there where it's less of an issue (eg, C# or Java late-bind everything, so you can change their in-memory layout later without problems).
精彩评论