f



Advantage of pimpl *other* than compiler firewall?

Apart from implementing compiler firewalls, what significant
advantages are there to using the pimpl idiom?

Why I ask: I'm currently studying some code that uses pimpl
extensively, but which happens to be organized in a way that it
obviates any possible compiler firewall advantages.  Specifically, the
"visible class" and "pimpl class" (to use the terminology in Herb
Sutter's "Exceptional C++) are declared in the same header file.  A
typical header file looks something like this:

// File C.h

class CImpl
{
public:
   virtual void SetX( int x ) = 0;
   virtual int GetX() = 0;
};

class C
{
public:
   virtual void SetX( int x );
   virtual int GetX();
private:
   auto_ptr<CImpl> pimpl;
};

class CImplDef : public CImpl
{
public:
   virtual void SetX( int x);
   virtual int GetX();
private:
   int m_x;
};

I've left out a few details (eg. constructors/destructor, assignment
operators), but basically this is the general layout for dozens of
header files - abstract base class for the implementation, the class
the client uses, and a specified implementation, all declared in the
same header.

Since CImplDef's privates are visible to anyone using class C (by
virtue of appearing in the same header file), in this case pimpl's
compiler firewall benefit has been voided.  So what *other* advantages
are there to using pimpl?

-Gerry Beauregard

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
g
8/23/2003 8:03:11 AM
comp.lang.c++.moderated 10738 articles. 1 followers. allnor (8506) is leader. Post Follow

11 Replies
636 Views

Similar Articles

[PageSpeed] 45

Hi,

Gerry Beauregard wrote:
> Apart from implementing compiler firewalls, what significant
> advantages are there to using the pimpl idiom?

It is fairly straightforward to implement "swap" algorithm on pimpled 
classes. Atomic swap is one of the building blocks useful in 
exception-aware code.

Related to this is another feature: when you use pimpl polymorphically 
(as in your example), it is possible to switch implementations in 
runtime. See also the "strategy pattern".

Of course, none of these two was necessarily exploited in the code you 
posted, so my answer is about what *can* be done, and not about what 
actually *was* done.

-- 
Maciej Sobczak
http://www.maciejsobczak.com/

Distributed programming lib for C, C++, Python & Tcl:
http://www.maciejsobczak.com/prog/yami/


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Maciej
8/23/2003 2:48:31 PM
Gerry Beauregard wrote:

> Apart from implementing compiler firewalls, what significant
> advantages are there to using the pimpl idiom?
> 
> Why I ask: I'm currently studying some code that uses pimpl
> extensively, but which happens to be organized in a way that it
> obviates any possible compiler firewall advantages.  Specifically, the
> "visible class" and "pimpl class" (to use the terminology in Herb
> Sutter's "Exceptional C++) are declared in the same header file.  A
> typical header file looks something like this:
> 
> // File C.h
> 
> class CImpl
> {
> public:
>    virtual void SetX( int x ) = 0;
>    virtual int GetX() = 0;
> };

Hmmm, the 'virtual' makes me question whether this is really a PIMPL and
not an exchangable plugin like e.g. streambuffers.
 
> class C
> {
> public:
>    virtual void SetX( int x );
>    virtual int GetX();
> private:
>    auto_ptr<CImpl> pimpl;
> };

Have you tried compiling this with just a declaration of class CImpl?
auto_ptr<>'s needs it perhaps. Furthermore, it should rather be 
  auto_ptr<CImpl> const pimpl;
which strengthen's my above doubt on the PIMPLness of this construct.

Even worse here, the virtual C::SetX() calls the virtual CImpl::SetX().
WTF?

> class CImplDef : public CImpl
> {
> public:
>    virtual void SetX( int x);
>    virtual int GetX();
> private:
>    int m_x;
> };

This makes no sense. The PIMPL in its One True Form(tm) is just like the
type of an object: it will never change during its lifetime. Therefore no
need for dynamic dispatch via virtual functions.

Furthermore, 

> I've left out a few details (eg. constructors/destructor, assignment
> operators), but basically this is the general layout for dozens of
> header files - abstract base class for the implementation, the class
> the client uses, and a specified implementation, all declared in the
> same header.

Hmmm, you left out the most interesting parts, because those define whether
this really is a PIMPL. Does CImpl perhaps have a clone() method ('named
copy ctor') ? Can't the definition of CImplDef not be moved outside the
header (some people are reluctant to define a class outside a header
"because that's how it's always done") ? Is there perhaps a ctor of class
C that takes a (hopefully auto-) pointer to a CImpl ?

> Since CImplDef's privates are visible to anyone using class C (by
> virtue of appearing in the same header file), in this case pimpl's
> compiler firewall benefit has been voided.  So what *other* advantages
> are there to using pimpl?

I sincerely doubt that this is a PIMPL, as already stated. To me, it rather
seems as if someone applied %PATTERN% because %PATTERN% is cool, but
without understanding what is really going on. Also possible that the
person in question simply did not know the PIMPL pattern and simply
invented this independently and called it 'pimpl' but with a slightly
different intent - after all it IS a pointer to the implementation of C.

If I were you, I'd look at the history of that file in you sourcecode
management system for the author and their comments. Maybe that will shed
some light on the issue.

happy hacking

Uli

-- 
Questions ?
see  C++-FAQ Lite: http://parashift.com/c++-faq-lite/  first !


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Ulrich
8/23/2003 5:46:11 PM
Gerry Beauregard wrote:

> Apart from implementing compiler firewalls, what significant
> advantages are there to using the pimpl idiom?
> 
> Why I ask: I'm currently studying some code that uses pimpl
> extensively, but which happens to be organized in a way that it
> obviates any possible compiler firewall advantages.  Specifically, the
> "visible class" and "pimpl class" (to use the terminology in Herb
> Sutter's "Exceptional C++) are declared in the same header file.  A
> typical header file looks something like this:
> 
> // File C.h
> 
> class CImpl
> {
> public:
>    virtual void SetX( int x ) = 0;
>    virtual int GetX() = 0;
> };
> 
> class C
> {
> public:
>    virtual void SetX( int x );
>    virtual int GetX();
> private:
>    auto_ptr<CImpl> pimpl;
> };
> 
> class CImplDef : public CImpl
> {
> public:
>    virtual void SetX( int x);
>    virtual int GetX();
> private:
>    int m_x;
> };
> 
> I've left out a few details (eg. constructors/destructor, assignment
> operators), but basically this is the general layout for dozens of
> header files - abstract base class for the implementation, the class
> the client uses, and a specified implementation, all declared in the
> same header.
> 
> Since CImplDef's privates are visible to anyone using class C (by
> virtue of appearing in the same header file), in this case pimpl's
> compiler firewall benefit has been voided.  So what *other* advantages
> are there to using pimpl?

The example you've shown looks pretty pointless to me. But I wondered 
about the class CImplDef and guess that you did not showed us the whole 
story ;-). Does CImpl declare a virtual function called clone()? If so, 
this might be an body/handle implementation with a polymorph body.

The advantage of such a construct is that the handle behaves like a 
polymorph class without the need to handle with pointers and dynamic 
memory allocation (from the class users point of view). Slicing is 
avoided too.

But in that case the functions of C don't have to be virtual nor have 
CImpl and CImplDef to be declared in the header.

Just a wild guess.

regards
Torsten


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Torsten
8/23/2003 5:47:59 PM
"Gerry Beauregard" <g.beauregard@ieee.org> wrote in message
news:9a7f3df5.0308220747.11b38882@posting.google.com...
> Apart from implementing compiler firewalls, what significant
> advantages are there to using the pimpl idiom?

1) having mostly one pointer in your class makes heap-allocations, well,
fewer which speeds up this part of the code
2) swaps get the same benefit

regards

Thorsten Ottosen, Dezide Aps



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Thorsten
8/23/2003 5:59:25 PM
>
> Since CImplDef's privates are visible to anyone using class C (by
> virtue of appearing in the same header file), in this case pimpl's
> compiler firewall benefit has been voided.  So what *other* advantages
> are there to using pimpl?
>

In fact, you loose the main advantage of pimpl by doing it that...

A compromise could be to have the base class in the header but
derived ones in sources files if you want to support multiple
derived classes (that may be defined elsewhere).

For others advantages, you may have the following:

- You may uses reference counting instead of copying the
main object.
- It may be usefull to uses to have another hierarchy of classes
for the implementation if you can have different combination
of main and implementation classes.

But typically, you should uses the pimpl idiom and hide the
implementation. You should try to move the code in CPP
files and if it still compiles leave it there.

In some case, you may be able to only move derived
implementation classes (without doing many changes).


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Philippe
8/23/2003 8:06:52 PM
Many thanks for the excellent responses!

>From a few of the posts, it sounds like I may have left out some
crucial details that might explain why the code is organized as it is.

So here's what the header and cpp files really look like.  (I've
changed the class names, and left out #includes, #pragmas, etc., and
most of the comment blocks, but otherwise this is the real deal).  In
the sources, there are many subclasses of the Node class.   For every
Node class (Node and every class derived from Node), there's only one
implementation.

.......

// File Node.h

 class NodeList;
 class __declspec(novtable) NodeImpl  
 {
 public:
  virtual ~NodeImpl() {}
  virtual bool IsComplete() const = 0;
  virtual void SetComplete(bool flag) = 0;
 };

 class Node  
 {
 public:
  virtual ~Node() {}

  // This method will ensure the correct copy constructor is called
for
  // the derived types.
  virtual Node& Clone() const { return *(new Node(*this)); }

  // Search for the given 'type' and append to the list argument.
  virtual bool FindNodeType(MFNode& list, const type_info& type);

  bool IsComplete();
  void SetComplete(bool flag);
 protected:
  NodeImpl& GetNodeImpl() const;
 private:
  mutable boost::shared_ptr< NodeImpl > m_NodeImpl;
 };

 class NodeImplDef : public NodeImpl  
 {
 public:
  NodeImplDef::NodeImplDef() : m_complete(false) {}
  virtual ~NodeImplDef() {}
  virtual bool IsComplete() const     { return m_complete; }
  virtual void SetComplete(bool flag) { m_complete = flag; }
 protected:
  bool m_complete;
 };

+++++++++++

// File Node.cpp

NodeImpl& Node::GetNodeImpl() const
{
 if (!m_NodeImpl)
  m_NodeImpl.reset(&(factory::NodeFact::Instance().CreateNodeImpl()));
 return *m_NodeImpl;
}

bool Node::IsComplete() { return GetNodeImpl().IsComplete(); }
void Node::SetComplete(bool flag) { GetNodeImpl().SetComplete(flag); }

bool Node::FindNodeType(MFNode& nodeList, const type_info& type)
{
 if (typeid(*this) == type)
 {
  nodeList.AddValue(this);
  return  true;
 } 
 return false; 
}

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
g
8/24/2003 11:42:54 PM
On 24 Aug 2003 19:24:48 -0400, llewelly <llewelly.at@xmission.dot.com> wrote:

 >But what about the auto_ptr<CImpl> ? If CImpl is incomplete at
 >auto_ptr<CImpl>'s destructor's point of instantiation, the
 >destructor will call delete on an incomplete class, which is
 >undefined behavior.

Use a boost::shared_ptr.

Or roll your own; the trick used by boost::shared_ptr is that the
smart-pointer in addition to a pointer to the object holds a pointer
to a destructor function, only declared in the .h file.


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
alfps
8/25/2003 9:14:17 AM
"llewelly" <llewelly.at@xmission.dot.com> wrote in message
news:8665kouhtp.fsf@Zorthluthik.local.bar...
 > "Philippe Mori" <philippe_mori@hotmail.com> writes:
[snip]
 > > But typically, you should uses the pimpl idiom and hide the
 > > implementation. You should try to move the code in CPP
 > > files and if it still compiles leave it there.
 > [snip]
 >
 > But what about the auto_ptr<CImpl> ? If CImpl is incomplete at
 >     auto_ptr<CImpl>'s destructor's point of instantiation, the
 >     destructor will call delete on an incomplete class, which is
 >     undefined behavior.

there a difference between declaration of a member and its instantiation.
as long as the class in complete at the point of instantation, it's ok.

see also

http://www.boost.org/libs/smart_ptr/smart_ptr.htm#common_requirements

regards

Thorsten Ottosen, Dezide Aps



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Thorsten
8/25/2003 9:14:52 AM
"Thorsten Ottosen" <nesotto@cs.auc.dk> writes:

> "llewelly" <llewelly.at@xmission.dot.com> wrote in message
> news:8665kouhtp.fsf@Zorthluthik.local.bar...
>  > "Philippe Mori" <philippe_mori@hotmail.com> writes:
> [snip]
>  > > But typically, you should uses the pimpl idiom and hide the
>  > > implementation. You should try to move the code in CPP
>  > > files and if it still compiles leave it there.
>  > [snip]
>  >
>  > But what about the auto_ptr<CImpl> ? If CImpl is incomplete at
>  >     auto_ptr<CImpl>'s destructor's point of instantiation, the
>  >     destructor will call delete on an incomplete class, which is
>  >     undefined behavior.
>
> there a difference between declaration of a member and its instantiation.
> as long as the class in complete at the point of instantation, it's
> ok.

I'm aware of this. That's why I said 'If'. My point was, if CImpl gets
    put in another header file, and some TU which instantiates
    auto_ptr<CImpl>'s destructor does not #include CImpl's class
    definition, there will be silent misbehavior. Philippe's
    suggestion opens up that danger where it didn't exist. I've seen
    the compilation firewall idiom implemented with 
    auto_ptr<>, and then seen it undone by combining header files,
    precisely because of time spent tracking down bugs due to
    auto_ptr<>'s silent misbehavior. The resulting headers looked much
    like what the OP posted. Quite possibly, the OP's example *was*
    the compilation firewall idiom, until it was undone to avoid the
    evils of auto_ptr<>. And I did know that:

> http://www.boost.org/libs/smart_ptr/smart_ptr.htm#common_requirements

is often a better way to avoid those problems, though I think in some
    cases scoped_ptr<> is more appropriate.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
llewelly
8/25/2003 10:30:40 PM
llewelly <llewelly.at@xmission.dot.com> wrote in message news:<8665kouhtp.fsf@Zorthluthik.local.bar>...

> But what about the auto_ptr<CImpl> ? If CImpl is incomplete at
>     auto_ptr<CImpl>'s destructor's point of instantiation, the
>     destructor will call delete on an incomplete class, which is
>     undefined behavior.


You declare a destructor, non-inlined.  Place the destructor in your
..cpp file.  It is then the destructor's job to call destructors on all
data members.  As the destructor is in the .cpp file, it can see the
definition of the pimpl class, and will call the destructor on CImpl.

The benefit of switching to BOOST is that it will give you an error if
you forget to do this.

joshua lehrer
factset research systems
NYSE:FDS

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
usenet_cpp
8/26/2003 12:40:10 AM
Hi,

One reason why I have recently used pimpl is to benefit from a third party
implementation without adding dependencies to my core libraries. Let me
explain.

We used to have a coordinate transformation class that used a hierarchy of
transformations based on different rubber sheet algorithms. All this classes
reside in a module named A that is used in our product, but that it is also
used by another team, and we release it to them as a third party.

For our new release, we wanted to add a new transformation, but instead of
using a rubber sheet algorithm, we wanted to use a third party
implementation of a coordinate system transformation. We want to make this
implementation available to our customers, but we do not want to have that
dependency in the module that we release to other team because of legal
issues (which I do not know much about).

In order to remove the dependency, we used a pimpl, defining our
transformation class in module A, and a base class for the implementation in
module A. This allow us to create implementations using whatever algorithms
we had and put them in module A, and create a new module B that uses the
third party library and defines a new implementation using it. Now, we can
still use the new implementation in our application, and we can ship module
A with no dependencies in the third party lib.

This architecture has also removed some compile time dependencies, which I
am also happy about.

--
Regards,

Isaac Rodriguez
=======================
Software Engineer - Autodesk


"Gerry Beauregard" <g.beauregard@ieee.org> wrote in message
news:9a7f3df5.0308220747.11b38882@posting.google.com...
> Apart from implementing compiler firewalls, what significant
> advantages are there to using the pimpl idiom?
>
> Why I ask: I'm currently studying some code that uses pimpl
> extensively, but which happens to be organized in a way that it
> obviates any possible compiler firewall advantages.  Specifically, the
> "visible class" and "pimpl class" (to use the terminology in Herb
> Sutter's "Exceptional C++) are declared in the same header file.  A
> typical header file looks something like this:



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Isaac
8/26/2003 7:06:44 PM
Reply: