没有规则可制作目标modules_VS2019 v16.5新特性出炉:增强对C++ Modules的支持

宣告

C++ 20标准马上就要来了。伴随着新标准的发布,我们一直期待的Modules特性终于粉墨登场了。早在2017年,编译器开发团队就写了一篇文章表明他们正在围绕着Modules TS开展了工作,从那个时候开始,我们就一直努力的工作来改进这一特性并提升编译器对这一标准特性的适应性。今天,我们终于觉得是时候来分享一些我们在Modules支持上的一些进展了。

有哪些新东西?

  • Header Units: 是一种新的翻译单元(Translation Unit)形式,可以将它看做是一个可移植的PCH。
  • 新增上下文可感知的关键字module和import: 用于在代码中使用这些标识符时提升足够的灵活性。
  • Global module fragment: 当组合一个模块的接口时,这是一种从模块接口代码中分离出非模块代码的新方式。
  • Module Partitions: 一种模块接口的类型,用来组合一个大型模块接口。

Header Units支持

在C++20 [module.import]/5中描述了一种新的翻译单元的引入,这个新的翻译单元就是所谓的Header Unit。有关它的具体语义已经在[module.import]/5中做出了详细的解释。其中最为重要的一点是:在被导出的头文件中,定义在其中的宏也会被同样的导出。我们举个例子如下:

myheader.h

1506bca37b48e7f94fc9231d68dbc564.png

myheader.cpp

7ee462d4bb04488b2207d643c1ec2786.png

以上代码可以使用新的编译开关/module:exportHeader进行编译,如下所示:

29dc3031beddf57e89aba44c5fa11cea.png

请注意这里对/module:exportHeader编译开关的使用,它的输入参数是一个头文件的路径(相对路径或绝对路径)。/module:exportHeader的输出是我们内部定义的.ifc格式的文件。同时,在导入端,我们使用了/module:reference编译开关,它的参数是:,它们中的任何一个都可以是相对路径或绝对路径。另外一个重要的地方是:如果没有指定/Fo选项,则编译器将不会自动生成object文件,它智慧生成.ifc文件。

/module:exportHeader的另一个使用场景是用于向用户或构建系统提供一个文本参数,这个文本参数将会作为某些头文件的名字被编译器所看见。下面是一个例子:

f2e6bfd1119804b0a8c2f3ca301f1f45.png

在上面的例子中,我们使用了/module:exportHeader来提示编译器:编译Header Units时使用头文件的搜索顺序,就像这个参数被写到源文件中一样。我们为这个功能也单独提供了一个开关:/module:showResolvedHeader,这个开关将会在查找阶段输出被找到的头文件的绝对路径。

请注意:目前还不能同时使用/module:exportHeader和/experimental:preprocessor这两个编译开关,因为它们是不兼容的。在将来的版本中,这个问题将会得到解决。

上下文可感知的关键字module和import

在Modules TS中,module和import是两个新的关键字。但是长久以来,我们观察到这两个名词被大量的用在了用户代码中,因此在C++ 20中,有大量的关于这两个关键字相关的提议。例如,R1703R1就是这样一项提议,在这个提议中添加了对import关键字的上下文可感知性。另一项类似的提议是P1857R1(该提议还未被采纳)。从目前看来,P1857R1是将module和import作为关键字的最为严格的提议。从VS2019 v16.5开始,MSVC将实现P1703R1和P1857R1这两个提议。下面是一个例子:

676f6b6e9d34e015f99c4eb25582300f.png

在上面的代码中,MODULE和IMPORT将被视为标识符,而不是关键字。在相关的报告中我们将会看到更多类似的案例,特别是在P1857R1中,提供了一些有用的对比表格来描述此变更所带来的影响。

Global module fragment

除了modules被引入到C++ 20之外,还有另外一项新的特性被引入,那就是所谓的”global module fragment”。global module fragment只会在组合模块接口的情况下被使用,它的语义借用了Module TS中有关挂接到Global Module的实体的概念。global module fragment的目的是用来作为预处理指令例如#include的一个空间。因此,模块接口可以被编译,但是在global module fragment中的代码却不被模块接口所拥有或者直接导出。以下是一个例子:

8ec4681c48d32bb3a1e31d499963c4cb.png

在以上的代码中,用户希望使用vector和string但是不希望导出它们,它们只是导出函数f的实现细节。global module fragment是位于module;和export module m;之间的一个代码区域。只有类似于#if和#define这样的预处理语句才可以被写入到这片代码区域中。需要特别注意的是,前两个定义的token不能看作是module。接口单元被看做是一个global module fragment,这一特性通过[cpp.global.frag]/1被强制执行。

Module Partitions

Module Partitions为用户提供了一种新的组合模块接口单元和组织模块代码的方式。在核心层面,Module Partitions是一些大型模块接口单元的组成部分,它们并不作为接口用于外部模块单元的导入。下面的例子中,我们定义了一个简单的模块接口,它使用到了两个Partitions。

4085b66231d82b63b386ecc8557815cd.png

为了编译上面的例子代码,可以使用如下的编译指令:

c2fabf3b8a72bf08506c76ab027120d2.png

之前提到过的,上述代码中的:internals partition只能被用在模块接口m的实现部分,而不能被直接使用。

总结

C++ 20为C++世界带来许多新的概念,其中最大的贡献就是Modules,它彻底改变了我们编写代码的方式。MSVC将会帮助用户重新认识我们组织API的方式以及背后的一些思想。当我们准备宣布C++ 20实现全部完成之时,上文中提到的所有编译开关和有关模块的编译器行为都有可能会改变。

460353cc2ea18f97f9f496e821b6b9ba.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值