2

Is there any reason not to write one header file that #includes all other header files and just #include that one header in each c file?

Each header file is setup #ifndef ... #define ... #endif

To clarify the duplicates; I was asking about user written code not system headers and the other possible duplicate didn't have answers that specified why it wasn't a good idea.

Thanks for the answers, I hadn't thought through some of them. I will look a taking the hybrid approach of some smaller headers for modules that fit together. This is for a codebase that I have taken over, it's very much a web I'm slowly trying to untangle.

3
  • possible duplicate of Is it right to simply include all header files? Commented May 24, 2015 at 21:33
  • I do not think it's a duplicate @Jongware, they do not ask the same thing! Commented May 24, 2015 at 21:36
  • This might be a better candidate for duplicate. Commented May 24, 2015 at 21:39

4 Answers 4

3

For a non-trivial project, a combination of both policies is often used: One header which includes commonly used system headers like stdbool.h, stdint.h, stdatomic.h, etc. As these never change, they are note relevant for rebuild.

You also might include some project-settings which are required by most/all modules, so you don't have to care about them when writing a new module (or change every module when refactoring them).

And then you have specific headers which represent your module-dependency tree. These are included as required. This not only speeds up building (not compiling) the project, but also can be used by doc-tools like doxygen to automatically create the dependency tree for the documentation, so you don't have to track every change manually. A similar approach may be used by an IDE for quick access to dependant headers/modules.

One disadvantage which expecially appies to C is called namespace pollution. As C does not allow for custom namespaces, every included header will have its symbols added to the namespace of the current compilation unit. For Macros, this might be even worse, as they are plain text-replacements with very limited knowledge of the context (for this, also check inline vs. function-macro).

Sign up to request clarification or add additional context in comments.

5 Comments

The 'name space' pollution and the extended build times are the two main reasons to NOT group all the headers into a single header file. The whole idea behind makefiles, etc is to reduce the build time. I'm from back in the days when a build could take hours and hours and hours. In those days (when programs were much much smaller) we would do all we could to minimize the amount of file compiles. Now, programs are much much larger and compiling a file typically happens almost instantly, but when there are hundreds/thousands of files, we still need to do what we can to minimize
@user3629249: Did you actually read my answer? I just detailed than. You do certainly not think the system headers will change that often, don't you? For some projects the build time is mostly irrelevant on decent host hardware, so it would also be no issue to combine some only occassionally changing headers; expecially if these are anyway included in most modules. Then it actually make no difference. I think I stated clear when and when not to use which approach. I'm in that business long enough to know how it was back then, too. And I certainly do nor argue for bundling all headers.
What answer? I just read the whole thing again and all I see from you is two comments, no answer.
@user3629249: Then I cannot help you - sorry. Apparently others think different.
Sounds like a reasonable compromise, especially because namespace-pollution on commonly-used headers is not that bad: some names commonly used in a project should not be defined as anything else anywhere for readability purposes, even if it were possible to compile.
3

The reason we split up header files and implementation files into separate files in the first place (aside from code readability) is to enable incremental building by the compiler. That is, if we make a change in HeadearA.h that only ImplA.c and ImplB.c need, there is no need to recompile ImplC.c through ImplZ.c. If you do it your way, however, there is no way for the compiler to know that, and you will wind up recompiling all of the .c files for every rebuild, regardless of necessity. For small projects with small build times, this is okay. For larger projects that have build times nearing 30 minutes, this becomes prohibitive.

5 Comments

30 minutes of build time... sounds like a c++ project.
It is; and to be fair, the longest time I've seen for a C project is around ~5 minutes, but the principle is the same, I feel.
Such large C projects are fairly rare. I can think of glibc, gcc, X-Windows, the Linux Kernel... most C projects rebuild from scratch in seconds on modern machines with enough RAM.
@chqrlie: Hm... somehow, most of the (C) projects I work on usually build in about ~2-3 minutes as I typically work on larger projects (which is still annoying to wait). I guess for most projects it shouldn't matter as much.
@chqrlie: depends. My current project includes quite some autogenerated code. But - acknowledged, it still compiler in under one minute on my old core 2 duo notebook from hdd. Hmm.. now I'm curious how long it takes on my desktop.
3

What you suggest is possible: many projects use this convention.

Some advantages:

  • Simplicity of file heading
  • Simpler dependencies in Makefile, no need for complex autogenerated dependency lists
  • Early detection of name clashes.
  • No recursive include conundrums
  • No subtle bugs due to different include orders
  • Faster compilation if you use precompiled headers.

Some drawbacks:

  • Any modification in a header file will cause all source files in the project to be recompiled.
  • Encourages lack of proper encapsulation
  • Slower compile time for individual files (fix this with precompiled headers if it becomes a performance issue)

Comments

2

The main problem is that you end up with a big bowl of spaghetti code and everything knows about everything ..eventually .anything you change has repercussions, and you end up having to refactor to re-modularize... You don't have the tiny little reminder of actually including some header to make the decision of x should know about y.

But there isn't anything intrinsically wrong with it aside from the normal issues of symbol visibility and macro expansion, which should be edge cases..

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.