views:

391

answers:

3

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 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?

A: 

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).

bdonlan
C# most decidedly does not "late-bind everything".
Dan
+6  A: 

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.

Dan
+1  A: 

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();
}
Martin York
That's an interesting idea, though you will end up still calling functions on an X object directly, which means you will need to expose the header and definition of X, so X will not be totally opaque. On the other hand, if you're pimpling primarily for ABI and not making the class opaque (and can trust users won't just opt to use X directly instead when its headers are available), I think it'd be OK.
Joseph Garvin
Also, functions defined inside class definitions in C++ are implicitly inline. You'd need to move PimplX's constructor and operator-> definitions out of the class for it to not tie the caller to the ABI of X.
Joseph Garvin