对于 Linux 上的 clang 和 libstdc++,目前在模块接口中使用任何标准库类型是否可行?

Sam*_*des 6 c++ clang libstdc++ c++-modules

到目前为止,在我看来,在 C++ 模块接口中包含几乎所有 libstdc++ 标头都会导致 clang 14.0.0 和与 GCC 11.2.0 捆绑在一起的 libstdc++ 出现编译错误。我想知道我是否做错了什么,或者这是否还不受支持。(我发现Clang 模块支持是“部分”的,但无法找到已实现的内容和未实现的内容。)

这是一个简单的模块示例,我在 Linux 中使用 clang-14,并与 libstdc++ 链接。它演示了 libstdc++ 标头可以在模块实现中使用,但此示例未在模块接口中 #include 任何内容:

// mod_if.cc
export module mod;
export int foo();

// mod.cc
module;
#include <iostream>
module mod;
int foo() {
    std::cout << "Hello world from foo()" << std::endl;
    return 42;
}

// use.cc
import mod;
#include <iostream>

int main() {
    std::cout << foo() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

这有效:

$ CXXFLAGS="-std=c++20 -fmodules -fprebuilt-module-path=prebuilt"
$ clang++ -c $CXXFLAGS -Xclang -emit-module-interface -o prebuilt/mod.pcm mod_if.cc
$ clang++ -c $CXXFLAGS -fmodule-file=prebuilt/mod.pcm mod.cc -o mod.o
$ clang++ $CXXFLAGS use.cc mod.o prebuilt/mod.pcm -o use
$ ./use 
Hello world from foo()
42
Run Code Online (Sandbox Code Playgroud)

但是,假设我想foo返回一个std::string

// mod_if.cc
module;
#include <string>
export module mod;
export std::string foo();

// mod.cc
module;
#include <string>
module mod;
std::string foo() {
    return "42";
}

// no use.cc needed since the error happens when building mod.cc
Run Code Online (Sandbox Code Playgroud)

这无法编译(显示了许多类似错误中的第一个):

$ clang++ -c $CXXFLAGS -Xclang -emit-module-interface -o prebuilt/mod.pcm mod_if.cc
$ clang++ -c $CXXFLAGS -fmodule-file=prebuilt/mod.pcm mod.cc -o mod.o
Run Code Online (Sandbox Code Playgroud)
In file included from mod.cc:2:
In file included from /usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/string:40:
In file included from /usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/char_traits.h:39:
In file included from /usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/stl_algobase.h:64:
In file included from /usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/stl_pair.h:65:
/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/compare:348:33: error: redefinition of '__cmp_cat_id<std::partial_ordering>'
      inline constexpr unsigned __cmp_cat_id<partial_ordering> = 2;
                                ^
/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/stl_pair.h:65:11: note: '/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/compare' included multiple times, additional include site in header from module 'mod.<global>'
# include <compare>
          ^
/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/stl_pair.h:65:11: note: '/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/compare' included multiple times, additional include site in header from module '<global>'
# include <compare>
          ^
mod.cc:1:1: note: <global> defined here
module;
^
Run Code Online (Sandbox Code Playgroud)

目前是否有一种方法可以使该代码正常工作(无需为 libstdc++ 标头编写模块映射)?为什么会出现这个错误?全局模块片段中包含的声明被导出听起来很奇怪inline constexpr,但我并不声称能够很好地理解模块。

Sam*_*des 1

好的,这对于一个大型项目来说是有效的。请注意,这是半年前的事,所以世界可能已经发生了变化。

我最终创建了一个标头“sys.hh”,它#includes 项目中使用的几乎所有系统标头。似乎重要的是,此文件直接或间接#included 的任何内容都不会在链接到最终二进制文件的任何内容中直接或间接(在模块系统之外)#included。

我的“sys.hh”看起来像这样:

#include <algorithm>
#include <array>
#include <assert.h>
#include <atomic>
#include <bits/std_abs.h>
// 100+ lines omitted, including things like glib, gtk, libjpeg
#include <vector>
#include <x86intrin.h>
#include <zlib.h>

// Macros won't get exported, so whatever the code needs, redefine as
// constexpr (or consteval functions) here. Unfortunately, I don't think
// there's a way to retain the name of the macro; so add an underscore.
// Also put them in a namespace.
#define MEXP(X) constexpr auto X ## _ = X;

namespace sys {
    MEXP(INTENT_PERCEPTUAL);
    MEXP(INTENT_RELATIVE_COLORIMETRIC);
    MEXP(INTENT_SATURATION);
    MEXP(INTENT_ABSOLUTE_COLORIMETRIC);
    MEXP(G_PRIORITY_DEFAULT_IDLE);
}
Run Code Online (Sandbox Code Playgroud)

我的模块映射文件包含这样的条目:

module sys {
    header "prebuilt/sys.hh"
    use _Builtin_intrinsics
    export *
}
Run Code Online (Sandbox Code Playgroud)

编译这个头文件/模块是一个增量过程;您将遇到无法编译的模块,因为它们间接包含相同的标头,因此您将它们添加到此文件中并重建,直到它起作用。

请注意,管理构建依赖关系对于模块来说变得更加重要。至少半年前,似乎还没有好的(自动)方法来发现需要重建的内容。由于模块的名称并不能说明它在源代码中的位置,因此这变得更加棘手。