Modules in Clang 11

In my previous post, I wrote about the support for C++20 modules in Visual Studio 2019 16.5. VC++ is not the only major compiler that has experimental support for modules. Clang has its own implementation, although only partial. In this post, I will discuss the support available in Clang 11. You can check the current status here.

Disclaimer: My experience with Clang is limited to compiling various snippets of code. Although documentation may exist in some inner circle of trust, it is not easily found using search engines. The content of this article was put together from several bits of information found online and large amounts of trial and error. Therefore, the information I present below may not be complete (although I hope it is correct). If you find anything that needs to be complemented or corrected, please leave a comment and I will update accordingly.

A first example

Let’s start again with the typical hello world application in C++ that looks as follows:

#include <iostream>

int main()
{
    std::cout << "Hello, World!\n";
}

To compile this with Clang you need to run the following command:

clang++ -Wall -std=c++2a -stdlib=libc++ main.cpp -o main

Noticed that I specified -std=c++2a to indicate support for C++20, although any other version would have worked. However, this switch is required to enable modules support, which used to be available only by specifying -fmodules-ts. This is now no longer necessarily when compiling with -std=c++2a.

This program can be modified to use modules instead by replacing the #include preprocessor directive with an import directive, as follows:

import <iostream>;

int main()
{
    std::cout << "Hello, World!\n";
}

Compiling this program requires some changes to the command we executed previously.

clang++ -Wall -std=c++2a -stdlib=libc++ -fimplicit-modules -fimplicit-module-maps main.cpp -o main

So, what do these additional arguments represent?

  • -fimplicit-modules tells the compiler to use implicit modules, which is a feature that automatically translates #include directives into import statements (i.e. headers into modules).
  • -fimplicit-module-maps tells the compiler to implicitly search the file system for module map files. A module map file is a file containing the mapping between existing headers and the logical structure of a module. You can learn more about these from here.

Writing a module

The next step is to write a module that exports a function, called get_greeting_text() in this example, that returns the text that will be printed to the console in the main program. The following snippet shows the content of the module from a file called greetings.cpp.

export module greetings;
 
import <string>;
 
export std::string get_greeting_text()
{
    return "Hello, World!";
}

The changes to the main program are simple: import the greetings module and call the get_greeting_text() function.

import <iostream>;
import greetings;

int main()
{
    std::cout << get_greeting_text() << '\n';
}

Compiling the module and the main program this time gets a bit more complicated. The following commands must be executed:

clang++ -Wall -std=c++2a -fimplicit-modules -fimplicit-module-maps -c greetings.cpp -Xclang -emit-module-interface -o greetings.pcm
clang++ -Wall -std=c++2a -fimplicit-modules -fimplicit-module-maps -fprebuilt-module-path=. main.cpp greetings.cpp -o main

The first command, compiles the module and generates a file called greetings.pcm. PCM here stands for “precompiled module”. This file is the Binary Module Interface (BMI) file and is the equivalent of the IFC file generated by the VC++ compiler. GCC is using yet another term, “Compiled Module Interfaces” and the extension CMI. The second command, compiles the main program. You will notice in the arguments, a new switch called -fprebuilt-module-path. This tells the compiler what is the path of the folder containing the prebuilt modules (the .pcm files).

In this example, the module file had the extension .cpp. However, Clang supports other extensions, including .cppm and even .ixx. However, when using different extensions, the commands for building the module change.

These are the commands to build the same sample if the greetings module was available in a file called greetings.cppm.

clang++ -Wall -std=c++2a -fimplicit-modules -fimplicit-module-maps -fmodules --precompile greetings.cppm -o greetings.pcm
clang++ -fmodules -c greetings.pcm -o greetings.o
clang++ -Wall -std=c++2a -fimplicit-modules -fimplicit-module-maps -fprebuilt-module-path=. greetings.o main.cpp -o main

These are the commands to build the same sample if the greetings module was available in a file called greetings.ixx.

clang++ -Wall -std=c++2a -fimplicit-modules -fimplicit-module-maps -fmodules --precompile -x c++-module greetings.ixx -o greetings.pcm
clang++ -fmodules -c greetings.pcm -o greetings.o
clang++ -Wall -std=c++2a -fimplicit-modules -fimplicit-module-maps -fprebuilt-module-path=. greetings.o main.cpp -o main

We can add more exports to the module, just as we did in the previous article. In the following example, the greeter class returns a random text every time its call operator is invoked.

export module greetings;
 
import <string>;
 
export std::string get_greeting_text()
{
    return "Hello, World!";
}

export struct greeter
{
   constexpr static const char* hellos[] {"Hello", "Hi", "Hey"};
   std::string operator()()
   {
      return hellos[rand() % 3] + std::string{", World!"};
   }
};

This new class can be used as shown below. If you run this program multiple times, you should see the second line changing randomly.

import <iostream>;
import <cstdlib>;
import greetings;

int main()
{
    std::cout << get_greeting_text() << '\n';

    std::srand(std::time(0));
    std::cout << greeter()() << '\n';
}

The commands required to build this program are the following:

clang++ -Wall -std=c++2a -fimplicit-modules -fimplicit-module-maps -c greetings.cpp -Xclang -emit-module-interface -o greetings.pcm
clang++ -Wall -std=c++2a -fimplicit-modules -fimplicit-module-maps -fprebuilt-module-path=. main.cpp greetings.cpp -o main

Exporting templates

Templates can also be exported from a module. In the next example, a module called foo, available in a file foo.cpp exports a class template foo and a function template called make_foo.

export module foo;
 
export template <typename T>
struct foo
{
    T value;
    
    foo(T const v):value(v){}
};
 
export template <typename T>
foo<T> make_foo(T const value)
{
    return foo<T>(value);
}

The exports from this module can be used as follows in main.cpp:

import <iostream>;
import <string>;
import foo;
 
int main()
{
    auto fi = make_foo(42);
    std::cout << fi.value << '\n';
    
    auto fs = make_foo(std::string("modules"));
    std::cout << fs.value << '\n';
}

To build this program, we must run the following commands:

clang++ -Wall -std=c++2a -fmodules -c foo.cpp -Xclang -emit-module-interface -o foo.pcm
clang++ -Wall -std=c++2a -fimplicit-modules -fimplicit-module-maps -fprebuilt-module-path=. main.cpp foo.cpp -o main

Partitions

As I mentioned in the beginning, Clang only supports C++20 modules partially. Unfortunately, this features is not available yet.

See also

You can learn more about Clang support for modules from the following articles:

4 Replies to “Modules in Clang 11”

  1. import ;

    I didn’t like this notation…

    import string;
    import iostream;

    simplicity is best = the creator of language tells that but why this ugly complexity again ?

  2. All views are subjective. Different people want different things. In the end that was the consensus in the committee so we have to take it as it is.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.