Compilation dependency and the pimpl idiom

This is a discussion on Compilation dependency and the pimpl idiom within the C++ Programming forums, part of the General Programming Boards category; So, I just read through Item 31 in Effective C++ and it confused the hell out of me. The idea ...

  1. #1
    Registered User ex-mortis's Avatar
    Join Date
    Mar 2012
    Posts
    37

    Compilation dependency and the pimpl idiom

    So, I just read through Item 31 in Effective C++ and it confused the hell out of me. The idea is to minimize compilation on the client's part and I get why it's desirable, but I just don't see how it's possible. In order for code to run it needs to be compiled, right? And therefore if you change anything pertaining to it, it needs to be compiled again. There's no way the changes would show up unless they are made at runtime. One part that confused me in particular was his use of the pimpl idiom to separate the interface and the implementation:

    Code:
    #include <string>
    
    #include <memory>
    
    class PersonImpl;
    
    class Date;
    class Address;
    
    class Person {
    public:
       Person(const std::string& name, const Date& birthday, const Address& addr);
       std::string name() const;
       std::string birthDate() const;
       std::string address() const;
       …
    
    private:
       std::tr1::shared_ptr<PersonImpl> pImpl;
    };
    And this is the explanation that follows:

    "Here, the main class contains as a data member nothing but a pointer to its implementation class.

    With this design, clients of Person are divorced from the details of dates, addresses, and persons. The implementations of those classes can be modified at will, but Person clients need not recompile."

    I don't understand that. Why would they not need to recompile if the implementation has been changed? If he is talking about changes at runtime, can't you also modify the data members through member functions at runtime with no need to compile? I really have no idea what he's talking about.
    Last edited by ex-mortis; 06-30-2012 at 12:14 PM.

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    21,442
    Quote Originally Posted by ex-mortis
    Why would they not need to recompile if the implementation has been changes?
    From the point of view of the client code, the implementation has not changed. The header is exactly the same, so the resulting translation unit (header files + source file) is exactly the same. What you do need to do is to link to the new "real implementation", but that is different from re-compiling source code.
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Registered User ex-mortis's Avatar
    Join Date
    Mar 2012
    Posts
    37
    How is it the same, exactly? I mean, assume we are just talking about int x = 9 and then later you change it to int x = 8. Hasn't the implementation changed? The pointer may remain constant, but somewhere you need to define the pointer, and the changes are going to show up there. Or do I just not understand the concept?

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    21,442
    Quote Originally Posted by ex-mortis
    How is it the same, exactly?
    It really is exactly the same. Absolutely nothing has changed.

    Quote Originally Posted by ex-mortis
    I mean, assume we are just talking about int x = 9 and then later you change it to int x = 8. Hasn't the implementation changed?
    Yes, but now we are talking about int x = y; where y is from elsewhere (extern). Later, you change the value of y from 9 to 8, but I really don't have to care because y is from elsewhere.

    Quote Originally Posted by ex-mortis
    The pointer may remain constant, but somewhere you need to define the pointer, and the changes are going to show up there.
    Yes, hence you need to compile the code that implements the PersonImpl class and re-compile the code that implements the Person class (because the PersonImpl class definition will have been changed, and it would be included in the source file that implements the Person class). What you don't need to do is re-compile other code that uses the Person class.
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #5
    Registered User ex-mortis's Avatar
    Join Date
    Mar 2012
    Posts
    37
    Ah, I understand now. But how does using a pointer to implementation help compilation dependency? It seems to me that if the implementation is located within the class, and the data members are safe and private, that the changes in other source files would also be "instantenous" without the pimpl.

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    21,442
    Quote Originally Posted by ex-mortis
    But how does using a pointer to implementation help compilation dependency? It seems to me that if the implementation is located within the class, and the data members are safe and private, that the changes in other source files would also be "instantenous" without the pimpl.
    No, because those other source files include that header that contains the modified class definition. Since you change the header, you need to re-compile those source files that include that header. The physical source files didn't change, but the translation units changed.
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  7. #7
    Registered User ex-mortis's Avatar
    Join Date
    Mar 2012
    Posts
    37
    Quote Originally Posted by laserlight View Post
    No, because those other source files include that header that contains the modified class definition. Since you change the header, you need to re-compile those source files that include that header. The physical source files didn't change, but the translation units changed.
    Yeah, that makes sense. Still, it doesn't answer my question regarding the use of the pimpl idiom. Why is it necessary exactly?

  8. #8
    Registered User whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    7,638
    One of the nice things about C++ that comes from C is the ability to declare types as opposed to defining them, and use pointers to the incomplete type. For operations like dereferencing to take place the type will need to be complete (as in defined), but you won't need to do that in header files.

    So you can use the pimpl idiom to hide the implementation details of your class from the header file, and keep any secrets a secret. This way, you can provide people with something they can link, and a header file. Because the header file is human readable, people will be able to use your class without necessarily understanding how it works, which is in the part that is not human readable.

    And when you update the implementation, people will not necessarily need a new header file. They could just link against the new version. The only time you would necessarily need both is if the exposed class is changed.

  9. #9
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    4,176
    O_o

    Be aware that the "Pimpl" idiom can be as costly to use and maintain as simple interface classes with virtual functions which offer almost the exact same possibilities.

    Consider using the "Pimpl" idiom only when you are already intending to employee the "Bridge" pattern.

    Soma

  10. #10
    Registered User ex-mortis's Avatar
    Join Date
    Mar 2012
    Posts
    37
    Quote Originally Posted by whiteflags View Post
    One of the nice things about C++ that comes from C is the ability to declare types as opposed to defining them, and use pointers to the incomplete type. For operations like dereferencing to take place the type will need to be complete (as in defined), but you won't need to do that in header files.

    So you can use the pimpl idiom to hide the implementation details of your class from the header file, and keep any secrets a secret. This way, you can provide people with something they can link, and a header file. Because the header file is human readable, people will be able to use your class without necessarily understanding how it works, which is in the part that is not human readable.

    And when you update the implementation, people will not necessarily need a new header file. They could just link against the new version. The only time you would necessarily need both is if the exposed class is changed.
    Awesome, I get it. Thanks everyone.

    Quote Originally Posted by phantomotap View Post
    O_o

    Be aware that the "Pimpl" idiom can be as costly to use and maintain as simple interface classes with virtual functions which offer almost the exact same possibilities.

    Consider using the "Pimpl" idiom only when you are already intending to employee the "Bridge" pattern.

    Soma

    What is the "Bridge" pattern?

  11. #11
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    4,176
    What is the "Bridge" pattern?
    The internet is your friend.

    Essentially it is jargon used to communicate a design approach to others.

    You may search to find more details of this specific instance.

    Soma

  12. #12
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,590
    PIMPL is one nice way to get around exporting STL containers from dlls. Since the PIMPL does not contain the STL container but points to a class that does the compiler won't complain about exporting the PIMPL. This is nice when you are creating a dll that might be around for a very long time which pretty much guarantees the version of compiler used to compile the dll will eventually not be the same version of compiler used to compile the dll. With PIMPL the code will not break (at least in the area we are discussing) if you compile the client with MSVS 2010 and compile the DLL with MSVS 2003. You might say why not compile both every time you change one or the other? This would be a huge nightmare in a large system. The client code might change a hundred times more than the dll code and interface.

    I recommend you purchase and read this book:
    http://www.amazon.com/Design-Pattern...esign+patterns
    Last edited by VirtualAce; 07-05-2012 at 05:23 PM.

  13. #13
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    Quote Originally Posted by phantomotap View Post
    Be aware that the "Pimpl" idiom can be as costly to use and maintain
    How so?

    as simple interface classes with virtual functions which offer almost the exact same possibilities.
    And the downside of only being usable through dynamic allocation and pointers. Now you've changed client syntax compared to normal objects. Also, you need a factory function for the concrete implementation class.

    Consider using the "Pimpl" idiom only when you are already intending to employee the "Bridge" pattern.
    I would offer the opposite advice. Use interface classes only when you are already intending to employ polymorphism. And when you want to use polymorphism to implement the State pattern, make the implementation class polymorphic and stay with Pimpl.

    Sebastian
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  14. #14
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    4,176
    And the downside of only being usable through dynamic allocation and pointers.
    O_o

    You can use references instead of pointers if you find "->" so significant.

    The "Pimpl" still does the allocation.

    Now you've changed client syntax compared to normal objects.
    Interfaces where `Type &' and `const Type &' are used work for both.

    you need a factory function for the concrete implementation class.
    That's what the "Pimpl" constructor does in any event.

    The "Pimpl" only hides the creation behind an opaque pointer and a constructor instead of a simple factory function in the interface.

    I would offer the opposite advice.
    That advice isn't opposite to what I offered.

    If you need to hide all implementation details, which is the only reason "Pimpl" exists, you only have a few practical choices.

    Inheritance based polymorphisms is one of the practical choices.

    The "Pimpl" idiom is one of the practical choices.

    Interface only abstractions is one of the practical choices.

    You are just suggesting that one should avoid introducing inheritance based polymorphisms only to hide implementation details. That's good advice. That isn't the opposite of my advice. Your advice simply offers more considerations to my advice. The opposite would be "use inheritance based polymorphisms only when you are already intending to employee the "Bridge" pattern".

    Soma

  15. #15
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    Quote Originally Posted by phantomotap View Post
    You can use references instead of pointers if you find "->" so significant.
    It's not about the allocation itself or the use of ->. It's that I like value semantics. A class implemented using Pimpl doesn't look any different to the user than a simple class that has all the details in the primary definition. An interface that is supposed to be used with a factory function and pointers does.

    Interfaces where `Type &' and `const Type &' are used work for both.
    True for further users. Not true for the primary user, i.e. the owner of the object. With Pimpl, the owner simply holds a variable of the object type. With interfaces, the owner holds a (hopefully smart) pointer.

    The "Pimpl" only hides the creation behind an opaque pointer and a constructor instead of a simple factory function in the interface.
    But that's exactly what I like about Pimpl.

    That advice isn't opposite to what I offered.
    I understood your advice as, "If you want to hide implementation details, use interfaces, unless you already intend to use the Bridge pattern." So interfaces by default, Pimpl as a special case. I recommend Pimpl by default, and interfaces only as a special case. Sounds like the opposite to me

    If you need to hide all implementation details, which is the only reason "Pimpl" exists,
    Not quite. Another reason would be copy-on-write semantics. Just to throw an idea out there.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Page 1 of 2 12 LastLast
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Understanding forward declarations of classes, pimpl
    By Boxknife in forum C++ Programming
    Replies: 2
    Last Post: 04-22-2010, 01:29 AM
  2. Identifying a C++ idiom
    By kovacsbv in forum C++ Programming
    Replies: 2
    Last Post: 08-10-2009, 02:50 PM
  3. pimpl confuses me
    By manav in forum C++ Programming
    Replies: 8
    Last Post: 03-30-2008, 10:31 PM
  4. Inheritance in Pimpl idiom
    By George2 in forum C++ Programming
    Replies: 4
    Last Post: 03-19-2008, 09:32 PM
  5. Pimpl Idiom client/serve compile/link
    By George2 in forum C++ Programming
    Replies: 12
    Last Post: 03-15-2008, 06:25 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21