Warning: strlen() expects parameter 1 to be string, array given in /home/sharpr6/public_html/wp-content/themes/starscape/code/starscape.php on line 450
C++ | SharpRobotica.com - Sharp ideas for the software side of robotics
Posts Tagged ‘C++’

Passing Containing Parent to pImpl Idiom Implementation Class

datePosted on 17:38, April 29th, 2010 by Billy McCafferty

The pImpl Idiom is a useful technique for hiding implementation details of C++ classes away from being exposed in the header file.  A couple of the primary benefits include decoupling of the implementation from the interface of the object (as declared in the header) and reduced compilation time if only the pImpl class is modified.  As a rule of thumb, when using pImpl Idiom, there should be clean separation of any implementation details from the containing class.  Spreading implementation details across both the containing class and the pImpl class muddies the separation of concerns and can quickly lead to maintenance issues, with unclear direction as to what should go into the parent vs. the pImpl class.  Accordingly, public methods declared in the parent should simply act as pass-through methods to pImpl.  I’ve seen arguments that any public method which does not require access to the internal state of pImpl may instead be defined on the parent and not require a pass-through; this sounds reasonable while still providing a gauge for cleanly deciding where implementation definitions should reside (i.e., if it’s a public method which doesn’t require access to pImpl state, define it on the parent).  To that end, there may be scenarios wherein the pImpl class needs to invoke a method or property on the parent.  Accordingly, this post shows how to pass a reference to the parent object to the pImpl object while avoiding a copy of the parent in the process.

The first step is in defining the header class which will expose the public methods and properties along with a forward declaration of the pImpl class:

// LaserScanReader.hpp
 
#ifndef GUARD_LaserScanReader
#define GUARD_LaserScanReader
 
#include <boost/shared_ptr.hpp>
 
namespace ladar_reporter_core
{
  class LaserScanReader
  {
    public:
      ...
 
    private:
      // Forward declare the implementation class
      class LaserScanReaderImpl;
      boost::shared_ptr<LaserScanReaderImpl> _pImpl;
  };
}
 
#endif /* GUARD_LaserScanReader */

Note that the _pImpl member is stored as a boost::shared_ptr so that _pImpl will be destructed automatically when it loses all references to it.

Now that the parent has been declared, we can turn our attention towards the implementation class.

// LaserScanReader.cpp
 
#include "LaserScanReader.hpp"
 
namespace ladar_reporter_core
{
  // Private implementation
  class LaserScanReader::LaserScanReaderImpl
  {
    public:
      explicit LaserScanReaderImpl(const LaserScanReader& laserScanReader);
 
    private:
      // Provides access to methods/properties of parent; e.g., _laserScanReader.someProperty
      const LaserScanReader& _laserScanReader;
  };
 
  LaserScanReader::LaserScanReader()
    // Passes a reference of LaserScanReader to LaserScanReaderImpl (not a copy)
    : _pImpl(new LaserScanReaderImpl(*this)) { }
 
  LaserScanReader::LaserScanReaderImpl::LaserScanReaderImpl(const LaserScanReader& laserScanReader)
    // Initialize the reference to the LaserScanReader (parent object of _pImpl)
    : _laserScanReader(laserScanReader) { }
}

Let’s now look at a few of the interesting points in more detail.

  • The constructor declaration of LaserScanReaderImpl takes a const reference to LaserScanReader, avoiding copying the parent object when passed to it.
  • The private member _laserScanReader holds the reference to the parent object, providing access to it’s public methods and properties, accordingly.
  • The constructor definition of LaserScanReader passes a pointer to itself to the constructor of LaserScanReaderImpl.  While it appears to pass a value pointer, thus creating a copy of itself as the parameter, the fact that the constructor is expecting a reference to LaserScanReader avoids copying the object, accordingly.
  • The constructor definition of LaserScanReaderImpl accepts a reference to the parent object, initializing the member _laserScanReader with that reference, accordingly.

With that, the pImpl class now has access to the parent object’s properties and methods via its _laserScanReader member.

Billy McCafferty

© 2011-2014 Codai, Inc. All Rights Reserved