Follow-up Pimpl question

Discussion in 'C++' started by Rupert Swarbrick, Oct 21, 2013.

  1. 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.
    Rupert Swarbrick, Oct 21, 2013
    #1
    1. Advertising

  2. Rupert Swarbrick

    K. Frank Guest

    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
    K. Frank, Oct 22, 2013
    #2
    1. Advertising

  3. Rupert Swarbrick

    Öö Tiib Guest

    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.
    Öö Tiib, Oct 22, 2013
    #3
  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?
    >
    >
    > 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
    Tobias Müller, Oct 22, 2013
    #4
  5. Rupert Swarbrick

    James Kanze Guest

    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
    James Kanze, Oct 22, 2013
    #5
  6. "K. Frank" <> 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, Oct 22, 2013
    #6
  7. Tobias Müller <> 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, Oct 22, 2013
    #7
  8. James Kanze <> 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, Oct 22, 2013
    #8
  9. Öö Tiib <> 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
    Rupert Swarbrick, Oct 22, 2013
    #9
  10. Rupert Swarbrick

    Öö Tiib Guest

    On Tuesday, 22 October 2013 21:52:19 UTC+3, Rupert Swarbrick wrote:
    > Öö Tiib <> 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.
    Öö Tiib, Oct 22, 2013
    #10
  11. Paavo Helde <> writes:
    > Rupert Swarbrick <> 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).


    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
    Rupert Swarbrick, Oct 23, 2013
    #11
  12. Rupert Swarbrick

    K. Frank Guest

    Hello Paavo (and Rupert)!

    On Thursday, October 24, 2013 12:16:45 AM UTC-4, Paavo Helde wrote:
    > Rupert Swarbrick <> wrote in
    > ...
    > > Paavo Helde <> 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
    K. Frank, Oct 24, 2013
    #12
  13. Paavo Helde <> writes:
    > "K. Frank" <> wrote in
    > news::
    >> 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.


    Thank you both! I've learned a lot from asking about this :)

    Rupert
    Rupert Swarbrick, Oct 24, 2013
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Icosahedron

    Pimpl Idiom

    Icosahedron, Nov 20, 2003, in forum: C++
    Replies:
    7
    Views:
    684
    Icosahedron
    Nov 22, 2003
  2. Simon Elliott
    Replies:
    6
    Views:
    394
    Thomas Wintschel
    Jan 18, 2004
  3. Asfand Yar Qazi

    pimpl or precompiled headers?

    Asfand Yar Qazi, Apr 29, 2004, in forum: C++
    Replies:
    6
    Views:
    492
    Siemel Naran
    Apr 30, 2004
  4. Debajit  Adhikary
    Replies:
    2
    Views:
    2,019
    Christopher Benson-Manica
    Jul 15, 2004
  5. JackC
    Replies:
    3
    Views:
    579
    Alan Griffiths
    Aug 13, 2004
Loading...

Share This Page