IncrediBlog

How to best utilize ‘include what you use’ and avoid common issues

About Us

IncrediBuild, the market leader in software development acceleration, dramatically reduces build, testing, code analysis and other development times. With its unique distributed processing acceleration technology, IncrediBuild is the only commercial tool bundled into Visual Studio. IncrediBuild supports more than 200,000 users from over 2,500 companies.
Learn more

In a nutshell, Include What You Use (IWYU), introduced by Google, ensures source files include all headers used in the C++ code. This methodology essentially maximizes the probability that code continues to compile even with reasonable changes made to the various interfaces. 

Previously I wrote to you about the “include what you use” tool.  It will automatically suggest forward declarations to speed up compile times, as well as missing includes to detect (accidental) reliance on indirect includes, which may not be portable.

However, if you run it you might notice a couple of false positives.

Problem: Guaranteed Indirect Includes

The first thing you notice is that following the “include what you use” philosophy can get a little bit annoying at times.

It is a common practice to have an “convenience header” that includes many other headers.

A lot of the Boost libraries follow this practice. For example, here we include boost/mp11.hpp to get access to all meta programming facilities provided by Boost.Mp11:


#include <boost/mp11.hpp>

using my_types = boost::mp11::mp_list<int, char, float>;

But IWYU doesn’t know about that and instead suggests we include boost/mp11/detail/mp_list.hpp to get mp_list which is obviously wrong.

If you are writing the library, you can fix it with a special comment // IWYU pragma: export:

// somewhere in a Boost.Mp11 header file
#include <boost/mp11/detail/mp_list.hpp> // IWYU pragma: export

This will tell IWYU that the contents of this include are guaranteed to be available when including the header file.

Tip: When writing “convenience headers” for libraries intended to be used with IWYU, use // IWYU pragma: export (or a more general begin_exports/end_exports) to mark guaranteed indirect includes.

If you are not writing the library, there is a // IWYU pragma: keep to tell IWYU that you want to keep a given include, but there is no pragma to say that this is actually the correct include.

A list of all pragmas can be found here: https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/IWYUPragmas.md

Non-intrusive Pragmas: Mapping Files

But there is a non-intrusive solution to the problem: a mapping file. In it you can define which headers should be suggested for which symbols.

In our example, we want to say that boost::mp11::mp_list is provided by boost/mp11.hpp. So we create an mp11.imp file with the following contents:


[
{ symbol: ["boost::mp11::mp_list", "private", "<boost/mp11.hpp>", "public"] }
]

It has a JSON like syntax containing an array of directives. Here we use the symbol directives specifying that the given symbol (boost::mp11::mp_list) with the given visibility (it must always be private for some reason) is provided by the given header (boost/mp11.hpp) with the given header visibility (usually public).

Then we simply pass that file to IWYU with -Xiwyu –mapping_file=mp11.imp, and IWYU will no longer complain.

Tip: Use a symbol mapping to override the headers IWYU will suggest you include for it.

This can be done for all symbols but is tedious.

Luckily there is a better way: We can use the include directive to remap a header file to a different header file, or an entire directory to a header file:


[
{ include: ["@<boost/mp11/.*>", "public", "<boost/mp11.hpp>", "public"] }
]

The @ introduces a wild card, so here we are mapping all headers found in the given directory — which are considered public — to boost/mp11.hpp — which is also public.

Then IWYU will not suggest any of the headers of boost/mp11/* but boost/mp11.hpp instead. Since we already include it, it will not complain at all.

Tip: Use an include mapping to map one header file or multiple header files to a different header file.

The final directive is ref, this allows merging multiple .imp files together.

See the full documentation of mapping files here: https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/IWYUMappings.md

Conclusion

Personally, I don’t bother with mapping files. I don’t need to because I don’t run IWYU in an automated fashion. I just run it manually and review the suggested changes.

Doing that is definitely worth the trouble. I was able to improve compilation times noticeable by cleaning up some unnecessary include directives.

But hopefully, modules will make it obsolete in the near future.

Take IWYU to the Next Level

Companies from the Gaming, Automotive, Finance, and other leading sectors today are facing a growing demand for increased computing power during peak times, along with escalating pressure to improve their time to market. These challenges cannot be accomplished only by using IWYU.

IncrediBuild’s cutting-edge solution accelerates a wide range of time-consuming tasks, such as builds, tests and more, by distributing them across the user’s local network or VM’s and running them simultaneously. This ensures speed, accuracy, and visibility to organizations of all sizes in one comprehensive package.

Click here to start using IncrediBuild and accelerate your development.

Jonathan Müller (Guest)

Jonathan, a C++ expert, works on various C++ projects, writes libraries, speaks at conferences and blogs about library development at foonathan.net