Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

pimpl <--> polymorphism

74 views
Skip to first unread message

Norman J. Goldstein

unread,
May 6, 2015, 7:09:19 PM5/6/15
to
I was thinking of which situations are better to use pimpl or
polymorhphism. Both options allow hiding the implementation, so that
the main class does not need to be recompiled, should the implementation
change.

Certainly, polymorphism is the C++ elegant approach, but does require a
static/global method to create new instances of the main class. Both
approaches require indirection to access the implementation, and require
storing an extra pointer to do that (polymorphism has the pointer to the
vtable).

The defining difference between the two approaches seems to be that with
pimpl, you can, at run time, switch to a different implementation while
keeping the same main class. I conclude that there is no point using
pimpl unless you want to do "hot" switching of implementations.
Other opinions about this?

Here is code that prints "hello" using both pimpl and polymorphism.

///////////////////////// PIMPL ////////////////////
class ImplPimpl;
class MainPimpl
{
public:
MainPimpl( void );
~MainPimpl();

void hello( void );

private:
ImplPimpl* impl_;
};

#include <iostream>
using namespace std;

class ImplPimpl
{
friend class MainPimpl;

void hello( void )
{ cout << "ImplPimpl hello" << endl; }
};

MainPimpl::MainPimpl( void ) :
impl_( new ImplPimpl )
{}

MainPimpl::~MainPimpl( void )
{
delete impl_;
impl_ = nullptr;
}

void MainPimpl::hello( void )
{impl_->hello(); }

////////////////// POLYMORPHISM //////////////////
class MainPoly
{
public:
virtual ~MainPoly(){};
virtual void hello( void ) = 0;

static
MainPoly* create( void );
};

class ImplPoly : public MainPoly
{
void hello( void )
{ cout << "ImplPoly hello" << endl; }

};

MainPoly* MainPoly::create( void )
{ return new ImplPoly; }

////////////////////////////////////////////////
#include <memory>

int main( int argc, char* argv[] )
{
unique_ptr< MainPimpl > pimpl( new MainPimpl );
pimpl->hello();

unique_ptr< MainPoly > poly( MainPoly::create() );
poly->hello();

return 0;
}// main

Paavo Helde

unread,
May 7, 2015, 2:12:29 AM5/7/15
to
"Norman J. Goldstein" <nor...@telus.net> wrote in news:mie6ul$o3l$1
@speranza.aioe.org:

> I was thinking of which situations are better to use pimpl or
> polymorhphism. Both options allow hiding the implementation, so that
> the main class does not need to be recompiled, should the implementation
> change.

Also: both options complicate the code and do not help if the interface
needs to be changed (as opposed to the implementation). Also, if the
implementation change does not involve changing the header file, then only
the source file needs to be recompiled anyway.

The main question is what is the goal? If it is just reducing compile
times, then my advice is just not to do it at all. Instead, include the
header file only where it is really needed and use forward declaration of
your class as much as possible, to reduce the recompilation times. Having
simple and straightforward code is way much more important in the long
term.

Paavo

K. Frank

unread,
May 7, 2015, 12:43:12 PM5/7/15
to
Hi Norman!

On Wednesday, May 6, 2015 at 7:09:19 PM UTC-4, Norman J. Goldstein wrote:
> I was thinking of which situations are better to use pimpl or
> polymorhphism. Both options allow hiding the implementation, so that
> the main class does not need to be recompiled, should the implementation
> change.
> ...
> The defining difference between the two approaches seems to be that with
> pimpl, you can, at run time, switch to a different implementation while
> keeping the same main class. I conclude that there is no point using
> pimpl unless you want to do "hot" switching of implementations.

Perhaps I misunderstand what you are saying, but I don't
think there is really any difference between pimpl and
polymorphism in terms of changing the implementation at run
time. That is, it seems to me that it is very straightforward
to switch implementations at run time using polymorphism.
See my in-line comments in your sample code, below.

> Other opinions about this?
>
> Here is code that prints "hello" using both pimpl and polymorphism.
> ...
> ///////////////////////// PIMPL ////////////////////
> ...
> MainPimpl::MainPimpl( void ) :
> impl_( new ImplPimpl )
> {}

Run-time-dependent implementation:

{
if (globalVariableUseImplementationOne)
impl_ = new ImplPimplOne;
else
impl_ = new ImplPimplTwo;
}

> ...
> ////////////////// POLYMORPHISM //////////////////
> ...
> MainPoly* MainPoly::create( void )
> { return new ImplPoly; }

Run-time-dependent implementation -- essentially the same
as the pimpl case:

{
if (globalVariableUseImplementationOne)
return new ImplPolyOne;
else
return new ImplPolyTwo;
}

> ...
> ////////////////////////////////////////////////
> #include <memory>
>
> int main( int argc, char* argv[] )
> {
> unique_ptr< MainPimpl > pimpl( new MainPimpl );
> pimpl->hello();
>
> unique_ptr< MainPoly > poly( MainPoly::create() );
> poly->hello();
>
> return 0;
> }// main

With both pimpl and poly, your client code (main) neither
knows nor cares which run-time-dependent implementation
is being used.

I think my personal preference would be to use pimpl
for the "compilation firewall" unless I have some other
standard need for polymorphism.

The code is pretty much the same, but stylistically pimpl
says (to me) that I want a compilation firewall, while
polymorphism says that I'm making use of polymorphism
(in the conventional ways).


Good luck.


K. Frank

Richard

unread,
May 7, 2015, 4:02:32 PM5/7/15
to
[Please do not mail me a copy of your followup]

"Norman J. Goldstein" <nor...@telus.net> spake the secret code
<mie6ul$o3l$1...@speranza.aioe.org> thusly:

>I was thinking of which situations are better to use pimpl or
>polymorhphism. Both options allow hiding the implementation, so that
>the main class does not need to be recompiled, should the implementation
>change.

Polymorphism makes it easier to unit test code collaborating with the
interface. Pimpl doesn't help there.

I view Pimpl primarily as a means of speeding up compilation, not as a
means of introducing abstraction.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

Richard

unread,
May 7, 2015, 4:06:19 PM5/7/15
to
[Please do not mail me a copy of your followup]

Paavo Helde <myfir...@osa.pri.ee> spake the secret code
<XnsA4935DA2BC7C5m...@216.166.105.131> thusly:

>The main question is what is the goal? If it is just reducing compile
>times, then my advice is just not to do it at all.

If you want to reduce build times, look at this talk from CppCon 2014:
"Common-sense Acceleration of Your MLOC Build"
<http://channel9.msdn.com/Events/CPP/C-PP-Con-2014/Common-sense-Acceleration-of-Your-MLOC-Build>

Lots of good stuff in there.

Norman J. Goldstein

unread,
May 7, 2015, 6:53:29 PM5/7/15
to
Hi. K. Frank.

This topic has come up for me while reading "Modern C++ Design", by
Andrei Alexandrescu. I realize now that the reason given by the author
for using pimpl in one of his designs, is to be able to treat the main
class as a bona fide object e.g that can be copied.

A couple more comments, below.

On 05/07/2015 09:43 AM, K. Frank wrote:
> ...
>> The defining difference between the two approaches seems to be that with
>> pimpl, you can, at run time, switch to a different implementation while
>> keeping the same main class. I conclude that there is no point using
>> pimpl unless you want to do "hot" switching of implementations.
>
> Perhaps I misunderstand what you are saying, but I don't
> think there is really any difference between pimpl and
> polymorphism in terms of changing the implementation at run
> time. That is, it seems to me that it is very straightforward
> to switch implementations at run time using polymorphism.
> See my in-line comments in your sample code, below.
>
>> ...
>> ////////////////// POLYMORPHISM //////////////////
>> ...
>> MainPoly* MainPoly::create( void )
>> { return new ImplPoly; }
>
> Run-time-dependent implementation -- essentially the same
> as the pimpl case:
>
> {
> if (globalVariableUseImplementationOne)
> return new ImplPolyOne;
> else
> return new ImplPolyTwo;
> }
>

This code chooses which implementation to create, but this is not the
"hot switching" that I had in mind. Once created, with pimpl, you can
>>keep the same Main object<<, and just switch where the implementation
is pointing to, say, with a method called, "switchImplementation". You
cannot do something similar with polymorphism, since the new
implementation can be an object of a larger size (if you were to try
using the placement "new" operator, say).

Interestingly, C++ has the internal capability to support hot switching:
With virtual inheritance, an object need not be contiguous in RAM, but
(thankfully!) programmers have no direct control over how any "offset to
base class" field is set up in the vtable.

>> ...
> I think my personal preference would be to use pimpl
> for the "compilation firewall" unless I have some other
> standard need for polymorphism.
>

Polymorphism gives a firewall, too -- users do not need to see the
source files for the Implementation class.

Jorgen Grahn

unread,
May 8, 2015, 8:40:26 AM5/8/15
to
Yeah. There are sometimes reasons to use pimpl or run-time
polymorphism, but it's not these reasons.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

mad...@acm.org

unread,
May 8, 2015, 9:33:27 AM5/8/15
to
So with pimpl you get run-time "hot switching" by virtue of being able to
reassign the internal pointer to refer to a different implementation.

How is that very different from having a pointer to a base class and
reassigning that to point to a different derived object? Would you
not consider that to be "hot switching" as well?

Randy.

K. Frank

unread,
May 8, 2015, 11:01:12 AM5/8/15
to
Hello Randy!

On Friday, May 8, 2015 at 9:33:27 AM UTC-4, mad...@acm.org wrote:
> On Thursday, May 7, 2015 at 6:53:29 PM UTC-4, Norman J. Goldstein wrote:
> > Hi. K. Frank.
> ...
> So with pimpl you get run-time "hot switching" by virtue of being able to
> reassign the internal pointer to refer to a different implementation.
>
> How is that very different from having a pointer to a base class and
> reassigning that to point to a different derived object? Would you
> not consider that to be "hot switching" as well?

You and Norman raise some interesting issues. (Hi Norman!
I see what you mean by "hot switching" now.)

I think reassigning a polymorphic pointer is similar to
hot switching with a pimpl class, but not entirely the same.

As I understand it, if your class has no state (e.g., it
has member functions, but no data member variables), then
reassigning the polymorphic pointer should count as hot
switching (but, in effect, you're really only reassigning
a function pointer).

If your class has state, then (unadorned) polymorphism
won't let you hot switch because if you were to reassign
the polymorphic pointer, you would lose the state of the
pointed-to class.

You don't get around this issue for free with pimpl, but
perhaps it lets you package the machinery better.

With pimpl in its simplest form, the pointer-to-implementation
points to the entire implementation -- both state and the
member functions of the implementation class. If you have
state and reassign the pointer-to-implementation, you will
also lose the state.

So, in this regard, you're right. Reassigning a polymorphic
pointer is pretty much the same as reassigning the
pointer-to-implementation.

But if you elaborate pimpl a little, for example, by having
both a pointer to implemented state (member variables) and a
pointer to implemented behavior (member functions), you
could hot switch behavior by reassigning just the latter
without losing state.

Of course, if you add some "stuff" to the polymorphic-pointer
approach, you could also keep state while hot switching,
but maybe pimpl lets you encapsulate this more cleanly.
One way of looking at this is that -- from the perspective
of the client code -- the pimpl class has (can have), as
Norman mentioned, value semantics, so it can have state that
survives hot switching (provided, of course, that the author
of the pimpl class has implemented the machinery that gives
the pimpl class proper values semantics and/or manages state).


> Randy.


Best regards!


K. Frank

Öö Tiib

unread,
May 8, 2015, 6:42:34 PM5/8/15
to
On Thursday, 7 May 2015 02:09:19 UTC+3, Norman J. Goldstein wrote:
> I was thinking of which situations are better to use pimpl or
> polymorhphism. Both options allow hiding the implementation, so that
> the main class does not need to be recompiled, should the implementation
> change.

For me pimpl is not comparable with polymorphism. Pimpl is opaque smart
pointer similar to FILE* in C standard library. However pimpl goes farther
from that. All "pointerness" of its envelope object is also erased. It
behaves fully by value semantics.

> Certainly, polymorphism is the C++ elegant approach, but does require a
> static/global method to create new instances of the main class. Both
> approaches require indirection to access the implementation, and require
> storing an extra pointer to do that (polymorphism has the pointer to the
> vtable).

Lot of problems can be solved with extra level of indirection.

> The defining difference between the two approaches seems to be that with
> pimpl, you can, at run time, switch to a different implementation while
> keeping the same main class. I conclude that there is no point using
> pimpl unless you want to do "hot" switching of implementations.
> Other opinions about this?

Most places where you use pointer (or smart pointer) you can also use pimpl.

Information erasure behind convenient interface is the whole difference.
Pimpl can be internally polymorphic, share objects, copy on write, have
object pools or what not but it is removed information from interface.

When implementation is polymorphic then we can achieve an effect like
pimpl object changing its type during its lifetime. That effect is
not possible with normal polymorphism in C++.

> Here is code that prints "hello" using both pimpl and polymorphism.

I can not imagine it as a problem to solve with either. ;)

Öö Tiib

unread,
May 8, 2015, 6:55:13 PM5/8/15
to
On Thursday, 7 May 2015 23:02:32 UTC+3, Richard wrote:
>
> Polymorphism makes it easier to unit test code collaborating with the
> interface. Pimpl doesn't help there.

Pimpl can be arranged in unit-testable manner. No much need to reinvent
a wheel here. Qt framework uses pimpl extensively and has plenty of good
examples how to arrange pimpl unit-testably.
0 new messages