PrevUpHomeNext

Easy Pimpl with Shared-Ownership Properties

Let's begin with something basic and conventional

struct Book
{
   Book (string const& title, string const& author);
   ...
   string const&  title() const;
   string const& author() const;

   private:

   string  title_;
   string author_;
   int     price_;
};

Now, due to reasons mentioned in the Introduction, we would like to deploy the Pimpl technique with the shared/pointer properties. In that setting the Book definition might change as follows:

struct Book
{
   Book(string const& title, string const& author);
   ...
   string const&  title() const;
   string const& author() const;

   bool operator== (Book const& other) const { return impl_ == other.impl_; }
   bool operator!= (Book const& other) const { return !operator==(other); }

   explicit operator bool () const { return  bool(impl_); }
   bool         operator! () const { return !bool(impl_); }

   private:

   struct implementation;
   std::shared_ptr<implementation> impl_;
};

Applying the Pimpl idiom is fairly straightforward as std::shared_ptr takes care of much of the implementation-related hassle. The auto-generated special member functions (the destructor, the copy constructor, etc.) suffice and the comparison, Boolean-conversion operators аrе easy.

Still, from design perspectives lumping the application logic with the infrastructure scaffolding is unfortunate. In our (admittedly simple) example more than half of the code is the Pimpl-related implementation detail. For one class in isolation that might not be that big a deal. On a larger scale though, reading and maintaining extra code, making sure nothing is forgotten or misplaced is a tiring and error-prone exercise. The following, therefore, seems like an improvement:

struct Book : boost::impl_ptr<Book>::shared
{
   Book(string const& title, string const& author);

   string const&  title() const;
   string const& author() const;
};

With no loss in functionality it is shorter, application-focused and reasonably self-documented. Importantly, it consists of nothing but pure public interface. It is not even a class as there is nothing to restrict access to.

[Note] Note

With such a clean separation of concerns (public interface and private implementation) the technique is ideal for building large-scale systems and, importantly, managing orderly evolution of those systems.


PrevUpHomeNext