进度更新:VS2019 v16.8中对C++20模块的支持

进度更新:VS2019 v16.8中对C++20模块的支持

作者:BlogUpdater |  时间:2020-09-16 |  浏览:136 |  评论已关闭 条评论

蝎子
距离上一次我们分享C++20模块有关的更新,已经有一阵子了。在过去的一段时间,整个开发团队,保活工具集,项目系统和IDE团队都在努力工作,致力于为C++开发者提供超一流的C++20模块体验,那今天我们就来讲讲我们都做了些什么。坐稳了吗?

都有哪些新东西?

/std:c++latest隐式包含C++模块
由于MSVC已经开始实现C++模块标准,工具集会在任何时候都强制使用/experimental:module编译开关。因为模块已经被正式移入到C++20,编译器团体做了大量的工作,终于将模块合并到了/std:c++latest中。

关于/std:c++latest有如下需要注意的点
> /std:c++latest现在隐式包含/permissive-。这意味着:当前依赖于编译器的许可行为与/std:c++ latest结合使用的客户现在必须在命令行上应用/permissive。注意:启用/permissive将会禁用模块。
> 现在,模块已转换为最新的语言模式,由于模块和导入被转换为关键字,因此某些代码可能会无法工作。
我们已经记录了一些常见方案。 MSVC所实现的文件转换模块和导入到关键字有更多方案,请参考P1857R1。
> Visual Studio附带的标准的std*模块不能单独通过/std:c++latest来使用。标准库模块尚未标准化,因此仍处于试验阶段。要继续使用标准库模块,请使用/experimental:module作为其命令行选项的一部分。

专用模块片段
C++20在主模块接口中添加了一个新部分,称为专用模块片段[module.private.frag]。专用模块片段允许代码作者真正隐藏库的详细信息,而不必创建单独的C++源文件来包含实现的详细信息。想象一下在主模块接口中使用PIMPL模式的情况,如下图所说:

下面是代码导入客户端

私有模块分区是一种抽象屏障,可以使包含模块的使用者不受私有分区权限中定义的任何限制,从而有效地使单个”头”库具有更好的卫生性,改进的封装和减少的构建系统管理。

头文件包含
引入头文件单元后, 头文件包含的转换就成为可能了,[cpp.include]/7 使编译器可以将#include指令转换为import指令,前提是头文件名称指定了可导入(对于MSVC,通过使用/headerUnit头文件单元,可以将头文件标记为可导入)。
可以通过C/C++ -> 所有选项 -> 其他选项 并添加/translateInclude来启用此编译开关。在将来的版本中,用户可以选择希望包含转换的特定头文件单元,而不是简单的全有或全无的开关。

模块链接
除了简单的(前端)解析之外,C++模块对工具集的要求更高。
C++20引入了一种新的链接形式,即”模块链接”([basic.link]/2.2)。仅使用前端名称修饰,在模块技术规范(TS)时代开发的模块链接的实现的概念验证已证明是不完善的,并且在规模上效率低下。从Visual Studio 2019 v16.8开始,编译器和链接器一起工作以强制执行模块链接语义(无需前端名称处理)。新的链接器意味着用户可以使用命名模块更自由地编写代码,而不必担心可能发生的名称冲突问题,同时获得其他任何语言工具都无法提供的更强的odr保证。

强所有权(Strong Ownership)
MSVC工具集还采用了强所有权模型来实现程序链接。强所有权模型通过赋予链接器权限以将导出的实体附加到其所属模块上,从而带来确定性并避免链接名称冲突。此功能允许MSVC排除由于链接不同模块(可能是同一模块的修订版)而导致的未定义行为,这些模块报告了同一程序中不同实体的相似声明。

例如,考虑下面的例子,它演示了一种未正式定义的行为:

在实践中,总的来说,我们不会故意写这样的代码,但是在代码迁移,演变和维护下很难避免这种情况。
在没有强大的模块所有权语义之前,无法使用此类程序(带有munge的两个外部链接名称)。强所有权可以实心新的odr保证。有一篇不错的论文<<一个用于C++的模块系统>>,里面详细介绍了强所有权背后的原理,大家可以参考一下。

工程系统
使用C++模块最重要的部分,可能是拥有一个构建系统,该系统可以满足C++模块构建的要求,同时为没有陡峭学习曲线。VC工程团队一直与编译器工具集团队密切合作,以带来自动模块和头文件单元支持的经验,从而最大程度地减少了用户对它们的设置。

扩展名为.ixx或.cppm的文件被视为”模块接口”文件,但最终它由CompileAs属性控制,如下图所示:

如果要为.h头文件构建头文件单元,则需要将其项类型更改为”C/C++编译器”,因为默认情况下,.h头文件位于”C/C++头文件”组中,并且不会传递给编译器。默认情况下,扩展名为.h的”C/C++编译器”文件被视为”头文件单元”,如下图所示:

项目构建将自动扫描Modules和Header Unit文件(根据其”CompileAs”设置),以查找同一项目中的其他Module和Header Units依赖关系,并以正确的依赖关系顺序进行构建。

要引用另一个项目产生的模块或头单元,只需添加对该项目的引用即可。引用项目中的所有”公共”模块和头文件单元都可自动用于代码引用。

一个项目可以通过修改以下属性来控制将哪些模块和头文件(包括构建为头文件的模块)视为”public”,如下图所示:

编译器开关
许多以/module:作为前缀的编译器开关的实验阶段已经结束,因此我们已将它们移动到一个新的正式名称。
如下表所示:

IntelliSense
如果没有IntelliSense,则Visual C++的可视化就大打折扣了。在v16.8中,我们添加了对在模块中使用IntelliSense的全面支持,包括编写模块接口(.ixx)以及从导入的模块和头文件单元中获取上下文信息的功能。
Preview 3中将不提供对导入模块的IntelliSense支持,但我们计划在即将发布的预览版中启用它。请继续关注我们的CppCon演示,我们将演示如何使用IntelliSense。

工具集开发团队一直在努力确保编译器发出的C++模块格式具有完整的文档记录,并且可以在IntelliSense引擎中稳定使用。MSVC负责构建IFC文件,然后IDE将使用这个文件。其他工具使用MSVC IFC格式的能力对于健康的生态系统至关重要,而使用IFC输出的IntelliSense引擎是朝这个方向迈出的第一步。

总结
它还是来了,复杂的C++。
我保证,下辈子,我一定用上C++模块,决不食言。

最后
Microsoft Visual C++团队的博客是我非常喜欢的博客之一,里面有很多关于Visual C++的知识和最新开发进展。大浪淘沙,如果你对Visual C++这门古老的技术还是那么感兴趣,则可以经常去他们那(或者我这)逛逛。
本文来自:《Standard C++20 Modules support with MSVC in Visual Studio 2019 version 16.8》

标签:

评论已关闭。