This doc is a step by step example to show how to use clang-modules-converter to help the rewriting process of a real world project.
The changes required are listed in the git log history.
git checkout b0bb413ec51bce99ef6a90a325df7445cc6336e2
An example to build the project may be:
mkdir build
cd build
CXX=clang++ CC=clang cmake ../ -DCMAKE_BUILD_TYPE=Release -DASYNC_SIMPLE_DISABLE_AIO=ON
make -j4
Then we should be able to find compile_commands.json
in build
.
In this project, there are two special headers: coro/Generator.h
and experimental/coroutine.h
. They provides a fallback implementation for generator
and coroutine
in the STL when <generator>
and <coroutine>
are not available.
Their preamble are too complex to be analyzed. Some I tried to rewrite them manually.
The change lives in update special files.
Add a config file:
modules:
- name: async_simple
path: async_simple/async_simple.cppm
headers: async_simple/**/*.h
excluded_headers:
- "**/test/**"
mode: header-wrapper
controlling_macro: ASYNC_SIMPLE_USE_MODULES
is_std_module_available: false
srcs_to_rewrite: async_simple/uthread/internal/thread.cc
compilation_database: build/compile_commands.json
std_module_path: async_simple/std.cppm
Here we changed the implementation file thread.cc
to use modules. If we still hope to keep the implementation to use headers (then the user of the codes won't be forced to use modules), we can remove the line for srcs_to_rewrite
.
The change lives in Add config file.
Then let's run the tool:
clang-modules-converter --config=modules.yml
And the changed codes are in here.
We need fill the build scripts ourselves: cmake update:
if (${ASYNC_SIMPLE_BUILD_MODULES})
set(MODULES_SRCS "")
list(APPEND MODULES_SRCS async_simple.cppm)
list(APPEND MODULES_SRCS std.cppm)
target_sources(async_simple_static
PUBLIC
FILE_SET CXX_MODULES FILES
${MODULES_SRCS}
)
target_sources(async_simple
PUBLIC
FILE_SET CXX_MODULES FILES
${MODULES_SRCS}
)
endif()
Note that it is generally not good to make std.cppm
a part of your project.
In ThreadPool, there are some conditionally enabled codes based on platforms:
#ifdef __linux__
static void getCurrentCpus(std::vector<uint32_t> &ids) {
cpu_set_t set;
ids.clear();
if (sched_getaffinity(0, sizeof(set), &set) == 0)
for (uint32_t i = 0; i < CPU_SETSIZE; i++)
if (CPU_ISSET(i, &set))
ids.emplace_back(i);
}
#endif
Note that CPU_ISSET
macro is used. So we need to update the module interface to contain the macro used manually:
#ifdef __linux__
#include <sched.h>
#endif
The dependency may be introduced implicitly by <thread>
. So the tool failed to detect it. But even if it did, we still need to update manually since the tool is not smart enough to understand the conditionally includes.
In thread.cc
, we are able to see the warning:
// There unhandled macro uses found in the body:
// 'AS_UNLIKELY' defined in /home/chuanqi.xcq/async_simple/async_simple/CommonMacros.h:22:9
// 'assert' defined in /usr/include/assert.h:50:10
Then let's add it directly (but use <cassert>
instead of <assert.h>
):
https://github.com/ChuanqiXu9/async_simple/commit/dc2ab2becc359e8d7b3f31b5bda2561f45e495e7
Let's verify it by building the modules:
mkdir build_modules
cd build_modules
CXX=clang++ CC=clang cmake ../ -DCMAKE_BUILD_TYPE=Release -DASYNC_SIMPLE_DISABLE_AIO=ON -GNinja -DASYNC_SIMPLE_BUILD_MODULES=ON
ninja