pimpl idiom vs. bridge design pattern
Asked Answered
P

5

35

I just noticed a new term pimpl idiom, what's the difference between this idiom with Bridge design pattern? I am confused about that.

I also noticed the pimpl idiom is always used for swap function, what's that? Could anybody give me an example?

Persuade answered 27/2, 2010 at 3:55 Comment(0)
A
54

PIMPL is a way of hiding the implementation, primarily to break compilation dependencies.

The Bridge pattern, on the other hand, is a way of supporting multiple implementations.

swap is a standard C++ function for exchanging the values of two objects. If you swap the pointer to the implementation for a different implementation, you are essentially changing the mechanism of the class at runtime.

But in its basic and common form, a class using PIMPL points to a single implementation, so there is no abstract class with distinct subclasses — just one class, forward declared, and compiled elsewhere. Changing the implementation class does not require any recompilation of sources that include the main header.

For example, say you have a lot of private member functions, private enums, and private data. And these private "bits" change fairly frequently as the class is developed and maintained. If the #include dependencies are such that touching this header file causes a large number of sources to be recompiled, you have a good candidate for PIMPL.

So the Bridge pattern is about object-oriented design, while the PIMPL idiom is about physical design of files.

(For more on physical design, I recommend the book Large-Scale C++ Software Design by John Lakos.)

Antimonic answered 27/2, 2010 at 4:20 Comment(3)
Just out of curiosity how does the approach in your example differ from compiling the example code as a shared or static library and than linking to the dependencies at their compile time?Dorotheadorothee
PIMPL may be even more important for libraries, to let you change the implementation of a class without changing the size of an instance. It all depends what your library has in its headers, I guess.Antimonic
So in other words its not an either or situation you can mix and match the compilation design.Dorotheadorothee
R
4

I have used pointer impl to hide implementation details from public interface/include file Basically interface look like this:

struct Interface {
private:
class Impl;
Impl *pImpl;
};

Then somewhere in inside Interface::Impl is defined and implemented, but details not exposed.

as far as swap, this is the first time I hear it. Can you elaborate on it?

Riot answered 27/2, 2010 at 4:11 Comment(0)
P
3

Pimpl: In short the pimpl pattern is really good for hiding the private implementation. If you wanted to expose your header files for clients that needed to build against your public interface but you didn't want to expose your private implementation details you could use the pimpl pattern to hide the details. You would do this for a few reasons but mainly your header file would not have to change when you change your private implementation details change which otherwise would force your clients to have to recompile. In this way you decouple along with hide your private implementation details. Usually you should hold the impl pointer in a RAII container like a unqiue pointer to make sure it is freed upon destruction.

Pimpl

    // my_class.h
    class my_class {
       public:
        // public here
    private:
       class impl;  //forward declare impl
       unique_ptr<impl> pimpl; // private implementation pointer
    };


    // my_class.cpp
    class my_class::impl {  // defined privately here
      // ... all private data and functions: all of these
      //     can now change without recompiling callers ...
    };
    my_class::my_class(): pimpl( new impl )
    {
      // ... set impl values ... 

       my_class& operator=(my_class other);
       friend void swap (my_class& lhs, myclass& rhs);  // used for assignment
    }

Swap probably came up because when you are doing an assignment operator for this class and implementing your own swap routine to assign the members you need to be aware of assigning this pointer as well

    my_class& my_class::operator=(my_class other)
    {
      swap(*this,other);
      return  *this;
    }

    void swap ( my_class& lhs, myclass& rhs )
    {
      using std::swap // now use default swap if override doesn't exist

      // call swap for other members
     //  swap (lhs.data,rhs.data);

      // call swap on unique_ptr
      lhs.pimpl.swap(rhs.pimpl);  // doesn't throw exceptions

    }

Bridge is a totally separate pattern that is used to bridge items together. The similarity between the patterns would be that you may have a process method in your class that hides the actual call since it will delegate this call to the contained object that will handle the actual method call. In other words if you had a request interface base class that contained a request implementor base class pointer. Therefore, at run time you could "bridge" the systems by initializing the bridge with a specific request implementor type but someone calling the bridge would just call the process request method which would delegate the call to the specific derived implementor request method at run time. There are several sources on google with nice diagrams that can explain this more clearly as well.

Putrescine answered 14/8, 2013 at 7:45 Comment(2)
yet much clear with the 'swap' need and concept. may be can you elaborate on "what if no swap?"Technician
If you didn't want a swap in the global namespace you could just remove the friend keyword on the swap method and keep it private with the private keyword in the class. If you didn't want assignment at all you could delete it by setting my_class & operator=(my_class const & other) = delete;Putrescine
M
1

There is currently a Boost library under review that implements the Pimpl pattern. I've cobbled together a few basic examples using the proposed Boost Pimpl implementation if anyone wants a jump on using this code internally:

https://github.com/sean-/Boost.Examples/tree/a148be39abcb21428857aa50495f8c352600741e/pimpl


UPDATE: The above link has been updated to point to the archived version. It doesn't seem likely that Boost.Pimpl will be accepted in to boost at this point in time in favor of std::unique_ptr<> as a viable replacement (albeit less complete than Boost.Pimpl for some less common usecases).

If you have access to C++11, you are probably better off using std::unique_ptr<> as a PIMPL implementation:

class MyClass {
 public:
  // ...
 private:
  class Impl;
  std::unique_ptr<Impl> impl_;
};

You can see a full example using std::unique_ptr<> in my C++11 reentrant class locking strategy question.

Using std::unique_ptr<>'s swap() method, it becomes convenient and very practical for C++11's move semantics to move the guts of an object from one owner to another. For example, suppose MyClass exports a swap() implementation that just forwards to std::unique_ptr<>'s swap:

void MyClass::swap(MyClass *c) { impl_.swap(c); }
MyClass c1, c2;
c1.swap(c2);

This is now an exception safe way of transferring c2's contents to c1. Another great reason to use a the PIMPL idiom is to preserve/maintain a stable ABI.

Minima answered 31/5, 2011 at 23:43 Comment(2)
The link is dead as of 2013.Banal
Updated the link. At this point if you are on C++11, you are probably much better off using std::unique_ptr<>.Minima
J
0

Well, here is PIMPL idiom: http://en.wikipedia.org/wiki/Opaque_pointer It is pretty clear what it does.

And Bridge Pattern is more involved - it does not just hold data. http://en.wikipedia.org/wiki/Bridge_pattern#C.2B.2B

Jeanelle answered 27/2, 2010 at 4:7 Comment(1)
Downvoted because the Opaque_pointer article says: "This technique is described in Design Patterns as the Bridge pattern. It is sometimes referred to as "handle classes",[2] the "Pimpl idiom" " So, doesn't answer the question clearly.Congratulatory

© 2022 - 2024 — McMap. All rights reserved.