What things should absolutely always/never be included in a header file?
- Functions: what must go in the header file and what mustn’t
- Constants: Is it good practice to define lot of constants in a header file?
- Any other good practices for C header files?
Example
I’m writing a parser for a documented industry standard format that has a lots of constants, is it a good practice to define them in a header file?
3
What to put in headers:
- The minimal set of
#include
directives that are needed to make the header compilable when the header is included in some source file. - Preprocessor symbol definitions of things that need to be shared and that can only accomplished via the preprocessor. Even in C, preprocessor symbols are best kept to a minimum.
- Forward declarations of structures that are needed to make the structure definitions, function prototypes, and global variable declarations in the body of the header compilable.
- Definitions of data structures and enumerations that are shared amongst multiple source files.
- Declarations for functions and variables whose definitions will be visible to the linker.
- Inline function definitions, but take care here.
What doesn’t belong in a header:
- Gratuitous
#include
directives. Those gratuitous includes cause recompilation of things that don’t need to be recompiled, and can at times make it so a system can’t compile. Don’t#include
a file in a header if the header itself doesn’t need that other header file. - Preprocessor symbols whose intent could be accomplished by some mechanism, any mechanism, other than the preprocessor.
- Lots and lots of structure definitions. Split those up into separate headers.
- Inline definitions of functions that require an additional
#include
, that are subject to change, or that are too big. Those inline functions should have little if any fan out, and if they do have fan out, it should be localized to stuff defined in the header.
What constitutes the minimal set of #include
statements?
This turns out to be a nontrivial question. A TL;DR definition: A header file must include the header files that directly define each of the types directly used in or that directly declare each of the functions used in the header file in question, but must not include anything else. A pointer or C++ reference type does not qualify as direct use; forward references are preferred.
There is a place for a gratuitous #include
directive, and this is in an automated test. For every header file in a software package, I automatically generate and then compile the following:
#include "path/to/random/header_under_test"
int main () { return 0; }
The compilation should be clean (i.e., free of any warnings or errors). Warnings or errors regarding incomplete types or unknown types mean that the header file under test has some missing #include
directives and/or missing forward declarations. Note well: Just because the test passes does not mean that the set of #include
directives is sufficient, let alone minimal.
Related
- Naming Include Guards
9
In addition to what has already been said.
H files should always contain:
- Source code documentation!!! At a minimum, what is the purpose of the various parameters and return values of the functions.
- Header guards, #ifndef MYHEADER_H #define MYHEADER_H … #endif
H files should never contain:
- Any form of data allocation.
- Function definitions. Inline functions may be a rare exception in some cases.
- Anything labelled
static
. - Typedefs, #defines or constants that have no relevance to the rest of the application.
(I would also say that there is never any reason to use non-constant global/extern variables, anywhere, but that’s a discussion for another post.)
8
Header file should have the following organization:
- type and constant definitions
- external object declarations
- external function declarations
Header files should never contain object definitions, only type definitions and object declarations.
5
I would probably never say never, but statements that generate data and code as they are parsed should not be in a .h file.
Macros, inline functions and templates may look like data or code, but they do not generate code as they are parsed, but instead when they are used. These items often need to be used in more than one .c or .cpp, so they belong in the .h.
In my view, a header file should have the minimum practical interface to a corresponding .c or .cpp. The interface can include #defines, class, typedef, struct definitions, function prototypes, and less preferred, extern definitions for global variables. However, if a declaration is used in only one source file, it probably should be excluded from the .h and be contained in the source file instead.
Some may disagree, but my personal criteria for .h files is that they #include all other .h files that they need to be able to compile. In some cases, this can be a lot of files, so we have some effective methods to reduce external dependencies like forward declarations to classes that let us use pointers to objects of a class without including what could be a big tree of include files.
Statements that generate data and code as they are parsed, should not be in a .h
file. As far as my point of view is concerned, a header file should only have the minimum practical interface to a corresponding .c
or .cpp
.