Why should the "PIMPL" idiom be used? [duplicate]
Asked Answered
S

11

152

Backgrounder:

The PIMPL Idiom (Pointer to IMPLementation) is a technique for implementation hiding in which a public class wraps a structure or class that cannot be seen outside the library the public class is part of.

This hides internal implementation details and data from the user of the library.

When implementing this idiom why would you place the public methods on the pimpl class and not the public class since the public classes method implementations would be compiled into the library and the user only has the header file?

To illustrate, this code puts the Purr() implementation on the impl class and wraps it as well.

Why not implement Purr directly on the public class?

// header file:
class Cat {
    private:
        class CatImpl;  // Not defined here
        CatImpl *cat_;  // Handle

    public:
        Cat();            // Constructor
        ~Cat();           // Destructor
        // Other operations...
        Purr();
};


// CPP file:
#include "cat.h"

class Cat::CatImpl {
    Purr();
...     // The actual implementation can be anything
};

Cat::Cat() {
    cat_ = new CatImpl;
}

Cat::~Cat() {
    delete cat_;
}

Cat::Purr(){ cat_->Purr(); }
CatImpl::Purr(){
   printf("purrrrrr");
}
Scolopendrid answered 13/9, 2008 at 14:30 Comment(8)
Because the PIMP idiom should be avoided?..Interaction
Excellent answer, and I found this link contains comprehensive information as well: marcmutz.wordpress.com/translated-articles/pimp-my-pimplSelfsealing
If you want to do the maintenance coder coming after you a favor, remember that this is an interface pattern. Don't use it for every internal class there is. To quote Blade Runner, I've seen s*** you people wouldn't believe.Asquith
be careful, PIMPL can have a lot of benefits, especially in larger projects, but can seriously complicate an otherwise simple smaller program. Somewhere down the line in this question there was a list of minimal "prerequisites" for using PIMPL in a project. Not everyone should follow the same list, make one for yourself, and stick to it. That is probably the best way to do it, in my opinion.Canonry
My own experience is, pimpl is preferred by people producing big undocumented frameworks and then leaving the company, so their former collegues have to deal with classed beeing extra hard to analyze... .Rhinoscopy
#2346663Kinescope
1/2: FYI most of the answers didn't say anything about why you might want to use PIMPL, and some answers simple said "PIMPL bad, avoid", which indicates a complete misunderstanding of what PIMPL is for. One obvious reason to use it is when you pass an object to a function by value and want the copy operation to be lightweight. In such situations where you cannot pass by reference you minimize the amount of data in the class to be copied. Hence you pass a class containing only a single pointer.Jackinthepulpit
2/2: There are other reasons, for example to seperate out interdependencies in a single class inheritance hierachy into 2 or more independent class hierachies. Examples of this are difficult to explain, but there are books which cover this. One example is Design Patterns, see the section on "Bridge" Pattern. I think the example given there is that of implementing a window system where different windows are part of one hierachy and different window system implementations are put into another hierachy. It avoids the "N*M problem".Jackinthepulpit
A
42
  • Because you want Purr() to be able to use private members of CatImpl. Cat::Purr() would not be allowed such an access without a friend declaration.
  • Because you then don't mix responsibilities: one class implements, one class forwards.
Addicted answered 13/9, 2008 at 15:16 Comment(4)
It's a pain to maintain though. But then again, if it is a library class the methods should not be changed much anyway. The code I'm looking at seems to be taking the safe road and using pimpl everywhere.Scolopendrid
Isn't this line illegal since all members are private: cat_->Purr(); Purr() is not accessible from the outside because by deafult it's private. What am I missing here?Hiragana
Both points do not make any sense. If you had one class - Cat it would also be able to access its members and it wouldn't "mix reposibilities", because it would be the class that "implements". The reasons for using PIMPL are different.Dissimilation
This answer is just plain wrong, for the reasons @doc mentioned. It would be absurd to introduce a new class just for the sake of using its private members and it's not a "responsibility" to just forward something!Wiles
M
76

I think most people refer to this as the Handle Body idiom. See James Coplien's book Advanced C++ Programming Styles and Idioms. It's also known as the Cheshire Cat because of Lewis Caroll's character that fades away until only the grin remains.

The example code should be distributed across two sets of source files. Then only Cat.h is the file that is shipped with the product.

CatImpl.h is included by Cat.cpp and CatImpl.cpp contains the implementation for CatImpl::Purr(). This won't be visible to the public using your product.

Basically the idea is to hide as much as possible of the implementation from prying eyes.

This is most useful where you have a commercial product that is shipped as a series of libraries that are accessed via an API that the customer's code is compiled against and linked to.

We did this with the rewrite of IONA's Orbix 3.3 product in 2000.

As mentioned by others, using his technique completely decouples the implementation from the interface of the object. Then you won't have to recompile everything that uses Cat if you just want to change the implementation of Purr().

This technique is used in a methodology called design by contract.

Mcduffie answered 13/9, 2008 at 14:51 Comment(7)
@Rob, I'm guessing there very little overhead to this. An extra class, but there is very little to them. Just a thin wrapper around the existing class. Someone correct me if I'm wrong, but the memory usage would just be an extra function table in RAM, a pointer to the pimpl and a redirecting function for each method in codespace. Painful for maintenance and debugging though.Scolopendrid
How is the pimpl idiom (or handle-body idiom as you call it) used in design by contract?Milla
Hi Andreas, Your point of interface to an API user is only in the exposed contract (handle) and not in the way in which you implement the body to provide the advertised functionality. You are free to change the implementation as you see fit, provided that you do not change the semantics of the API that you have advertised.Mcduffie
@RobWells I downvoted this answer, because it's also wrong (but not as wrong as the accepted one; this one could potentially be fixed). Issues: a) "Basically the idea is to hide as much as possible of the implementation from prying eyes." How does it differ from a simple .h/.cpp class declaration/definition separation and shipping library as .h/(.a|.lib)? It also, obviously, hides the implementation from the client. The OP mentioned this explicitly in the question!Wiles
b) "We did this with the rewrite of IONAs Orbix 3.3 product in 2000." That's cool, I guess. c) "using his technique completely decouples the implementation from the interface of the object. Then you won't have to recompile everything that uses Cat if you just want to change the implementation of Purr()." It's just not true. Changing the implementation of a method never triggers a recompilation. It will require a re-link. But is it surprising? You changed a code, you want the new version to be used.Wiles
d) " It's also known as the Cheshire Cat because of Lewis Caroll's character that fades away until only the grin remains." Source, please? e) "This technique is used in a methodology called design by contract." Source, please?Wiles
FFR: Basically refer to the accepted answer in the #8973088Wiles
A
42
  • Because you want Purr() to be able to use private members of CatImpl. Cat::Purr() would not be allowed such an access without a friend declaration.
  • Because you then don't mix responsibilities: one class implements, one class forwards.
Addicted answered 13/9, 2008 at 15:16 Comment(4)
It's a pain to maintain though. But then again, if it is a library class the methods should not be changed much anyway. The code I'm looking at seems to be taking the safe road and using pimpl everywhere.Scolopendrid
Isn't this line illegal since all members are private: cat_->Purr(); Purr() is not accessible from the outside because by deafult it's private. What am I missing here?Hiragana
Both points do not make any sense. If you had one class - Cat it would also be able to access its members and it wouldn't "mix reposibilities", because it would be the class that "implements". The reasons for using PIMPL are different.Dissimilation
This answer is just plain wrong, for the reasons @doc mentioned. It would be absurd to introduce a new class just for the sake of using its private members and it's not a "responsibility" to just forward something!Wiles
T
24

For what is worth, it separates the implementation from the interface. This is usually not very important in small size projects. But, in large projects and libraries, it can be used to reduce the build times significantly.

Consider that the implementation of Cat may include many headers, may involve template meta-programming which takes time to compile on its own. Why should a user, who just wants to use the Cat have to include all that? Hence, all the necessary files are hidden using the pimpl idiom (hence the forward declaration of CatImpl), and using the interface does not force the user to include them.

I'm developing a library for nonlinear optimization (read "lots of nasty math"), which is implemented in templates, so most of the code is in headers. It takes about five minutes to compile (on a decent multi-core CPU), and just parsing the headers in an otherwise empty .cpp takes about a minute. So anyone using the library has to wait a couple of minutes every time they compile their code, which makes the development quite tedious. However, by hiding the implementation and the headers, one just includes a simple interface file, which compiles instantly.

It does not necessarily have anything to do with protecting the implementation from being copied by other companies - which wouldn't probably happen anyway, unless the inner workings of your algorithm can be guessed from the definitions of the member variables (if so, it is probably not very complicated and not worth protecting in the first place).

Tax answered 23/9, 2014 at 11:18 Comment(0)
T
15

If your class uses the PIMPL idiom, you can avoid changing the header file on the public class.

This allows you to add/remove methods to the PIMPL class, without modifying the external class's header file. You can also add/remove #includes to the PIMPL too.

When you change the external class's header file, you have to recompile everything that #includes it (and if any of those are header files, you have to recompile everything that #includes them, and so on).

Thermoscope answered 13/9, 2008 at 15:28 Comment(0)
B
7

Typically, the only reference to a PIMPL class in the header for the owner class (Cat in this case) would be a forward declaration, as you have done here, because that can greatly reduce the dependencies.

For example, if your PIMPL class has ComplicatedClass as a member (and not just a pointer or reference to it) then you would need to have ComplicatedClass fully defined before its use. In practice, this means including file "ComplicatedClass.h" (which will also indirectly include anything ComplicatedClass depends on). This can lead to a single header fill pulling in lots and lots of stuff, which is bad for managing your dependencies (and your compile times).

When you use the PIMPL idiom, you only need to #include the stuff used in the public interface of your owner type (which would be Cat here). Which makes things better for people using your library, and means you don't need to worry about people depending on some internal part of your library - either by mistake, or because they want to do something you don't allow, so they #define private public before including your files.

If it's a simple class, there's usually isn't any reason to use a PIMPL, but for times when the types are quite big, it can be a big help (especially in avoiding long build times).

Basiliabasilian answered 13/9, 2008 at 14:53 Comment(0)
C
4

Well, I wouldn't use it. I have a better alternative:

File foo.h

class Foo {
public:
    virtual ~Foo() { }
    virtual void someMethod() = 0;

    // This "replaces" the constructor
    static Foo *create();
}

File foo.cpp

namespace {
    class FooImpl: virtual public Foo {

    public:
        void someMethod() {
            //....
        }
    };
}

Foo *Foo::create() {
    return new FooImpl;
}

Does this pattern have a name?

As someone who is also a Python and Java programmer, I like this a lot more than the PIMPL idiom.

Constabulary answered 21/6, 2011 at 13:30 Comment(4)
If you are already limiting yourself to a factory approach to object creation, then this is fine. But it completely eliminates value semantics, whereas a traditional pImpl works with either method.Hamite
Well pImpl basicly just wraps the pointer. All you need to do is to make the create() above return a PointerWrapperWithCopySemantics<Foo> :-). I usually do the opposit, and return an std::auto_ptr<Foo>.Constabulary
Why inheritance over composition? Why add the overhead of a vtable? Why virtual inheritance?Doldrums
This doesn't work with template methodsBusiness
L
3

Placing the call to the impl->Purr inside the .cpp file means that in the future you could do something completely different without having to change the header file.

Maybe next year they discover a helper method they could have called instead and so they can change the code to call that directly and not use impl->Purr at all. (Yes, they could achieve the same thing by updating the actual impl::Purr method as well, but in that case you are stuck with an extra function call that achieves nothing but calling the next function in turn.)

It also means the header only has definitions and does not have any implementation which makes for a cleaner separation, which is the whole point of the idiom.

Lakeesha answered 13/9, 2008 at 14:45 Comment(0)
S
3

We use the PIMPL idiom in order to emulate aspect-oriented programming where pre, post and error aspects are called before and after the execution of a member function.

struct Omg{
   void purr(){ cout<< "purr\n"; }
};

struct Lol{
  Omg* omg;
  /*...*/
  void purr(){ try{ pre(); omg-> purr(); post(); }catch(...){ error(); } }
};

We also use a pointer-to-base class to share different aspects between many classes.

The drawback of this approach is that the library user has to take into account all the aspects that are going to be executed, but only sees his/her class. It requires browsing the documentation for any side effects.

Shoop answered 11/11, 2014 at 18:20 Comment(1)
Putting the compilation speed argument aside, this is the most beneficial thing PIMPL provides, IMO.Amye
D
1

I just implemented my first PIMPL class over the last couple of days. I used it to eliminate problems I was having, including file *winsock2.*h in Borland Builder. It seemed to be screwing up struct alignment and since I had socket things in the class private data, those problems were spreading to any .cpp file that included the header.

By using PIMPL, winsock2.h was included in only one .cpp file where I could put a lid on the problem and not worry that it would come back to bite me.

To answer the original question, the advantage I found in forwarding the calls to the PIMPL class was that the PIMPL class is the same as what your original class would have been before you pimpl'd it, plus your implementations aren't spread over two classes in some weird fashion. It's much clearer to implement the public members to simply forward to the PIMPL class.

Like Mr Nodet said, one class, one responsibility.

Dc answered 24/3, 2009 at 16:34 Comment(0)
S
0

I don't know if this is a difference worth mentioning but...

Would it be possible to have the implementation in its own namespace and have a public wrapper / library namespace for the code the user sees:

catlib::Cat::Purr(){ cat_->Purr(); }
cat::Cat::Purr(){
   printf("purrrrrr");
}

This way all library code can make use of the cat namespace and as the need to expose a class to the user arises a wrapper could be created in the catlib namespace.

Scolopendrid answered 13/9, 2008 at 15:31 Comment(0)
C
0

I find it telling that, in spite of how well-known the PIMPL idiom is, I don't see it crop up very often in real life (e.g., in open source projects).

I often wonder if the "benefits" are overblown; yes, you can make some of your implementation details even more hidden, and yes, you can change your implementation without changing the header, but it's not obvious that these are big advantages in reality.

That is to say, it's not clear that there's any need for your implementation to be that well hidden, and perhaps it's quite rare that people really do change only the implementation; as soon as you need to add new methods, say, you need to change the header anyway.

Crosson answered 15/9, 2008 at 9:54 Comment(2)
Yes, it only makes a difference if you can pick a good public interface and stick to it. As Rob Wells mentions, this is important if you need to distribute updated libraries to people linking against compiled versions of your library without forcing them to recompile -- you just supply a new DLL and voila. Mind you, using interfaces (== abstract classes without data members in C++) achieves much the same thing with less maintenance (no need to manually forward each public method). But OTOH you must use special syntax to create instances (i.e. invoke a factory method).Halfmoon
Qt uses PIMPL idiom extensively.Scourge

© 2022 - 2024 — McMap. All rights reserved.