What should and what shouldn't be in a header file?

  • What things should absolutely never be included in a header file?

    If for example I'm working with a documented industry standard format that has a lot of constants, is it a good practice to define them in a header file (if I'm writing a parser for that format)?

    What functions should go into the header file?
    What functions shouldn't?

    Short and painless: definitions and declartions that are needed in more than one module.

    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.

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

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

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

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

    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.

    Is adding constants to a header file a big no-no?

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

    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.

License under CC-BY-SA with attribution


Content dated before 6/26/2020 9:53 AM