C++ header only style

Started by
24 comments, last by tbrick 12 years, 4 months ago
I hate the way C++ uses separate interface and implementation files, so I decided to try a little experiment to see if it might be possible to just use headers.


Testing to see if using a header only approach to C++ has any benefits, by this I mean that each project would only have 1 .cpp file, everything else goes in headers.


All tests done with Visual Studio 2010

Code includes use of many templates, STL, boost, and TBB(lots of templates).


LTGC is on for release built, along with comdat folding

/MP is also enabled for all builds, although in header only world it doesn't do much(anything?)


Using CPP files: size: release = 375,296 bytes debug = 1,856,512 bytes
Using header only: size: release = 375,296 bytes debug = 1,856,512 bytes


So using 1 CPP, instead of many, made no difference to exe size



Full Build Times Release:

CPP Header:
1)33.74 1) 18.17
2)33.79 2) 18.02
3)33.48 3) 18.44



Full Build Times Debug:
CPP Header:
1) 24.65 1) 12.47
2) 25.16 2) 12.11
3) 24.80 3) 11.76



Full rebuilds are much faster under header only, and this is with /MP(multithreaded builds) enabled(8 cores) which favors having extra .cpp to split up the work


Partial Rebuild Release:
CPP
1)16.18 <-- changed 1 line in 1 cpp
2)25.81 <-- changed 1 line in random .h
3) 13.66 <--- changed 1 line in a different cpp


Can't do partial rebuilds with header only, but full rebuilds with header only are comparable to partial rebuilds with CPP's time wise. Partial rebuilds heavily depend on exactly what was changed


Partial Rebuild Debug
1) 19.06 <-- changed random header by 1 line
2) 3.32 <-- changed a line in 1 cpp
3) 16.62 <-- changed a line in another random .h



The code was all originally written using .cpp/.h combo as traditional C++. To switch to header only I just copied the contents of each .cpp under the contents of the .h and then removed the .cpp from the project.


After I had finished this I only had one compiler error which resulted from two objects which both made use of each other. One by value and one by reference.


I fixed this by splitting the class which used the other by reference into two headers, the .h as it had already existed, and contents of its .cpp moved into a .hpp and included in a header called "circular.hpp" which is then included in the one and only .cpp(main.cpp).


+ could probably also just rename all .cpp to .hpp and add them to circular.hpp or some such which is included in main.cpp, but this keeps those extra files around...


I had expected it to decrease the size of the .exe(or at least change it) since I've heard of other people doing similar things to create more optimized builds, and of course people crying about template bloat. That didn't happen though.


Built times are better in many cases, slower in others. I could see the header only approach not working so well with a giant project as it pretty much results in constant time builds which grow linearly with the # of files.


Of course changing a header in a giant C++ project can result in a nightmare rebuild...


Perhaps the best way would be header only, but split into multiple projects, so that not everything must always be remade?


Overall Header Only:
+faster worst case build times(changing a header)
+simpler build process
+can place code right in the class itself, like in most modern languages
+less files to deal with
+less worrying about forward declaring to avoid excess header includes and PIMPL pattern
+only one translation unit so warnings/errors in headers are seen once(less spam)
+can write functions in header without being forced to "inline" them (normally this results in multiple definitions error but with only one translation unit, no such problem)
+on older compilers that don't have link time code gen, should be more optimal?

-best case build times are slower(changing cpp)
-might not scale too well? not sure here, might be easy to work around this with multiple projects


I think I am going to start doing this with all of my code, and port my existing code over to this approach..

Does anyone have any experience with this, or know of any problems I missed with this approach?



C++ needs a module system:(
Advertisement

Does anyone have any experience with this, or know of any problems I missed with this approach?


Did you use precompiled headers? How many files are you compiling in this project here? I hypothesize you'll see at least a 60% reduction in compiling times using precompiled headers with the separate interface and implementation approach.
In general, putting all of your code into the header files is a really, really bad idea for build-times. You're telling the compiler that is has to recompile the implementation of every function for each time that it's interface needs to be known.

In effect, by copy&pasting all of your code into a single CPP file, you've just reinvented the "unity build" or "uber build", which breaks many language features, but in exchange, can decrease build times in certain situations.
You can read about "unity builds" here:
http://altdevblogada...f-unity-builds/

You'd probably also be interested in:
http://altdevblogada...-optimisation1/
http://altdevblogada...ization-part-2/
http://altdevblogada...isation-part-3/

N.B. there's other ways to achieve the "place code right in the class itself, like in most modern languages" point, such as in most approaches to the PIMPL idiom.
less worrying about forward declaring to avoid excess header includes and PIMPL pattern[/quote]I don't think that this is a very good point... I can rephrase it as: "you don't have to worry about code dependencies, because now everything is dependent on everything else!" ;P

I hate the way C++ uses separate interface and implementation files, so I decided to try a little experiment to see if it might be possible to just use headers.

I'm going to be blunt here...
It is not feasible in the real world. If you want something that works this way then use a different language like C#.
If you want to write real world programs with extensible, reusable, and maintainable code in C++ then you'll be using source files and headers, end of story.

Overall Header Only:
+faster worst case build times(changing a header)[/quote]Worst case shouldn't be hit much at all in a typical program. You don't optimise for the 1%

+simpler build process[/quote]Can't argue there although it comes at the cost of a loss of flexability.

+can place code right in the class itself, like in most modern languages[/quote]You can do that anyway whenever appropriate

+less files to deal with[/quote]True, though that is really not much of a consolation

+less worrying about forward declaring to avoid excess header includes and PIMPL pattern[/quote]Anything that encourages "excess header includes" cannot be a good thing.

+only one translation unit so warnings/errors in headers are seen once(less spam)[/quote]That may be true if the program consists of a single project, but a typical real-world solution might consist of a dozen or so projects for DLLs and so forth.

+can write functions in header without being forced to "inline" them (normally this results in multiple definitions error but with only one translation unit, no such problem)[/quote]You probably should not have been writing global functions inside header files to begin with.

+on older compilers that don't have link time code gen, should be more optimal?[/quote]Perhaps, if you don't exceed any limitations on the size of a single translation unit that such an older compiler might have.

-best case build times are slower(changing cpp)
-might not scale too well? not sure here, might be easy to work around this with multiple projects[/quote]You didn't think too hard there. If I could be bothered then I'm sure I could list at least half a dozen more, beyond the fact that average case build times will go through the roof, and what I've noted above.

Someone had to give you an overdose of reality, sorry it had to be me this time.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
fastcall22 user_popup.png

fastcall22:on precompiled headers: some of my projects are set up to use them, some aren't, this particular project was not. I have never noticed any significant boost from using them though(my built times really aren't that bad though).




Anyway I wasn't trying to improve built times by doing this, but more interested in if writing code in a more appealing style(to me..) without seriously harming build times.



In general, putting all of your code into the header files is a really, really bad idea for build-times. You're telling the compiler that is has to recompile the implementation of every function for each time that it's interface needs to be known.[/quote]

Yes but since there is only one TU, each header is only seen once, and it is only created once. It does have to create it and every time of course-- which is bad.. but by giving each project its own TU hopefully that would fix it.

In effect, by copy&pasting all of your code into a single CPP file, you've just reinvented the "unity build" or "uber build", which breaks many language features, but in exchange, can decrease build times in certain situations.
You can read about "unity builds" here:
http://altdevblogada...f-unity-builds/[/quote]

I've seen that before, but they still write cpp files as per normal. They just do it for optimal integration built times. It breaks some things such as using declarations at file scope and possibly any usage of global variables, but I have virtually none of this in my code..



You'd probably also be interested in:
http://altdevblogada...-optimisation1/
http://altdevblogada...ization-part-2/
http://altdevblogada...isation-part-3/
[/quote]

That was interesting, haven't seen that before, but again I'm not so much trying to improve built times, just verifying they didn't go horribly wrong when I write code all in headers(which is the way I prefer).
Overall Header Only:
+faster worst case build times(changing a header)[/quote]Worst case shouldn't be hit much at all in a typical program. You don't optimise for the 1%

[/quote]

In my compilation tests header only was generally faster or about the same, so I'd say it is abit more than 1%

+can place code right in the class itself, like in most modern languages[/quote]You can do that anyway whenever appropriate

[/quote]
Yes but it causes the everything to be inlined, and templates to be created in each TU(ie it gets slow fast), if you just do this without abandon it would be similar to what I am doing but having N translation units instead of one



+less worrying about forward declaring to avoid excess header includes and PIMPL pattern[/quote]Anything that encourages "excess header includes" cannot be a good thing.
[/quote]
-your looking at it from the perspective of having multiple TU, with only one, each header is only included once independent of however many times you #include it

+only one translation unit so warnings/errors in headers are seen once(less spam)[/quote]That may be true if the program consists of a single project, but a typical real-world solution might consist of a dozen or so projects for DLLs and so forth.
[/quote]
-my solution has 40 projects in it give or take, I'll work on converting more of them and see how it goes. So it would show up once per project instead of once per TU, still an improvement ;)

+can write functions in header without being forced to "inline" them (normally this results in multiple definitions error but with only one translation unit, no such problem)[/quote]You probably should not have been writing global functions inside header files to begin with.

[/quote]
I wasn't, you either misunderstood my point, or just felt the need to insult. It works fine as long as only one TU ever sees the file

+on older compilers that don't have link time code gen, should be more optimal?[/quote]Perhaps, if you don't exceed any limitations on the size of a single translation unit that such an older compiler might have.[/quote]
Yeah I guess you would have to split it up similar to how they did for the unity build

-best case build times are slower(changing cpp)
-might not scale too well? not sure here, might be easy to work around this with multiple projects[/quote]You didn't think too hard there. If I could be bothered then I'm sure I could list at least half a dozen more, beyond the fact that average case build times will go through the roof, and what I've noted above.[/quote]
It did not go through the roof so far, when I have more projects using it I'll see. If each project has its own TU, it should be quite comparable to normal compilation times, or so I hope. It was actually overall faster for release builds(even incrementally). The project is pretty representative of the average size of my projects, so if each is rewritten this way, with it's own single TU, I should get about the same time for compilation, and the freedom to write code in a more template/header centric way










Two things that haven't been mentioned:
1) Putting all the code in the header files is not idiomatic, so other C++ programmers that come into the project will no be used to working that way.
2) The traditional model of separate declaration and implementation allows for parallel compilation, which can speed up the process dramatically.
I don't actually aim for "headers only" but since I make heavy use of templates, most of my code ends up in them anyway...

If you want to write header only code just find an excuse to make everything a template :rolleyes:
One argument for header-only when making utility classes/functions is that it can be made very easy to use in other projects.

One argument for header-only when making utility classes/functions is that it can be made very easy to use in other projects.


How is it any easier to reuse headers than it is to reuse .cpp files?

This topic is closed to new replies.

Advertisement