I am sure that the title of this blog posting is familiar to most of the readers of this blog, in that you have opened up a C source file and found a single #include statement that references a file that is typically called ‘includes.h’. On opening ‘includes.h’ one invariably finds an enormous list of other header files. Furthermore, as you go through other source files in the project, you will find that they all use ‘includes.h’. I suspect that by this point, the readers of this blog are divided into camps, namely:
- Either: So what, I do it all the time because it makes my life a lot easier.
- Or: I want to scream whenever I see this done.
I’m one of the screamers – and this is my rationale.
Back in the dark ages when one had to compile on computers with extremely limited resources, the compilation time of a module was a major issue. One of the things that significantly affected compile time was the number and the size of the header files that a module opened. As a result, most of us took steps to ensure that we only included the header files that were needed. However, as processor speeds increased and compilers started using pre-compiled header files, this became less of an issue, such that today I seriously doubt if you’d notice much difference in compilation times regardless of the number of header files that are included. I don’t know but I suspect that this was the enabler that caused people to start using ‘includes.h’.
So if compilation time is no longer an issue, what’s the big deal? After all we have all had the hassle of compiling a file only to be told that we are missing a prototype or a data type. At which point we have to hunt down the requisite header file, include it and recompile. If you do this half a dozen times in a new module, then it takes you say 15 minutes before everything is OK – and who has 15 minutes to waste on such irritating details? Well, in my opinion it’s time well spent. Here’s my case:
The number of header files a module needs to use is a crude but effective indicator of coupling. A module that needs to include almost no header files is clearly a module that is extremely self contained. That is it isn’t relying upon the outside world. Modules like this are typically easier to maintain and also more immune from changes made elsewhere in the system. In short I like modules that don’t have to include a lot of header files. Indeed when I have finished writing a module, I take a look at its include list. If the list is long then it really makes me wonder whether I should be breaking the module apart in some way so as to reduce the degree of coupling between it and the outside world.
Maintenance – understanding coupling
This is related to the first point. If I need to do some maintenance on a module, then a quick look at the include list can tell me how this module interacts with the rest of the code. This can be extremely useful if one is trying to understand how a program is put together.
Maintenance – understanding functionality
If I look at the include list and I see ‘math.h’, then I know that the module is using transcendental functions, which in turn implies complex floating point operations, which in turn implies potentially long execution times. In a similar manner, if it includes the header for the hardware interrupt handler, then I know I’m dealing with something related to the chip. I can get all this sort of information in a two second scan of the include list.
If you use an automated documentation tool such as Doxygen, then only including the header files that are needed by a module ensures that Doxygen generates a meaningful documentation set for you, rather than including hyperlinks to useless files.
Not getting what you want
I have left what is probably the biggest problem to last. By including an enormous number of header files you lay yourself wide open to problems like this:
#define FALSE 0
#define TRUE !FALSE
#define FALSE 0UL
#define TRUE 1UL
#define FALSE NULL
#define TRUE !FALSE
Trust me when I tell you I have seen this done! In other words the more files you include, the more likely it is that the macro that you are blithely using does not in fact have the value you think it does. Time to debug problems such as these – a lot longer than 15 minutes!
On the off chance that I have convinced an ‘includes.h’ fan of the error of their ways, it would be remiss of me to not tell you how to quickly find out just the header files needed by a module.
- Paste the include list of includes.h into the module.
- Delete the entry for includes.h
- Compile the code to make sure you haven’t broken anything.
- Lint the file. Lint will tell you all the header files that aren’t being used.
- Delete the unnecessary include statements.
- Repeat from step 3 until Lint is happy.
Of course the chances are that if you use ‘includes.h’ you aren’t using Lint. If you do start using Lint then it will do a lot more for you than just telling you about unnecessary includes.