4 ms·
I seriously doubt the compiler deduplicating header files that specify "pragma once" is what is slow about parsing all those header files. I really think the ma
by grogers 4y ago
I seriously doubt the compiler deduplicating header files that specify "pragma once" is what is slow about parsing all those header files. I really think the main issue is that all the different translation units have to reparse the web of headers, which this doesn't solve since you still need to manually figure out the correct order and include them all in each source file. Seems like a lot of headache for no gain.
Holding things by pointer on the other hand actually does let you prevent the includes since you can just forward declare in the header, but it's an indirection :-/
- flohofwoe 4y agoIME the main problem of allowing to include headers in headers is that this quickly grows out of control, and after a while headers will include stuff that's not actually needed (usually, no longer needed). Then you get quickly into a situation where a header includes another header that's no longer needed by itself, but is used by another header in the same 'include tree', which in turn doesn't include the required header itself. Gets very messy very quickly. It's much easier to notice and fix such situations in the top-level implementation file, and another advantage is that the "dependency complexity" of an implementation file is visible at a glance by looking at the include list at the top of the file. TL;DR: It's not primarily for compilation speed, but for 'header hygiene' (but of course this will also eventually help with compilation speed as the project grows). Another well known (at least among game devs) proponent of the the same idea is Our Machinery (granted, the whole idea makes a lot more sense in C than in C++, because in C declaration and implementation is usually much stricter separated into header and source files than in C++) https://ourmachinery.com/post/physical-design/ https://ourmachinery.com/post/physical-design/
- account42 4y agoThe tool for deal with this is https://include-what-you-use.org/ https://include-what-you-use.org/ The idea is that each file (source or header) should include exactly those headers from which it uses things. In practice, it gets a bit more complicated has you don't want to include internal implementation headers and sometimes the same thing does not even have a canonical public header but IWUY does allow you to configure all that to your liking.
- marco_craveiro 4y agoGreat point. And this will soon be available directly from Clang tools such as clangd, which will make life much easier.
- trinovantes 4y agoHopefully explicit exports with modules in c++20 will solve the include tree problem
- DavidVoid 4y ago> Holding things by pointer on the other hand actually does let you prevent the includes since you can just forward declare in the header, but it's an indirection :-/ And it means you now need to deal with raw pointers, which often isn't optimal or preferred (over say references for example). At least we'll get modules soon. That should reduce the need for some of these workarounds that are really mainly used to reduce compile times, and not to improve the code in any other meaningful way.
- lfowles 4y agoSure, but you can also forward declare references!
- DavidVoid 4y agoFor some reason I thought they weren't but that's good to know.
- jll29 4y agoThere are issues with type safety when using raw pointers (e.g. void ) over alternatives. Reminds me of the good ol' days when Modula-2 had .def (definition) and *.mod2 (implementation) files, and you could compile the definition files alone to get binaries of the interface files that you could write and compile against in a typesafe way before the interfaces were even implemented And as far as I recall the decoupling also made things compile fast (e.g. with Applications Systems Heidelberg's Modula-2 on the Atari ST 520+).
- dzdt 4y agoThe gain is mentioned in the article: without such discipline sometimes when you #include button.h (what you expect to be innocuous header file for a desired button functionality) you accidently inlcude nuclearpowerplant.h (a giant monstorisity of recursively included functionality none of which you actually need). It may be possible to avoid that worst-case situation without such strict discipline, but with the no-recursive-include rule for sure it won't happen.
- TrianguloY 4y agoIf including button.h ends including nuclearpowerplant.h it's because it's a dependency, so with the article method you will end up including nuclearpowerplant too, otherwise it won't compile. Of course this assumes that headers only include what they really need. If a header includes something it shouldn't (maybe an old dependency that was forgotten) the issue is old dependencies, and the fix is to remove it from the header once. With the article method you will need to remove it from every file that includes button (except maybe it is also used by another dependency and compile will fail). Edit: I've realized the problem. If A includes B because it requires it, and C requires A and B, C will probably only include A and it will compile. Later if A is refactored and no longer requires B, removing it will break C. The solution is to always include what you use, even if it it's already included as dependency, as explained by another comment.
- account42 4y agoI think TFAs approach actually makes things worse over time as now you need even more discipline to clean out no longer needed #inlcues from all source files using a header when that header removes a dependency. IME the best tools to solve the duplicated parsing (and compilation for templates) is to use IWYU [0] to cut down on unneeded includes and unity/jumbo builds to combine multiple translation units so common headers only need to be parsed once. [0] https://include-what-you-use.org/ https://include-what-you-use.org/
- slavik81 4y agoThere actually is a bug filed against GCC [1] because it searches a linked list for headers using #pragma once and searches a hash table for headers using include guards. The performance difference is noticable for translation units that include a lot of headers. It would be a relatively simple thing to fix, if anyone is looking for a fun little project to learn a bit about GCC. [1]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58770 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58770