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

Follow-up Pimpl question

90 views
Skip to first unread message

Rupert Swarbrick

unread,
Oct 21, 2013, 6:39:27 PM10/21/13
to
I'm coming back to writing some C++ after a few years in Lisp-land, and
was wondering about the "pimpl idiom". I understand how to write and use
it, and have done in the past. However, I don't really understand what
it gains you over having an abstract base class in the header, along
with a factory function.

Presumably there's a significant difference, since people put up with
the reams of boilerplate required for passing functions through to the
implementation object. Can anyone explain to me what it is?


Rupert


PS: I'm not sure whether there are strong feelings about whether to use
this idiom or not. To be clear, I'm not trying to hear them! I can
see obvious downsides to keeping a pointer to an implementation
object (verbosity; have to be careful about destructor +
exceptions...) and I'm interested to know what the upsides are.

K. Frank

unread,
Oct 21, 2013, 9:07:25 PM10/21/13
to
Hello Rupert!

On Monday, October 21, 2013 6:39:27 PM UTC-4, Rupert Swarbrick wrote:
> I'm coming back to writing some C++ after a few years in Lisp-land, and
> was wondering about the "pimpl idiom". I understand how to write and use
> it, and have done in the past. However, I don't really understand what
> it gains you over having an abstract base class in the header, along
> with a factory function.
>
> Presumably there's a significant difference, since people put up with
> the reams of boilerplate required for passing functions through to the
> implementation object. Can anyone explain to me what it is?

Well, first off, as you recognize, there is cost to
using the pimpl idiom, and I certainly won't argue
that it is always desirable of preferable.

However, one benefit is that for some use cases
constructors and destructors are very useful tools.

In particular they let you have local instances of
classes on the stack and those instances get cleaned
up automatically -- their destructors called -- when
they go out scope, even if scope is exited because
an exception was thrown. This is the main reason
that RAII plus executions / stack unwinding is so
powerful.

Of course there are other ways of achieving this.
For example you could use a smart pointer and a
factory function (but one might argue that doing
so is just reimplementing the pimpl idiom by another
name).

> Rupert
> ...


Good luck.


K. Frank

Öö Tiib

unread,
Oct 21, 2013, 10:36:47 PM10/21/13
to
On Tuesday, 22 October 2013 01:39:27 UTC+3, Rupert Swarbrick wrote:
> I'm coming back to writing some C++ after a few years in Lisp-land, and
> was wondering about the "pimpl idiom". I understand how to write and use
> it, and have done in the past. However, I don't really understand what
> it gains you over having an abstract base class in the header, along
> with a factory function.

We call it "pimpl" since we like Orcish language perhaps, rest call it
"Cheshire Cat", "Compiler firewall" or "Bridge pattern".

There is better separation of concerns. Abstraction implements external
interface. Implementor implements internal implementation.

There are more dynamics. For example when object behind pointer to
interface is made then it can not change its type anymore in C++.
The implementor that is behind abstraction of pimpl however may be
is dynamically replaced, shared, cached, reused or copied-on-write etc.
by abstraction. It is not business of user of abstraction but
externally it may feel like type has changed entirely during life-time
of abstraction.

Slight performance advantage of pimpl is that the virtual functions
are not needed. There may be virtual functions as implementor may be
polymorphic ... but those are not mandatory. So virtual functions
may be used when those improve performance, not when they describe
common interface.

> Presumably there's a significant difference, since people put up with
> the reams of boilerplate required for passing functions through to the
> implementation object. Can anyone explain to me what it is?

It is never clear if any of named advantages is substantial enough for
you.

> PS: I'm not sure whether there are strong feelings about whether to use
> this idiom or not. To be clear, I'm not trying to hear them! I can
> see obvious downsides to keeping a pointer to an implementation
> object (verbosity; have to be careful about destructor +
> exceptions...) and I'm interested to know what the upsides are.

I must say that pimpl has its downsides too. If the problem has simple
solution then it is easy to make it more complex by adding unneeded
pimpl. We should aim to keep things as simple as possible (just not
simpler than possible). So pimpl is good for complex enough objects.

Tobias Müller

unread,
Oct 22, 2013, 1:41:31 AM10/22/13
to
IMO the abstract base class pattern is not the right solution for this
problem. It may work as long as your objects are only given out, and never
taken back.
As soon as you have a method taking the abstract base class as parameter it
is not safe anymore.
While the method is formally taking an abstract base class, it actually
expects your concrete subclass instead (and must resort to casting).
I know no way for restricting a class to only be derived once. There is
nothing (except documentation) that prevents the client code from deriving
from your abstract base class and pass such an object to your method.

It is just an incomplete solution to the problem. OTOH, pimpl objects
behave like normal C++ objects in every single aspect. As long as you
restrict yourself to the public interface you could use the pimpl class as
a drop-in replacement of the actual implementation.

Tobi

James Kanze

unread,
Oct 22, 2013, 2:04:18 PM10/22/13
to
On Monday, 21 October 2013 23:39:27 UTC+1, Rupert Swarbrick wrote:
> I'm coming back to writing some C++ after a few years in Lisp-land, and
> was wondering about the "pimpl idiom". I understand how to write and use
> it, and have done in the past. However, I don't really understand what
> it gains you over having an abstract base class in the header, along
> with a factory function.

> Presumably there's a significant difference, since people put up with
> the reams of boilerplate required for passing functions through to the
> implementation object. Can anyone explain to me what it is?

It allows value semantics, which the abstract base class
doesn't. Typical C++ makes extensive use of value semantics.

Note that you would never use the compilation firewall idiom
except for a class which had value semantics (and thus isn't
meant to be derived from).

--
James

Rupert Swarbrick

unread,
Oct 22, 2013, 2:32:32 PM10/22/13
to
"K. Frank" <kfran...@gmail.com> writes:
> However, one benefit is that for some use cases
> constructors and destructors are very useful tools.
>
> In particular they let you have local instances of
> classes on the stack and those instances get cleaned
> up automatically -- their destructors called -- when
> they go out scope, even if scope is exited because
> an exception was thrown. This is the main reason
> that RAII plus executions / stack unwinding is so
> powerful.
>
> Of course there are other ways of achieving this.
> For example you could use a smart pointer and a
> factory function (but one might argue that doing
> so is just reimplementing the pimpl idiom by another
> name).

Thank you, that makes a lot of sense. I guess std::unique_ptr makes this
considerably less painful, but the user of the class still has extra
hoops to jump through with the factory function.

Rupert

Rupert Swarbrick

unread,
Oct 22, 2013, 2:41:09 PM10/22/13
to
Tobias Müller <tro...@bluewin.ch> writes:
> IMO the abstract base class pattern is not the right solution for this
> problem. It may work as long as your objects are only given out, and never
> taken back.
> As soon as you have a method taking the abstract base class as parameter it
> is not safe anymore.
> While the method is formally taking an abstract base class, it actually
> expects your concrete subclass instead (and must resort to casting).
> I know no way for restricting a class to only be derived once. There is
> nothing (except documentation) that prevents the client code from deriving
> from your abstract base class and pass such an object to your method.

Ahah! I hadn't thought of that!

But thinking further, I'm a bit confused about how important it
is. Functions that "take the object as a this pointer" work for free:
you add a pure virtual function to the abstract base class, have the
subclass implement it, then rely on virtual functions doing the right
thing.

So I suppose the problem is when I have this in foo.h:

class foo {
void something () = 0;
};

void take_a_foo (foo& x);


Is that what you mean? I've never used the abstract base class idiom
except when writing applications. Then, of course, you can just say
"don't derive from foo unless you are foo_impl" and declare the problem
solved.

I guess that in a library context, this is a bit harder. Presumably you
could use RTTI to throw an exception in the body of take_a_foo if x
isn't actually a foo_impl, but that's brittle and has an additional
performance penalty. (Interestingly, the take_a_foo example is the only
way of specialising methods in Lisp, but there you put up with slower
method dispatch in exchange for much more flexibility)

> It is just an incomplete solution to the problem. OTOH, pimpl objects
> behave like normal C++ objects in every single aspect. As long as you
> restrict yourself to the public interface you could use the pimpl class as
> a drop-in replacement of the actual implementation.

Hmm, I'm not convinced that this argument doesn't apply equally well to
an implementation of an abstract base class. That's what the "is-a"
relationship means, right?

Rupert

Rupert Swarbrick

unread,
Oct 22, 2013, 2:46:44 PM10/22/13
to
James Kanze <james...@gmail.com> writes:
> It allows value semantics, which the abstract base class
> doesn't. Typical C++ makes extensive use of value semantics.

Ah, because without being able to call the constructor of my
implementation class "by name", the client code will never be able to
get an actual object rather than some pointer? Thanks, that makes sense.

> Note that you would never use the compilation firewall idiom
> except for a class which had value semantics (and thus isn't
> meant to be derived from).

Yep, that bit makes sense to me.


Rupert

Rupert Swarbrick

unread,
Oct 22, 2013, 2:52:19 PM10/22/13
to
Öö Tiib <oot...@hot.ee> writes:
> We call it "pimpl" since we like Orcish language perhaps, rest call it
> "Cheshire Cat", "Compiler firewall" or "Bridge pattern".

Well, I could have been even more horrible and called it pImpl. Or maybe
p_impl? The trick is the glottal stop after the p to emphasise the
camel-case (and the recovering Java programmer?)

> There is better separation of concerns. Abstraction implements
> external interface. Implementor implements internal implementation.
>
> There are more dynamics. For example when object behind pointer to
> interface is made then it can not change its type anymore in C++.
> The implementor that is behind abstraction of pimpl however may be
> is dynamically replaced, shared, cached, reused or copied-on-write etc.
> by abstraction. It is not business of user of abstraction but
> externally it may feel like type has changed entirely during life-time
> of abstraction.

Ah, I hadn't thought about the copy-on-write use case. But how is that
different to doing it on one or more members of an abstract base class's
implementation? Presumably more data members actually means that you
have more fine-grained control!

> Slight performance advantage of pimpl is that the virtual functions
> are not needed. There may be virtual functions as implementor may be
> polymorphic ... but those are not mandatory. So virtual functions
> may be used when those improve performance, not when they describe
> common interface.

That doesn't make much sense to me. Surely every public function in your
class has to look something like

void interface::function (int x)
{
pimpl->function (x);
}

which... is a virtual function table, just manually written out.


Rupert

Paavo Helde

unread,
Oct 22, 2013, 4:26:57 PM10/22/13
to
Rupert Swarbrick <rswar...@gmail.com> wrote in news:l46hh1$4bk$2
@speranza.aioe.org:
> That doesn't make much sense to me. Surely every public function in your
> class has to look something like
>
> void interface::function (int x)
> {
> pimpl->function (x);
> }
>
> which... is a virtual function table, just manually written out.

No, if the function is not virtual, then this translates approx. to:

PimplClass::function(pimpl, x);

The address of the function to call is fixed at compile/link time, no
lookup needed from anywhere. Thus optimizers can also inline this call
(even if it is in another translation unit, though it is harder then).

Cheers
Paavo

Öö Tiib

unread,
Oct 22, 2013, 5:01:32 PM10/22/13
to
On Tuesday, 22 October 2013 21:52:19 UTC+3, Rupert Swarbrick wrote:
> Öö Tiib <oot...@hot.ee> writes:
> <snip naming>
> > There is better separation of concerns. Abstraction implements
> > external interface. Implementor implements internal implementation.
> >
> > There are more dynamics. For example when object behind pointer to
> > interface is made then it can not change its type anymore in C++.
> > The implementor that is behind abstraction of pimpl however may be
> > is dynamically replaced, shared, cached, reused or copied-on-write etc.
> > by abstraction. It is not business of user of abstraction but
> > externally it may feel like type has changed entirely during life-time
> > of abstraction.
>
> Ah, I hadn't thought about the copy-on-write use case. But how is that
> different to doing it on one or more members of an abstract base class's
> implementation? Presumably more data members actually means that you
> have more fine-grained control!

Derived classes have one or more polymorphic members accessible from base
abstract interface? It is even more complex.

> > Slight performance advantage of pimpl is that the virtual functions
> > are not needed. There may be virtual functions as implementor may be
> > polymorphic ... but those are not mandatory. So virtual functions
> > may be used when those improve performance, not when they describe
> > common interface.
>
> That doesn't make much sense to me. Surely every public function in your
> class has to look something like
>
> void interface::function (int x)
> {
> pimpl->function (x);
> }

Pimpl typically does not have exact same names in its interface:

void const* interface::address() const
{
return &pimpl_->data();
}

> which... is a virtual function table, just manually written out.

If something does not make sense to you then measure. ;)
Compilers are quite happily removing such thin one liner forwarding
functions by inlining. Nothing like that is done with vtable.

Rupert Swarbrick

unread,
Oct 23, 2013, 5:14:14 PM10/23/13
to
Ahah. I hadn't thought of that. Well, I guess that it will always be in
another translation unit (since if it wasn't, you wouldn't get the
compile time separation that's the whole point of the idiom). But I see
that a sufficiently clever compiler could inline it. Do existing
compilers deal with that though? It doesn't seem like something a linker
could/should do, and doesn't a compiler normally operate one translation
unit at once?

Rupert

Paavo Helde

unread,
Oct 24, 2013, 12:16:45 AM10/24/13
to
Rupert Swarbrick <rswar...@gmail.com> wrote in
news:l49e71$v72$2...@speranza.aioe.org:

> Paavo Helde <myfir...@osa.pri.ee> writes:
>> The address of the function to call is fixed at compile/link time, no
>> lookup needed from anywhere. Thus optimizers can also inline this
>> call (even if it is in another translation unit, though it is harder
>> then).
>
> Ahah. I hadn't thought of that. Well, I guess that it will always be
> in another translation unit (since if it wasn't, you wouldn't get the
> compile time separation that's the whole point of the idiom). But I
> see that a sufficiently clever compiler could inline it. Do existing
> compilers deal with that though? It doesn't seem like something a
> linker could/should do, and doesn't a compiler normally operate one
> translation unit at once?

This is called whole program optimization or link time optimization and
requires cooperation between compiler and linker. Basically compiler emits
(parts of) compiled code in some special or intermediate representation,
and the linker generates actual code. Both current MSVC and gcc support
this, don't know about others. The drawback is of course that the linking
step takes enormous time (compared to ordinary linking) and cannot be
(currently?) parallelized.

Cheers
Paavo

K. Frank

unread,
Oct 24, 2013, 8:22:25 AM10/24/13
to
Hello Paavo (and Rupert)!

On Thursday, October 24, 2013 12:16:45 AM UTC-4, Paavo Helde wrote:
> Rupert Swarbrick <reward...@nomail.com> wrote in
> ...
> > Paavo Helde <myfir...@is.paavo> writes:
> >> The address of the function to call is fixed at compile/link time, no
> >> lookup needed from anywhere. Thus optimizers can also inline this
> >> call (even if it is in another translation unit, though it is harder
> >> then).
> >
> > Ahah. I hadn't thought of that. Well, I guess that it will always be
> > in another translation unit (since if it wasn't, you wouldn't get the
> > compile time separation that's the whole point of the idiom). But I
> > see that a sufficiently clever compiler could inline it. Do existing
> > compilers deal with that though? It doesn't seem like something a
> > linker could/should do, and doesn't a compiler normally operate one
> > translation unit at once?
>
> This is called whole program optimization or link time optimization and
> requires cooperation between compiler and linker. Basically compiler emits
> (parts of) compiled code in some special or intermediate representation,
> and the linker generates actual code. Both current MSVC and gcc support
> this, don't know about others. The drawback is of course that the linking
> step takes enormous time (compared to ordinary linking) and cannot be
> (currently?) parallelized.

Of course, now if you change your pimpl implementation, you
have to go back and re-link-time-optimize the client code
that calls it, thus defeating one of the main benefits of the
pimpl (compiler firewall) idioim. Sure, technically, LTO is
not compilation, but it's a kissing cousin.

Well, you can't win for losing ...

>
> Cheers
> Paavo


Cherrio!


K. Frank

Paavo Helde

unread,
Oct 24, 2013, 11:53:44 AM10/24/13
to
"K. Frank" <kfran...@gmail.com> wrote in
news:8f2946be-e726-4cb0...@googlegroups.com:

>
> Of course, now if you change your pimpl implementation, you
> have to go back and re-link-time-optimize the client code
> that calls it, thus defeating one of the main benefits of the
> pimpl (compiler firewall) idioim.

Only in the (hopefully automated) release builds.

Cheers
Paavo

Rupert Swarbrick

unread,
Oct 24, 2013, 2:58:58 PM10/24/13
to
Thank you both! I've learned a lot from asking about this :-)

Rupert
0 new messages