92

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?

Drizzle
  • 5
  • 2
Moshe Magnes
  • 1,071
  • 1
  • 8
  • 6
  • 1
    Short and painless: definitions and declartions that are needed in more than one module. – ott-- Oct 06 '12 at 20:51
  • 36
    Marking this question as "too broad" and closing is an absolute shamefull overkill of moderation. This question _exactly_ asks what i am looking for - the Question is well formed and does ask a very clear question: what are the best practices? If this is "too broad" for softwareengineering .. we could aswell just close down this whole forum. – Gewure Feb 21 '17 at 10:35
  • TL; DR. For C++, in the fourth edition of "The C++ Programming Language" written by Bjarne Stroustrup (its creator), in Section 15.2.2 it is described what a header should and should not contain. I know you tagged the question to C, but some of the advices are also applicable. I think this is a good question... – horro Mar 14 '18 at 10:07

5 Answers5

76

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

David Hammen
  • 8,194
  • 28
  • 37
  • So, if I have a library that defines a struct, called A, and this library, called B, uses that struct, and library B is used by program C, should I include library A's header file in library B's main header, or should I just forward declare it? library A is compiled and linked with library B during it's compilation. – MarcusJ Jul 11 '18 at 14:34
  • @MarcusJ - The very first thing I listed under *What doesn't belong in a header* was gratuitous #include statements. If header file B does not depend on the definitions in header file A, don't #include header file A in header file B. A header file is not the place to specify third party dependencies or build instructions. Those go somewhere else such as a top-level readme file. – David Hammen Jul 12 '18 at 03:24
  • 1
    @MarcusJ - I updated my answer in an attempt to answer your question. Note that there isn't one answer to your question. I'll illustrate with a couple of extremes. Case 1: The only place where library B directly uses the functionality of library A is in the library B source files. Case 2: Library B is a thin extension of the functionality in library A, with the header file(s) for library B directly using types and/or functions defined in library A. In case 1, there's no reason to expose library A in the header(s) for library B. In case 2, this exposure is pretty much mandatory. – David Hammen Jul 13 '18 at 20:55
  • Yeah it's case 2, sorry my comment skipped over the fact that it's using types declared in library A in library B's headers, I was thinking I might be able to forward declare it, but I don't think that's gonna work. Thanks for the update. – MarcusJ Jul 14 '18 at 22:15
  • Is adding constants to a header file a big no-no? – mding5692 Nov 17 '18 at 19:57
  • @mding5692 - It depends on context. Enumeration values are constants; there's absolutely nothing wrong with having enums in a header. Constants defined with the as a macro (e.g., `#define PI 3.1415926535897932`): There's nothing wrong with that, either. Other constants: It depends. Constants declared and defined at global or namespace scope as `constexpr` or `const` , that's ok. Every file that uses such constants will have a copy, but in a non-linkage sense. ... – David Hammen Nov 18 '18 at 00:18
  • Constant static data members defined in a header are a different matter. Prior to c++11, only integer `static const` data members could be defined in a header. (Non-integer constant static data members could be declared but not defined.) C++11 added `constexpr`, and that concept has grown since c++11. – David Hammen Nov 18 '18 at 00:20
  • Nice answer, maybe you have a good response to https://stackoverflow.com/questions/64295652/is-it-a-good-or-bad-practice-to-include-a-header-in-a-file-if-that-header-is-alr ? – mercury0114 Oct 10 '20 at 16:34
  • @mercury0114 That question is now closed as opinion-based. – David Hammen Oct 10 '20 at 18:51
20

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.)

  • 2
    I agree with everything, except what you have highlighted. If you are making a library, yes, you should document or the users of your library. For an in-house project you shouldn't need to clutter your headers with documentation, if you use good, self explanatory variable and function names. – martiert Oct 08 '12 at 14:02
  • 5
    @martiert I'm also of the "let the code speak for itself" school. Yet you should at the very least always document your functions, even if nobody but yourself will use them. Things of particular interest are: in case the function has error handling, what error codes does it return and under which conditions does it fail? What happens to parameters (buffers, pointers etc) if the function fails? Another thing that is very relevant is: are the pointer parameters returning something to the caller, ie do they expect allocated memory? -> –  Oct 08 '12 at 14:09
  • 1
    It should be obvious to the caller what error handling is done inside the function and what is not done. If the function expects an allocated buffer, it will most likely leave the out-of-bounds checks to the caller as well. If the function relies on another function to be executed, this must be documented (ie run link_list_init() before link_list_add()). And finally, if the function has a "side-effect" such as creating files, threads, timers or whatever, it should be stated in the documentation. -> –  Oct 08 '12 at 14:12
  • If the user of your code (possibly yourself, x years ahead in time) needs to read through the implementation of each function before using it, they will be wasting hours of time and might still understand things wrong. Nobody will ever understand the function as well as the programmer who wrote it, at the very time when they wrote it. So writing down some simple 10 lines of documentation when everything is fresh in your mind won't take long, but it will save the next person reading the code a whole lot of trouble. –  Oct 08 '12 at 14:14
  • 1
    Maybe "source code documentation" is too broad here, this really belongs to the source code. "Usage documentation" with input and output, pre- and postconditions and side-effects should definitely go there, not in epic but in a *brief* form. – Secure Oct 08 '12 at 14:19
  • 2
    A bit belated, but +1 for documentation. Why does this class exist? The code does *not* speak for itself. What does this function do? RTFC (read the fine .cpp file) is an obscene four letter acronym. One should never have to RTFC for understanding. The prototype in the header should summarize, in some extractable comment (e.g., doxygen), what the arguments are and what the function does. Why does this data member exist, what does it contain, and is the value in meters, feet, or furlongs? That too is another subject for (extractable) comments exist. – David Hammen Oct 14 '13 at 12:55
  • This except for the docs. There might be rare exceptions but usually documentation is never up to date with the code. It's tends to be forgotten by quick fixes, bug fixes and refactorings and the compiler couldn't care less. I always prefer looking at the code instead of assuming that the documentation is correct. – robsn May 04 '21 at 13:38
  • @David Hammen There are no classes or .cpp files in C. – robsn May 04 '21 at 13:42
4

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.

theD
  • 2,883
  • 1
  • 16
  • 16
  • What about inline function definitions? – Kos Oct 06 '12 at 10:00
  • If the inline function is a “helper” function that’s only used inside one C module, then put it in that .c file only. If, the inline function must be visible to two or more modules, the put it inside the header file. – theD Oct 06 '12 at 10:16
  • Also, if the function has to be visible across a library boundary, don't make it inline as that forces everyone who uses the library to recompile every time you modify things. – Donal Fellows Oct 06 '12 at 12:11
  • @DonalFellows: That's a backhanded solution. A better rule: Don't put stuff in headers that is subject to frequent modification. There's nothing wrong with inlining a short little function in a header if the function has no fanout and has a clear definition that will only change if the underlying data structure changes. If the function definition changes because the underlying structure definition changed, yeah, you have to recompile everything, but your going to have to do that anyway because the structure definition changed. – David Hammen Oct 06 '12 at 14:11
  • so is constant definition not an object definition? Like `const int x = 5;` Or are you talking about macros here? – mercury0114 Oct 10 '20 at 19:55
4

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.

DeveloperDon
  • 4,958
  • 1
  • 26
  • 53
0

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.

Martijn Pieters
  • 14,499
  • 10
  • 57
  • 58