Self-Sufficient Header Files in C/C++

Self-sufficient header files in C/C++

A self sufficient header file is one that doesn't depend on the context of where it is included to work correctly. If you make sure you #include or define/declare everything before you use it, you have yourself a self sufficient header.

An example of a non self sufficient header might be something like this:

----- MyClass.h -----

class MyClass
{
MyClass(std::string s);
};

-

---- MyClass.cpp -----

#include <string>
#include "MyClass.h"

MyClass::MyClass(std::string s)
{}

In this example, MyClass.h uses std::string without first #including .
For this to work, in MyClass.cpp you need to put the #include <string> before #include "MyClass.h".

If MyClass's user fails to do this he will get an error that std::string is not included.

Maintaining your headers to be self sufficient can be often neglected. For instance, you have a huge MyClass header and you add to it another small method which uses std::string. In all of the places this class is currently used, is already #included before MyClass.h. then someday you #include MyClass.h as the first header and suddenly you have all these new error in a file you didn't even touch (MyClass.h)

Carefully maintaining your headers to be self sufficient help to avoid this problem.

Having both self-sufficient headers and precompiled headers

Using the precompiled header does not speed up the processing of redundant includes. If the redundant include files contain header guards (or #pragma once) they will be processed much faster.

Why include related header first?

It's to make sure your clients don't hate you when they include your library header.

If the header is brittle and subject to break on wrong inclusion order, it may appear to work in your environment when it isn't first - since you include the headers you need - but fail to compile for client code. Because it may not at all be obvious what other headers need to be pulled in for it to work.

Including first the header which corresponds to the current implementation file goes toward checking that the header is self-contained. Self-containment goes beyond just including the necessary headers. It also entails adding the required forward declarations for types you use in your API. Naturally your header will compile if you include the header for the type before it, but you may not wish to pull it in since you only depend on the type name in your API.

Some style guides prohibit forward declarations, so those may not be part of the rationale they pose.

Forward declarations or self-sufficient headers?

Sutter and Alexandrescu on item #22 say "Don't be over-dependent: Don't #include a definition when a forward declaration will do".

Personally, I agree with this statement. If in my class A, I don't use any functionality of a class B, nor I instantiate an object of class B, then my code doesn't need to know how class B is made. I just need to know that it exists.

Forward declaration is also useful to break cyclic dependencies...

Edit: I also like a lot the observation that Mark B pointed out: it happens sometimes that you don't include the file a.hpp because it's already included in the file b.hpp you are including, which chose to include a.hpp even though forward declaration was enough. If you stop using the functions defined in b.hpp, your code won't compile anymore. If the programmer of b.hpp had used forward declaration, that wouldn't have happened, since you would have included a.hpp somewhere else in your code...

How can I let CMake check whether my headers are self-sufficient?

For C++ headers rather than C headers, you need to use CheckIncludeFileCXX. This module also differs from CheckIncludeFiles in that you can only pass a single file at a time to this macro.

It could well also be that you need to set CMAKE_REQUIRED_INCLUDES, or one of the other variables listed in the docs.

For example, if some of your headers refer to eachother (as in #include "h1.hpp"), then you'll need:

set(CMAKE_REQUIRED_FLAGS ${CMAKE_CXX_FLAGS})
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_SOURCE_DIR}/include)
include(CheckIncludeFileCXX)
foreach(project_header ${project_headers})
get_filename_component(header_name ${project_header} NAME_WE)
check_include_file_cxx("${project_header}" ${header_name}_IS_STANDALONE)
endforeach()

There's a little more info in the CMake wiki article How To Write Platform Checks.

Is it common practive to #include header files already included by included header files?

If B.h somehow changes and no longer depends on C.h, A.h will break.

Exactly. Why take the chance?

On the other hand if I think this through to the end it seems unneccessary/impractical to reinclude every single dependency.

If it's impractical, your file has too many dependencies and is probably too large.

Refactor it into smaller modules.

A common case I have are standard libraries. In almost all my header files I would have to include <stdint.h> and <stdbool.h>. I often skip this because they were already included in one of the dependencies but this always feels kind of arbitrary.

I admit to sometimes skipping these when I know one of my headers — expressly defined to bring the types I need into scope — has done this. It's unlikely to be refactored because it has these headers for this reason, not for some other dependency that may disappear.

But, ultimately, there's nothing wrong with including <stdint.h> and <stdbool.h> where you need it. I'm surprised that you find you need them in "almost all [your] header files", to be honest.

C/C++ include header file order

I don't think there's a recommended order, as long as it compiles! What's annoying is when some headers require other headers to be included first... That's a problem with the headers themselves, not with the order of includes.

My personal preference is to go from local to global, each subsection in alphabetical order, i.e.:

  1. h file corresponding to this cpp file (if applicable)
  2. headers from the same component,
  3. headers from other components,
  4. system headers.

My rationale for 1. is that it should prove that each header (for which there is a cpp) can be #included without prerequisites (terminus technicus: header is "self-contained"). And the rest just seems to flow logically from there.



Related Topics



Leave a reply



Submit