How to Detect Unnecessary #Include Files in a Large C++ Project

How should I detect unnecessary #include files in a large C++ project?

While it won't reveal unneeded include files, Visual studio has a setting /showIncludes (right click on a .cpp file, Properties->C/C++->Advanced) that will output a tree of all included files at compile time. This can help in identifying files that shouldn't need to be included.

You can also take a look at the pimpl idiom to let you get away with fewer header file dependencies to make it easier to see the cruft that you can remove.

Detecting superfluous #includes in C/C++?

It's not automatic, but doxygen will produce dependency diagrams for #included files. You will have to go through them visually, but they can be very useful for getting a picture of what is using what.

Tools to find included headers which are unused?

DISCLAIMER: My day job is working for a company that develops static analysis tools.

I would be surprised if most (if not all) static analysis tools did not have some form of header usage check. You could use this wikipedia page to get a list of available tools and then email the companies to ask them.

Some points you might consider when you're evaluating a tool:

For function overloads, you want all headers containing overloads to be visible, not just the header that contains the function that was selected by overload resolution:

// f1.h
void foo (char);

// f2.h
void foo (int);


// bar.cc
#include "f1.h"
#include "f2.h"

int main ()
{
foo (0); // Calls 'foo(int)' but all functions were in overload set
}

If you take the brute force approach, first remove all headers and then re-add them until it compiles, if 'f1.h' is added first then the code will compile but the semantics of the program have been changed.

A similar rule applies when you have partial and specializations. It doesn't matter if the specialization is selected or not, you need to make sure that all specializations are visible:

// f1.h
template <typename T>
void foo (T);

// f2.h
template <>
void foo (int);

// bar.cc
#include "f1.h"
#include "f2.h"


int main ()
{
foo (0); // Calls specialization 'foo<int>(int)'
}

As for the overload example, the brute force approach may result in a program which still compiles but has different behaviour.

Another related type of analysis that you can look out for is checking if types can be forward declared. Consider the following:

// A.h
class A { };

// foo.h
#include "A.h"
void foo (A const &);

// bar.cc
#include "foo.h"

void bar (A const & a)
{
foo (a);
}

In the above example, the definition of 'A' is not required, and so the header file 'foo.h' can be changed so that it has a forward declaration only for 'A':

// foo.h
class A;
void foo (A const &);

This kind of check also reduces header dependencies.

How should I detect unnecessary #include files in a large C++ project?

While it won't reveal unneeded include files, Visual studio has a setting /showIncludes (right click on a .cpp file, Properties->C/C++->Advanced) that will output a tree of all included files at compile time. This can help in identifying files that shouldn't need to be included.

You can also take a look at the pimpl idiom to let you get away with fewer header file dependencies to make it easier to see the cruft that you can remove.

Clean up your #include statements?

To verify that header files are including everything they need, I would creating a source file that all it does is include a header file and try to compile it. If the compile fails, then the header file itself is missing an include.

You get the same effect by making the following rule: that the first header file which foo.c or foo.cpp must include should be the correspondingly-named foo.h.
Doing this ensures that foo.h includes whatever it needs to compile.

Furthermore, Lakos' book Large-Scale C++ Software Design (for example) lists many, many techniques for moving implementation details out of a header and into the corresponding CPP file. If you take that to its extreme, using techniques like Cheshire Cat (which hides all implementation details) and Factory (which hides the existence of subclasses) then many headers would be able to stand alone without including other headers, and instead make do with just forward declaration to opaque types instead ... except perhaps for template classes.

In the end, each header file might need to include:

  • No header files for types which are data members (instead, data members are defined/hidden in the CPP file using the "cheshire cat" a.k.a. "pimpl" technique)

  • No header files for types which are parameters to or return types from methods (instead, these are predefined types like int; or, if they're user-defined types, then they're references in which case a forward-declared, opaque type declaration like merely class Foo; instead of #include "foo.h" in the header file is sufficient).

What you need then is the header file for:

  • The superclass, if this is a subclass

  • Possibly any templated types which are used as method parameters and/or return types: apparently you're supposed to be able to forward-declare template classes too, but some compiler implementations may have a problem with that (though you could also encapsulate any templates e.g. List<X> as implementation details of a user-defined type e.g. ListX).

In practice, I might make a "standard.h" which includes all the system files (e.g. STL headers, O/S-specific types and/or any #defines, etc) that are used by any/all header files in the project, and include that as the first header in every application header file (and tell the compiler to treat this "standard.h" as the 'precompiled header file').


//contents of foo.h
#ifndef INC_FOO_H //or #pragma once
#define INC_FOO_H

#include "standard.h"
class Foo
{
public: //methods
... Foo-specific methods here ...
private: //data
struct Impl;
Impl* m_impl;
};
#endif//INC_FOO_H

//contents of foo.cpp
#include "foo.h"
#include "bar.h"
Foo::Foo()
{
m_impl = new Impl();
}
struct Foo::Impl
{
Bar m_bar;
... etc ...
};
... etc ...


Related Topics



Leave a reply



Submit