Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing pImpl with minimal amount of code

What kind of tricks can be used to minimize the workload of implementing pImpl classes?

Header:

class Foo {
    struct Impl;
    boost::scoped_ptr<Impl> self;
public:
    Foo(int arg);
    ~Foo();
    // Public member functions go here
};

Implementation:

struct Foo::Impl {
    Impl(int arg): something(arg) {}
    // All data members and private functions go here
};

Foo::Foo(int arg): self(new Impl(arg)) {}
Foo::~Foo() {}

// Foo's public functions go here (and they refer to data as self->something)

How would you improve this, using Boost, possibly inheritance, CRTP or other tricks to avoiding as much boilerplate code as is possible? The runtime performance is not an issue.

like image 430
Tronic Avatar asked Mar 01 '10 02:03

Tronic


People also ask

What is pImpl in design pattern?

"Pointer to implementation" or "pImpl" is a C++ programming technique that removes implementation details of a class from its object representation by placing them in a separate class, accessed through an opaque pointer: // -------------------- // interface (widget.


2 Answers

Implementation of pimpl from Loki may be a good answer. See also a DDJ Article on this.

like image 142
amit kumar Avatar answered Oct 01 '22 04:10

amit kumar


It is possible, but a naive implementation is not what you want.

The problem is that templates are generally inlined, the naive implementation would be:

template <class Object>
class Pimpl
{
public:
  explicit Pimpl(Object* obj): mObject(obj) {}
  ~Pimpl() { delete mObject; }

  // either deep copy or no copy
private:
  Object* mObject;
};

Now the problem is that you don't want Object to be known in your header file in general (not for binary compatibility, but for dependency management). And if Object is not known, then you cannot implement the Destructor, Copy Constructor and Assignment Operator directly...

The problem is far from being unsolvable however! Boost indeed has solve it for the shared_ptr.

The idea is to pass a second item in the constructor, that will take care of releasing the memory of the first, and that will be provided with a nice default implementation.

This works with an indirection, of course.

namespace detail {
  template <class Object>
  struct Deleter { virtual void do(Object*) = 0; };
}

template <class Object>
class Pimpl
{
public:
  typedef detail::Deleter<Object> deleter_type;
  typedef boost::shared_ptr<deleter_type> deleter_pointer;

  Pimpl(std::auto_ptr<Object> obj, deleter_pointer del);
  ~Pimpl();
  Pimpl(const Pimpl&);
  Pimpl& operator(const Pimpl&);

private:
  Object* mObject;
  deleter_pointer mDeleter;
};

It's a classic idiom in C++, add yet another level of indirection :)

like image 39
Matthieu M. Avatar answered Sep 30 '22 04:09

Matthieu M.