标签: c++-modules

Linux 包管理器将如何处理 C++20 模块?

现在是 2020 年,C++20 以及期待已久的 C++ 模块功能即将到来。但是在看了一些关于 CppCon 的演讲后,我发现 C++ 模块处于一个奇怪的地方,尤其是对于 Linux 包管理器(pacman、apt、emerge 等...)

据我所知,C++ 模块是

  1. 依赖编译器
    • 您不能在 Clang 中使用由 GCC 构建的模块
    • GCC 9.1 模块不适用于 GCC 9.2
  2. 您可以拥有同一模块的许多不同版本
    • 只要它们不导出到同一范围内
  3. 如果依赖项更新,您需要重建模块

我的问题是,在所有滚动发布发行版中,编译器一直在更新,用户可能有自己的编译器版本。目前可以只更新编译器或更新libstdc++. 但是对于模块,似乎建议libstdc++在编译器更新时必须更新。

当编译器更新时,包管理器将如何处理更新,例如 STL?我不认为为每个版本的编译器构建每个版本的 STL 模块是可行的。用户必须构建自己的 STL 模块也不是一个好主意。

c++ dependency-management package-management c++20 c++-modules

12
推荐指数
1
解决办法
1144
查看次数

如何使用 Godbolt(编译器资源管理器)测试 C++ 模块?

为了在 C++20 中使用自编模块提出问题或演示错误/功能,能够使用Matt Godbolt 的编译器资源管理器会很棒。

例子:

test.cpp(模块测试):

export module test;

export template<typename T>
void do_something(const T&)
{
}
Run Code Online (Sandbox Code Playgroud)

编译 clang++ -std=c++20 -stdlib=libc++ -fmodules -c -Xclang -emit-module-interface -o test.pcm test.cpp

主.cpp:

import test;

int main() {
    do_something(7);
}
Run Code Online (Sandbox Code Playgroud)

编译 clang++ -std=c++20 -stdlib=libc++ -fmodules -fimplicit-modules -fimplicit-module-maps -fprebuilt-module-path=. main.cpp

问:有没有办法用编译器资源管理器来做到这一点?

c++ c++20 c++-modules compiler-explorer

11
推荐指数
1
解决办法
1189
查看次数

使用 CMake 的实验性模块依赖扫描构建 C++ 模块

CMake 有实验性的 C++20 模块依赖扫描 ( !5562 )。我尝试使用 CMake 3.20、g++-11 和 ninja-1.10 来构建一个带有模块的项目。

// main.cpp
import mod;
int main() { return 0; }
Run Code Online (Sandbox Code Playgroud)
// mod.ixx
export module mod;
export void f() {}
Run Code Online (Sandbox Code Playgroud)

CMakeLists.txt 是对https://gitlab.kitware.com/ben.boeckel/cmake/blob/cpp-modules/Modules/Compiler/GNU-CXX.cmake的改编

cmake_minimum_required(VERSION 3.20)
project(simple)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_DEPFILE_FORMAT gcc)
set(CMAKE_CXX_DEPENDS_USE_COMPILER TRUE)

string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE
        "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E -x c++ <SOURCE>"
        " -MT <DYNDEP_FILE> -MD -MF <DEP_FILE>"
        " -fmodules-ts -fdep-file=<DYNDEP_FILE> -fdep-output=<OBJECT>"
        " -fdep-format=trtbd")

set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "gcc")

set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG
        " -fmodules-ts -fmodule-mapper=<MODULE_MAP_FILE>"
        " -fdep-format=trtbd -x c++")

set(CMAKE_CXX_FLAGS "-fmodules-ts")
add_executable(simple main.cpp …
Run Code Online (Sandbox Code Playgroud)

c++ gcc cmake c++20 c++-modules

11
推荐指数
1
解决办法
997
查看次数

C++ 模块从另一个模块转发声明实体

我最近一直在尝试使用 GCC 11 将代码库转换为 C++20 模块。但是,我遇到了以下情况。首先,这是使用标头完成的方法:

\n

\n
class B;\n\nclass A {\n    public:\n        void f(B& b);\n};\n
Run Code Online (Sandbox Code Playgroud)\n

a.cpp

\n
#include "A.h"\n#include "B.h"\n\nvoid A::f(B& b)\n{\n    // do stuff with b\n}\n
Run Code Online (Sandbox Code Playgroud)\n

(Bh的内容在这里并不重要)

\n

需要注意的是 B 的前向声明。并不是每个使用 A 的人都应该关心 B,所以我使用前向声明来阻止重新编译的发生。有了标题,这种情况就完美了。

\n

问题在于尝试将此代码转换为模块时。主要问题是实体与声明它们的模块相关联,因此在 Ah 中向前声明是不可能的。我尝试在全局模块中进行前向声明,但编译器仍然抱怨 B 的定义与其声明位于不同的模块中。我还尝试使用第三个模块,其中仅包含 B 的前向声明,但这仍然是在两个不同的模块中声明和定义 B。所以,我的主要问题是:如何从模块外部转发声明某些内容? 我也会对最终产生相同效果的方式感到满意:当 B 更改时,A 的用户不需要重新编译。

\n

在搜索时,我发现了一些地方谈论类似的情况,但由于某种原因它们都不起作用。他们不起作用的原因:

\n
    \n
  • 有人说有一个带有前向声明的模块。正如我上面所说,这是行不通的。
  • \n
  • 有人说要使用声明的所有权声明。然而,它们已从最终的 C++ 标准中删除。
  • \n
  • 有人说使用模块分区。然而,这仅当 A 和 B 位于同一模块中时才有效。A 和 B 不应该在同一个模块中,所以这是行不通的。
  • \n
\n

编辑:为了回应评论,以下是我尝试过的一些事情的详细信息:

\n

尝试 1:在 A.mpp 中转发声明 …

c++ forward-declaration c++20 c++-modules gcc11

11
推荐指数
1
解决办法
1734
查看次数

对于具有传递标准库“#include”的 C++ 项目来说,“import std;”是一个现实的目标吗?

Visual Studio 2022 17.5 及更高版本支持导入 C++23 标准库命名模块std,方法是将 的所有实例替换#include <header>import std;where<header>是标准库头。

根据教程,有一些限制,例如:

不要混合和匹配导入 C++ 标准库头文件和命名模块。例如,不要 #include <vector> 和 import std; 在同一个文件中。

这是否意味着如果我import std;在文件中使用(通过“文件”我假设本教程指的是.cpp文件或其他翻译单元),那么它所包含的任何非标准库头都#include不允许传递到#include标准库标题?

我很确定答案显然是肯定的,因为在预处理之后,是否存在并不重要#include- 它的内容最终将.cpp与语句混合在文件中import

但是,除了彻底搜索所有传递性之外,还可以采取什么措施来防止混合呢?#include但是,除了彻底搜索所有传递性以查看是否存在任何标准库头之外,import std;特别是如果一个项目依赖于 Boost 之类的东西,那么在 Boost 本身作为模块提供之前,这似乎不是一个现实的选择,对吗?

那么与此同时,我的说法对吗?import std; is basically only realistic for new or small projects where the standard library header dependencies (both direct and transitive) …

c++ c++-modules

11
推荐指数
1
解决办法
424
查看次数

如何在 C++20 模块中使用模板显式实例化?

本答案中所述,模板实例化允许减少编译时间和大小,因为不需要为使用它们的每个新文件中的每个新类型重新编译模板。

我也很高兴C++20 模块应该如何提供一个干净的解决方案来向外部项目公开模板并减少 hpp/cpp 重复。

允许它们一起工作的语法是什么?

例如,我希望模块看起来有点像(未经测试,因此可能是错误的代码,因为我没有足够新的编译器/不确定它是否已实现):

helloworld.cpp

export module helloworld;
import <iostream>;

template<class T>
export void hello(T t) {
    std::cout << t << std::end;
}
Run Code Online (Sandbox Code Playgroud)

helloworld_impl.cpp

export module helloworld_impl;
import helloworld;

// Explicit instantiation
template class hello<int>;
Run Code Online (Sandbox Code Playgroud)

主程序

// How to prevent the full definition from being imported here, which would lead
// hello(1) to instantiate a new `hello<int>` instead of reusing the explicit instantiated
// one from `helloworld_impl.cpp`?
import helloworld;

int main() {
    hello(1); …
Run Code Online (Sandbox Code Playgroud)

c++ templates c++20 c++-modules

10
推荐指数
1
解决办法
868
查看次数

如何分发 C++ 模块?

使用“旧”C++ 编译模型组织和构建项目通常遵循一些常见做法:

  • 将您的公共标题放在一个include目录中。
  • 将实现和私有头文件放在一个src目录中
  • 从你的来源编译一个库
  • 将库(静态或动态)与公共头文件一起分发

当然,有许多不同的方法可以实现这一点,但这里的要点是:您将库二进制文件和公共标头作为纯文本分发。

现在有了模块,编译模型可以发生巨大的变化。但到目前为止我发现的一切都只解释了如何在封闭的项目中使用模块 - 没有分发,没有外部依赖。

现在可以将属于特定模块的所有内容放在单个文件中(这会有一些好处,例如避免重复声明)。这就提出了一个问题:我必须分发什么才能让其他人使用我的图书馆?我可以保密哪些部分?也许这可以通过实施单位解决?

c++ c++20 c++-modules

10
推荐指数
0
解决办法
182
查看次数

C++ 模块中的前向声明 (MSVC)

我最近一直在试验 MSVC 提供的模块实现,我遇到了一个有趣的场景。我有两个在它们的接口中相互依赖的类,这意味着我必须使用前向声明来编译它。以下代码显示了一个示例:

Module interface

export module FooBar;

export namespace FooBar {
    class Bar;

    class Foo {
    public:
        Bar createBar();
    };

    class Bar {
    public:
        Foo createFoo();
    };
}
Run Code Online (Sandbox Code Playgroud)

Module implementation

module FooBar;

namespace FooBar {
    Bar Foo::createBar() {
        return Bar();
    }

    Foo Bar::createFoo() {
        return Foo();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我想将这两个类拆分为它们自己的名为Foo和的模块Bar。但是,每个模块都需要导入另一个模块,因为它们的接口相互依赖。根据目前的模块提案,不允许循环接口导入。这篇文章建议使用proclaimed ownership声明,但是在模块的MSVC实现中似乎还没有实现。

因此,我是否正确假设目前无法使用 MSVC 提供的当前实现解决这种情况?还是我缺少一些替代方案?在这种情况下,情况非常简单,但是我在模块化具有许多具有此类依赖关系的类的库时遇到了这个问题。我意识到循环依赖通常表明设计不佳,但在某些情况下,它们不可避免或难以重构。

c++ circular-dependency visual-c++ c++-modules

9
推荐指数
1
解决办法
642
查看次数

从 clang c++20 模块导出全局变量

当使用 clang 12 构建 c++20 模块时(它也使用 clang 10 重现)从模块导出全局变量并在另一个编译单元中从同一个导出类声明全局变量会导致“'vtable for foo'的多重定义” ”。

我的理解是,c++20 模块可以使用单个 cppm 源创建(clang 对模块使用 cppm),并且编译器将从它生成模块声明和定义。在这种情况下,clang++ 似乎确实生成了两次 vtable。

这是设置

foo.cppm:

export module foo;

export struct foo {
   virtual void vf();
};

void foo::vf() {}

export foo module_foo; // variable, exported
Run Code Online (Sandbox Code Playgroud)

主.cpp:

import foo;

foo main_foo;

int main() {
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

构建主可执行文件的编译步骤:

/usr/lib/llvm-12/bin/clang++ -std=c++20 -fmodules --precompile foo.cppm -o foo.pcm
/usr/lib/llvm-12/bin/clang++ -std=c++20 -fmodules --compile foo.cppm -o foo.o
/usr/lib/llvm-12/bin/clang++ -std=c++20 -fmodules -fmodule-file=foo.pcm --compile main.cpp -o main.o
/usr/lib/llvm-12/bin/clang++ -std=c++20 -fmodules foo.o …
Run Code Online (Sandbox Code Playgroud)

c++ clang c++20 c++-modules

9
推荐指数
1
解决办法
185
查看次数

为什么 GCC 不完全支持 C++20 模块?

查看GCC C++ 语言功能支持状态页面,我发现 C++20 模块支持的两个方面仍然缺失,其中之一是部分支持:

  1. P1766R1:减轻小模块问题
  2. P1815R2:翻译单元本地实体
  3. P1103R3:合并模块

考虑到我们已经 2022 年了,为什么会出现这种情况?对于实施这些措施是否存在分歧?这仅仅是开发人员时间不足的问题吗?

c++ gcc g++ c++20 c++-modules

9
推荐指数
0
解决办法
2506
查看次数