Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Follow-up Pimpl question

Reply
Thread Tools

Follow-up Pimpl question

 
 
Rupert Swarbrick
Guest
Posts: n/a
 
      10-21-2013
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.
 
Reply With Quote
 
 
 
 
K. Frank
Guest
Posts: n/a
 
      10-22-2013
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
 
Reply With Quote
 
 
 
 
Tiib
Guest
Posts: n/a
 
      10-22-2013
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.
 
Reply With Quote
 
Tobias Müller
Guest
Posts: n/a
 
      10-22-2013
Rupert Swarbrick <(E-Mail Removed)> 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?
>
>
> 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.


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
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      10-22-2013
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
 
Reply With Quote
 
Rupert Swarbrick
Guest
Posts: n/a
 
      10-22-2013
"K. Frank" <(E-Mail Removed)> 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
 
Reply With Quote
 
Rupert Swarbrick
Guest
Posts: n/a
 
      10-22-2013
Tobias Müller <(E-Mail Removed)> 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
 
Reply With Quote
 
Rupert Swarbrick
Guest
Posts: n/a
 
      10-22-2013
James Kanze <(E-Mail Removed)> 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
 
Reply With Quote
 
Rupert Swarbrick
Guest
Posts: n/a
 
      10-22-2013
Öö Tiib <(E-Mail Removed)> 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
 
Reply With Quote
 
Tiib
Guest
Posts: n/a
 
      10-22-2013
On Tuesday, 22 October 2013 21:52:19 UTC+3, Rupert Swarbrick wrote:
> Tiib <(E-Mail Removed)> 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.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Objects containing a smart ptr (pimpl) and stored in STL containers - help please JackC C++ 3 08-13-2004 11:17 AM
Is the Bridge Pattern the same as Herb Sutter's Pimpl Idiom? Debajit Adhikary C++ 2 07-15-2004 01:25 PM
pimpl or precompiled headers? Asfand Yar Qazi C++ 6 04-30-2004 05:04 PM
pimpl, eliminating include files, encapsulation Simon Elliott C++ 6 01-18-2004 03:19 AM
Pimpl Idiom Icosahedron C++ 7 11-22-2003 11:34 PM



Advertisments