Organize Includes Comes to CDT

No C or C++ programmer likes to see compilation errors due to missing #include statements. Determining what #include statements are required to compile a given program is a tedious task. It becomes even worse when pieces of code are moved from one source file to another. Figuring out which #include statements should be added and which can be removed can easily become a lengthy process of trial and error unless assisted by a software tool.

Such a tool is finally available in Eclipse CDT. The Organize Includes command adds missing #include statements, removes unnecessary ones, and even adds forward declarations for symbols that can be forward declared. A programmer can just hit Shift+Ctrl+O and the #include statements are taken care of. Sounds simple, isn’t it?

To deliver this simplicity to the user, a lot of work is happening under the hood. Organizing #include statements in C or C++ is a pretty complex task that only on the surface looks similar to organizing imports in Java. Several aspects of C and C++ languages and a wide variation of C/C++ development practices make the problem of organizing #include statements hard.

We will now look at different aspects of the problem and learn how CDT approaches them.

Include or Forward Declare

C++ has quite complex rules for when a forward declaration is sufficient and when the definition of a symbol has to be visible at the point of use. Here is a simple example:

					
#include "A.h"
A foo(A a) {     // definition of A is required
  return a;
}

class A;
A* bar(A* a) {   // definition of A is not required
  return a;
}

#include "A.h"
void baz(A* a) {
  a->f();        // definition of A is required
}

#include "D.h"
class E;
class C {
  D x;           // definition of D is required
  static E y;    // definition of E is not required
};

There are also more complex rules. Template type arguments, for example, can be forward declared for some templates but not for others:

#include "A.h"
class B;
std::vector<A> a;      // definition of A is required
std::shared_ptr<B> b;  // definition of B is not required

It is not possible to tell whether forward declaration of a template argument is sufficient or not without examining details of the template definition.

In some cases forward declarations are allowed by the language but should not be used to avoid creating code that is hard to maintain. In addition to following the language rules, Organize Includes allows users to selectively prevent the use of some kinds of forward declarations:

organize includes

Indirect Inclusion

Header files in C and C++ can be included in a source file directly or through other included header files. Sometimes relying on indirect inclusion is ok, but sometimes it may lead to code fragility when seemingly innocent changes not affecting the public API can break compilation of other source files. Consider the following example:

GC.h

class GC { ... };

Graphics.h

#include "GC.h"
inline void drawLine(int x1, int y1, int x2, int y2) {
  GC.getCurrent().drawLine(x1, y1, x2, y2);
}

main.cpp

#include "Graphics.h"
void main() {
  drawLine(0, 0, 1, 1);
  GC gc;
  ...
}

If Graphics.h is refactored by moving the body of the drawLine method and the #include "GC.h" statement to Graphics.cpp, main.cpp will no longer compile.

The Include What You Use principle is intended to prevent this kind of code fragility. According to this principle main.cpp must include GC.h directly because it uses the GC type defined in that file.

Sometimes it is OK to rely on indirect inclusion.

MyString.h

					
class MyString {
public:
  MyString(const char* s);
};

Compare.h

#include "MyString.h"
int compare(const MyString& s1, const MyString& s2);

main.cpp

#include "Compare.h"
int main(int argc, const char* argv[]) {
  return compare(argv[1], argv[2]);
}

In this example main.cpp can rely on Compare.h providing the definition of MyString class because, due to the existence of the conversion constructor MyString(const char* s), the compare(const MyString& s1, const MyString& s2) method may be called without explicitly mentioning MyString. On the other hand, Compare.h may not be written as:

					
class MyString;
int compare(const MyString& s1, const MyString& s2);					

If it were, main.cpp would have to include MyString.h although it does not mention the MyString class anywhere.

In some cases indirect inclusion is mandatory since some header files are simply not intended to be included directly. For example, to use std::vector someone has to include <vector>, not <bits/stl_vector.h>, which contains the definition of std::vector.

In some cases any of a set of headers can be included for a particular symbol. In such situations Organize Includes tries to include headers that minimize the number of #include statements.

The rules for using some header files instead of some other header files are defined through preferences:

header substitution

header substitution map

Another factor that contributes to rules for indirect inclusion is include pragmas in header files.

facade.h

#include "detail/Constants.h" // IWYU pragma: export
#include "detail/Types.h" // IWYU pragma: export
#include <vector>

main.cpp

					
#include "Facade.h"
#include <vector>
std::vector<Thing> things(MAX_THINGS);

Facade.h exports detail/Types.h and detail/Constants.h and may be included instead of these header files that define the Thing class and the MAX_THINGS constant.

Include pragmas are configurable in preferences. The default is compatible with the Include-What-You-Use project.

include pragmas

Ordering of #include Statements

There are many different styles for ordering of #include statements. Most of these styles can be represented as some grouping of #include statements with some ordering between groups and alphabetic ordering within each group.

include style

A partner header is the header file most closely related to the source file. For example, if MyClass.h contains the definition of MyClass, MyClass.cpp contains the implementation of the class, and MyClassTest.cpp contains a unit test for it, MyClass.h is the partner header for both, MyClass.cpp and MyClassTest.cpp.

include style2

All header files that don’t fall into any category with the Keep include statements together option enabled, are grouped under Other Headers.

The default style mostly follows the Google C++ Style Guide that is designed to reduce hidden dependencies.

Parting Words

If you are curious how Organize Includes is implemented or would like to extend its functionality, the code is located in the CDT GIT repository and can be viewed here.

If you don’t like tweaking your tools as long as they work, simply start using Shift+Ctrl+O in C/C++ editor and stop worrying about those messy #include statements.

About the Author
Sergey Prigogin

Sergey Prigogin
Google