What Are Additional Include Directories? Link to heading

In C and C++, additional include directories (as referred to in Microsoft’s Visual C++) are the directories where include files are searched when they are not present in the current directory or the standard library.

Example:

#include <iostream> // File from the standard library
#include "MyHeaderFile.h" // Where does this file exist?

MyHeaderFile.h will be searched in the same directory as the current file (which includes the include statement). If it is not found, the compiler will search in all additional include directories.

Who Defines the Additional Directories? Link to heading

You, as a programmer, define these directories. The compiler only defines the (additional) directories for the standard library shipped with the compiler.

But Why Fake Additional Include Directories? Link to heading

As you can read in the title of this post, I will explain how you can fake additional include directories. But why should you do something like that?

Short answer: If your build system has no support for additional include directories!

At work, I have to integrate our internal framework into the CAD system called CATIA. CATIA is one of the biggest commercial CAD systems in the world. Its framework is called CATIA CAA. I have worked with CAA for the last 12 years and built many features with it. CAA can be used in Visual Studio via RADE (Rapid Application Development Environment). RADE is, in short, a compiler toolset built on top of MSVC. RADE is mostly based on (naming) conventions. Shared libraries have to be in a specific place in the project directory to be detected by RADE.

A typical CAA project directory looks like the following (simplified):

  • MyFramework
    • PublicInterfaces
    • MyShareLib.m
    • MySecondShareLib.m
  • MyOtherFramework
    • PublicInterfaces
    • MyLib.m

The top-level directories contain the frameworks. Each framework contains a list of modules.

All directories ending with .m are code modules. They can build *.dll, *.exe, and some other types depending on the settings in the makefile in the module directory.

The PublicInterfaces directory contains all the header files that can be used from outside. And here is where the problem starts.

Generating a CAA Framework Link to heading

To integrate our prebuilt framework into CAA, I have to create a CAA framework that can be used by other frameworks.

It was easy to create the framework directory and copy the lib and dll files into specific folders so they can be used by RADE. The problem was the PublicInterfaces directory. I cannot add additional directories (I can only add absolute paths, which is not a good approach. The reason is simple: RADE copies the header files to the other framework when they are used, so relative paths would not work with RADE). And here is where my idea of faking additional include directories arose.

How Do Additional Include Directories Work? Link to heading

Additional include directories are really simple. They are just a list of directory paths searched from left to right to find the given header file.

For example, the list could look like C:\MyDir1;C:\MyDir2;D:\MyDir3. When there is an include statement like #include "MyHeaderFile.h", the preprocessor will look in the directory of the file containing the include statement, then in C:\MyDir1, C:\MyDir2, and D:\MyDir3. When it finds the file, it will stop the search and go to the next include statement (otherwise you will get an error).

To emulate that, we need to find a way to get the same behavior without the additional include directories property.

Conan the C++ Package Manager Link to heading

I will use Conan, the C++ package manager, which is a really great tool and I highly recommend it. With Conan, it is really easy to get all my additional include directories, lib, dll, and exe files because our framework is available as a Conan recipe. Without that, it would be really hard to get all the required information.

Fake It Link to heading

The idea is simple. I just have to copy all header files from right to left from the additional include directories into the PublicInterfaces. With that, every file that is the same will be overwritten. Because of the right-to-left order, I will get the same behavior as searching from left to right. That was easy, but there is one problem: the include statements in the copied include files are no longer valid because the paths are different.

Next step: copy all headers from all projects to the PublicInterfaces directory and replace all include statements with the new locations.

Thanks to a really long Python script with some regex magic, I was able to do that.

I also learned that the Windows filesystem (NTFS) has the same limitation as the Linux one: you cannot have a file and a directory with the exact same name in the same directory. I solved that problem by always preferring the directory, which works in my scenario.

Conclusion Link to heading

If your build system supports additional include directories, just use it. If you can’t, there is an option like the one presented in this post 😉.