Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Design

Making Pimpl Easy


The User Perspective

Let's say we need to write a Pimpl-based Book class with pointer semantics. That is:

a) We want to separate interface and implementation and to hide implementation through the deployment of the Pimpl idiom.
b) Our Book class needs to have pointer semantics, i.e. with regard to the underlying data it will behave in the smart-pointer/proxy fashion by allowing shared access to the underlying implementation data.

In that setting the Book class public declaration is quite likely to look like the following:

class Book
{
    public:
 
    Book(string const& title, string const& author);
    string const& title() const;
    string const& author() const;
 
    bool operator==(Book const& that) const { return impl_ == that.impl_; }
    bool operator!=(Book const& that) const { return !operator==(that); }
    operator bool() const { return impl_; }
 
    private:
 
    struct Implementation;
    boost::shared_ptr<Implementation> impl_;
};

Thanks to boost::shared_ptr, applying the Pimpl idiom is fairly straightforward as boost::shared_ptr takes care of much of the scaffolding hassle. As a result, the auto-generated destructor, copy constructor and assignment operator suffice and writing the comparison operators is child's play. What more could we wish for? For one thing, lumping the application interface with the infrastructure scaffolding is messy. Moreover, in our (admittedly simple) Book class more than half of the code is the Pimpl-related scaffolding. For one class in isolation that might not look like that big a deal. On a larger scale, analyzing and maintaining twice as much code, mentally separating the application interface from the infrastructure scaffolding, and making sure nothing is forgotten, misused, or misplaced is a tiring exercise and not exactly fun. The following, therefore, seems like a worthwhile improvement:

struct Book : public pimpl<Book>::pointer_semantics
{
    Book(string const& title, string const& author);
    string const& title() const;
    string const& author() const;
};

It is considerably shorter, application-focused and reasonably self-explanatory. It is lean and mean, consisting of nothing but pure application-specific public interface. It does not even need to be a class as there is nothing to restrict access to.

Probably due to the specificity of my task (and/or my programming style) I have been using Pimpl-based classes with pointer semantics (as in the example above) almost exclusively. However, it is certainly not always the right solution. Let's say we needed a Pimpl-based Book class but with value semantics instead. That is, we still want to have interface and implementation properly separated to hide implementation, but our Book class needs to be a class with every Book instance having and managing its own internal data (rather than sharing).

If boost::shared_ptr is an indisputable favorite for Pimpls with pointer semantics, its deployment for Pimpls with value semantics is certain to cause a debate about efficiency, associated overhead, etc. However, writing and getting right a "raw" Pimpl-based class is certainly more involved and possibly more challenging with all things considered. The corresponding declaration might look as follows:

class Book
{
    public:
 
    Book(string const& title, string const& author);
    string const& title() const;
    string const& author() const;
    bool operator==(Book const&) const;
    bool operator!=(Book const&) const;
 
    Book(Book const&);
    Book& operator=(Book const&);
   ~Book();
 
    private:
 
    struct Implementation;
    Implementation* impl_;
};

Again, the interface can be improved upon and shrunk to:

struct Book : public pimpl<Book>::value_semantics
{
    Book(string const& title, string const& author);
    string const& title() const;
    string const& author() const;
    bool operator==(Book const&) const;
};

It is still almost three times shorter and it consists of pure application-related public interface. Clean, minimal, elegant.

Both presented pimpl-based declarations (with pointer and value semantics) look almost identical and internal implementations (as we'll see later) are as close. A notable difference is that for value-semantics classes the comparison operators are not freebies as with pointer-semantics classes. Well, the comparison operators are never freebies (they are never auto-generated). However, due to the specificity of classes with pointer semantics those comparison operators can be reduced to pointer comparisons and generalized. Clearly, that's not true with value-semantics classes. If such a class needs to be comparable, we have to write those comparison operators ourselves. That is, for value-semantics classes the comparison operators become part of the user-provided interface. Still, pimpl<> tries to help and adds

bool operator!=(T const& that) const

when

bool operator==(T const& that) const

is supplied.

So far our public for-all-to-see interface looks clean, minimal, and application-related. Our Book class users and those who are to maintain the code later are likely to appreciate that. Let’s have a look at the implementation.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.