| ||||
| Comment on GotW #7b Solution: Minimizing Compile-Time Dependencies, Part 2 by Ralph Tandetzky
I just looked at the next question and figured out that it asks for what I already noticed. Sorry, I should have read on first. But here’s one more note: If both Comment on GotW #7b Solution: Minimizing Compile-Time Dependencies, Part 2 by Ralph Tandetzky
You can still remove one more header which is Comment on GotW #7b Solution: Minimizing Compile-Time Dependencies, Part 2 by Herb Sutter
@Marcelo: using namespace std; is perfectly fine (and should be encouraged) in an implementation file after all #includes. See C++ Coding Standards Item 59: “Don’t write namespace usings in a header file or before an #include.” However, you’re right that saying “std::” in front of the list member is now redundant. Removed, with credit. Thanks! Read More »Comment on GotW #7b Solution: Minimizing Compile-Time Dependencies, Part 2 by Marcelo Pinto
Shouldn’t the standard headers come after our own header to avoid hiding some including errors in our own headers? And in the last code snippet you have “using namespace std” (that I don’t like very much) and then you have “std::list…”. Don’t you think you should avoid “using namespace std” in such a short code? Best regards Read More »Comment on GotW #7c: Minimizing Compile-Time Dependencies, Part 3 by Roman M.
Hello Herb, - dependency rules are often not obvious, so a conservative developer will rather include too much instead of risking a broken build; Some time ago I’ve create an issue on User-Voice (http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2680137-make-header-include-refactoring-possible). I understand that creating good solution for compile-time issues is far from trivial, but currently I would probably invest in better hardware instead of wasting my time on PIMPL Read More »Comment on GotW #7c: Minimizing Compile-Time Dependencies, Part 3 by Phil Nash
@Herb. This is not a response to your questions, but is pertinent to the topic. I’ve been following this series with interest to see if I can pick up any more tips in my war against build-time sapping dependencies. While it’s all been good stuff and covered well I’ve not seen anything new – yet I still suffer from productivity killing compile times in several projects I’m working on (to be clear, these are mostly legacy code bases where it’s too late for the large scale refactoring necessary to take advantage of some of these techniques on enough of the code-base – or the runtime cost is too high – e.g. with the pImpl idiom). In the first post you alluded to modules – which I think should be a huge win in this area. But when I asked Bjarne about it earlier this year he seemed to think they weren’t even on the radar for C++17 at this point. Is that still the case? Can you give any more insight here? Will you really be covering it in an upcoming post? Read More »Comment on GotW #7c: Minimizing Compile-Time Dependencies, Part 3 by Phil Nash
@Matthieu (& @herb) the fixed sized block of storage with placement new (and explicit destructor) is the usual solution. As Herb says adding a bit of extra headroom is usually a good idea. However, it may also be worth considering using a pool allocator, or some other more efficient allocator (I’ve found tbb’s scalable_allocator to be a very good general purpose allocator) to keep the size dynamic (and thus always correct), but with a much smaller overhead. You can’t beat the near zero overhead of the obtrusive storage block but a decent allocator may be good enough for a lot of cases where the std allocator isn’t. Read More »Comment on GotW #7b Solution: Minimizing Compile-Time Dependencies, Part 2 by Mike Capp
Given that pimpl introduces an allocation anyway, I’ve never really seen what benefits it offers that couldn’t be provided more cleanly and flexibly by a factory returning a unique_ptr to a (probably abstract) base class. Pimpl trades vtable indirection for data-access indirection, which isn’t an obvious win, and makes subclassing a good deal uglier since you need to subclass both handle and body types. Could you expand on where you see the relative merits here? Read More »Comment on GotW #7b Solution: Minimizing Compile-Time Dependencies, Part 2 by Ralph Tandetzky
@Mike: For polymorphic classes your approach is an alternative. But for value classes (types being copyable and movable) a base class could introduce slicing problems. Instead of copying you would have to clone your value type. Hence normal copy semantics are impossible because of this implementation detail. The idea is to preserve the semantics of the class and only touch the internals in the private section of the class. Even in the case of a class hierarchy pimpling is sometimes preferred since it’s less code to write. Think about it: You have to create an interface class and mirror the functions there in the implementation class. This is not necessary when pimpling and only those functions are virtual which really need to be virtual giving the reader of your code an idea of intent. He or she can then know which functions may be customized. Read More »Comment on GotW #7b Solution: Minimizing Compile-Time Dependencies, Part 2 by nosenseetal
@herb: Comment on GotW #7c: Minimizing Compile-Time Dependencies, Part 3 by germinolegrand
1) Inheritance is the tightest coupling you can ever find in C++ (inheritance with virtual overriding really is the most complicated dependency to hide). These are the ones that determine the size of the class, which is what the compiler always need. Interestingly, these also are the ones that can’t be recursive, thus it makes it a solvable problem (no cyclic inclusion) without any indirection. 2) The private inheritance to class B, that has no virtual functions, can be transformed into a private member. It has no consequence on the size, unless sizeof B is zero (as a member it is required to be at least 1 if i make no mistake). Anyway, if it is private member, it can become a member of the pimpl and simply disappear from class X. Another way of having class B disappear from class X, but keeping inheriting from class B (perhaps to access some protected things, or to keep its zero size), is to simply make struct impl publicly inherit from class B (so that it can be accessed from class X, but needing some proxy functions in struct impl to access the protected part of class B). Once any of those two solutions are applied, the #include “B.h” can be removed from x.h and added in x.cpp. It’s worth mentionning that the order in which class X inherits from class A then from class B permits to preserve construction order while sending class B to the pimpl. If class B really needed to be constructed before (or deleted after) class A, this wouldn’t be possible. It’s so easy to mess up the construction/destruction order by playing with dependancies. The code for x.h now looks like this (a forward-declaration to class B is needed for the f function): // x.h: after converting to use a Pimpl to hide implementation details // #include <iosfwd> #include <memory> #include "a.h" // class A (has virtual functions) class B; class C; class E; class X : public A { public: X( const C& ); B f( int, char* ); C f( int, C ); C& g( B ); E h( E ); virtual std::ostream& print( std::ostream& ) const; private: struct impl; std::unique_ptr<impl> pimpl; // ptr to a forward-declared class }; std::ostream& operator<<( std::ostream& os, const X& x ) { return x.print(os); } and the x.cpp like that : // Implementation file x.cpp // #include <list> #include "b.h" // class B (has no virtual functions) #include "c.h" // class C #include "d.h" // class D using namespace std; struct X::impl: public B { std::list<C> clist; D d; }; X::X() : pimpl{ make_unique<X::impl>(/*...*/) } { } X::~X() =default; The last trick is not needed in the GotW question, but might be interesting. What if class B had virtual functions overrided in class X ? The solution may have an extra cost, with crossed dependancies between class X and struct impl. First, the inheritance is changed, same as before, instead of class X, struct impl will inherit from class B. The overriding functions will be located in struct impl. Then, everything depends on what is in the overriding functions. If the scope of struct impl is enough to implement them (for example it only needs to play with the members of the pimpl idiom), all is fine, nothing to do, no extra cost. On the contrary, if it needs some access to class A or class X (eg. call a function in the (virtual) interface, or access to a member not in struct impl), there will come the necessary dependancy to class X. An extra member of impl will have to be added : a reference to an X (the X constructor will pass *this to the impl constructor). If needed, class X will be added a friend class impl; declaration so that class impl can access private things of class X it needs. One of the drawbacks (not often a problem) is that a reference to a B can no longer be dynamicly casted into a reference to an A or an X. I wish you all a happy new year =). Read More » | ||||
| | ||||
| ||||
Thursday, January 2, 2014
FeedaMail: Comments for Sutterâs Mill
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment