目录

一、什么是modules

二、vs版本要求

三、项目配置

四、标准库模块引入方式

五、兼容旧式写法

六、导出类与变量

七、文件后缀

八、实现与主接口分开

九、模块命名与分区

十、引入模块

十一、模块分区

十一、模块私有部分

十二、结语


大家好,我是略游。今天讲一讲我在vs上操作C++20新标准模块(modules)的经验。此文章具有时效性,只代表当前实验结果,也可作为一个基本写法入门,将来的编译器一定会完善得更好。

一、什么是modules

modules是C++20标准的源码组织方式,以替代#include等传统的源码组织方式。与它相关的基本知识网上有很多文章,废话不多说直接实际操作。

二、vs版本要求

官方文档说16.10版本以上,更新最新版本即可。使用vs2022也可,不过需要注意的是需要安装以下工具:

虽然在随后的实验中,我没有成功使用上标准库的模块接口,但这个工具是否必须安装,我暂时懒得测试了,在这里获取工具:

三、项目配置

首先设置C++语言标准为/std:c++ latest,并且启用标准库模块

在定义使用最新特性的命令行/await

在链接器中关闭使用增量链接,同时使用程序数据库,这二者是配套的,如果不关闭增量链接,会与模块编译有冲突,后者不能正常识别修改。

四、标准库模块引入方式

按照微软文档的说法,如下即可导入标准库模块:

import std.core;
import std.filesystem;
//...

但实际操作中很困难,会提示一些编译选项冲突。除了/EHsc和/MD选项,还有一些其他的定义冲突,例如_DEBUG宏。

文档链接如下:

Overview of modules in C++ | Microsoft Docs

五、兼容旧式写法

要以#include包含旧式头文件,可以如下写:

module;
#include "Head.h"
export module Test;

这里包含了一个头文件Head.h,然后导出了一个模块叫Test。但是此处有一些局限性,Head.h不能是预编译头,预编译头是msvc的一种加快编译速度的方式,但是这里是不能兼容的。

在Head.h里面我包含了一大串头文件,这样我们也能使用上标准库。当前这样的写法,缺点是每次修改都需要重新编译所包含的头文件内容。预编译头可以解决这个问题,等完全使用上模块后,也能解决这个问题。现在只是兼容的写法。

//Head.h
#pragma once
#include <array>      //!< 数组
#include <list>           //!< 链表
//more...

值得一提的是,我在包含windows头文件时,遇到一些报错无法解决,最后只好不直接包含windows头文件了,暂时使用传统方法隐藏起来。

语法很简明,在“module;”和“export module Test;”之间就是旧式的包含头文件方式。

六、导出类与变量

前面加export即可导出类与全局变量,如下:

module;
#include "Head.h"
export module Test;export class Test
{//...
};export Test g_test;

这里就不用再像以往一样,害怕变量重定义,不能直接放置到头文件。在以前的写法就是这样的:

//--------------Test.h
#include "Head.h"class Test
{//...
};extern Test g_test;//--------------Test.cpp
Test g_test;

另外constexpr常量并不能直接导出,因为它是不变量,在编译期就会替换为数值,并不会真实存在这个变量,只能通过命名空间间接导出。

//-----------failed
export constexpr size_t DEFINE_NUM_GOODS_00 = 16 * 22;//-----------ok
export
namespace Define
{constexpr size_t NUM_GOODS_00 = 16 * 22;
}

七、文件后缀

C++标准没有规定文件的后缀名必须是什么,但是到目前为止,msvc必须使用.ixx后缀名,否则编译器会报错。

同时还应该设置文件项类型为C/C++编译器 ,如下图所示:

为了区分主接口文件,我们可以让实现文件后缀为.cpp,但仍然注意属性页里的项类型也要是C/C++编译器。

直接右键添加一个模块文件,这样也是正确的:

八、实现与主接口分开

我们可以直接在主接口文件实现Test::Init成员函数的定义,但也可以分开文件存放。例如以下文件Test.cpp,在兼容旧写法的同时,还需表明自己是Test模块的一部分。

module;
#include "Head.h"
module Test;void Test::Init()
{
}

在以前的写法,将函数定义在头文件的严重缺点就是,修改此函数就会导致整个文件被修改,然后所有包含此文件的源码都需要重新编译。而模板函数必须放在头文件,不但编译速度极慢,而且无法规避这个问题。而使用模块后便可以改进这个缺点,同时我们还能保持文件分开以保证代码的整洁性。

九、模块命名与分区

在一些示例中我们可以看到用点来分隔名字,比如export module Test.A,但实际上Test.A就是一个模块的名字,它是一个整体,并不代表Test模块的A分区(点在这里没有任何意义,只是为了好看)。正规的分区写法如下:

//Test.A是一个独立的模块
export module Test.A;//A是Test的分区
export module Test:A;

另外微软推荐文件名命名与模块名相似,假设一个模块叫Test.Point:Draw,那么它的文件名应该是Test.Point-Draw.ixx,实现文件可以叫Test.Point-Draw.cpp。即用-来代替:。

十、引入模块

很简单,如下写即可:

import Test;

如果想要“转发”模块,在自己引入X的时候,同时引入自己的地方也会引入X,如下所示:

export module Test;export import Test.Image;
export import Test.Sprite;
export import Test.Tex;
export import Test.Text;
export import Test.UI;

当另一个文件引入Test时,就不需要再引入Test.Image等了,只需import Test就会import Test.Image等。注意这里的.只是名字的一部分,而不是模块分区,只是为了好看。

如果只是Test自己使用,去掉export则不会转发:

export module Test;import Test.Image;
import Test.Sprite;
import Test.Tex;
import Test.Text;
import Test.UI;

十一、模块分区

前面用“.”来分隔名字只是一种权宜手段,它们本质上是单独的模块,其余文件不需要通过Test来导入Test.Image,可以直接导入Test.Image。而分区模块如果主接口没有导出,则其他文件是使用不了的。

假设有个Test:Struct分区,在Main.cpp想要导入。那么这么写是无效的:

import Test:Struct; //error

而用.的话,就可以:

import Test.Image; //ok

所以用分区的区别在于此,并且从定义上来说Test:Image是真正属于Test的。

要定义一个模块分区,如下:

export module Test:Struct;export
struct A
{//...
};

在上面的代码,在Test:Struct分区导出了一个类A。在Test主接口文件,同样可以选择import或者export import。这决定了Test:Struct是否对外界可见。

//导出模块Test
export module Test;//导入Define
import Define;//导入Test.Image并且使导入Test的自动导入Test.Image
export import Test.Image;//导入Test.Ui供自己使用
import Test.UI;//导入分区,但是不导出,也可以前面加export导出
import :Struct;

注意import模块分区时,则必须省略冒号前面的模块名, 想必编译器也是由此判断的。因为不属于Test的模块会import Test:Struct,然而这是不允许的。而属于Test的模块,是知道自己在Test的,所以可以省略“:”前的Test。

十一、模块私有部分

在声明module :private之后的内容只对自己文件可见,当然这是对于实现文件模块分区来说的。因为一个类或变量是否导出,取决于前面是否有export关键字。

module :private;

十二、结语

如果你觉得此文章有帮到你,可以点击收藏,然后点击关注,这可以极大的支持我发更多的文章。

你还可以加我的QQ群讨论:游戏编程星云阁 170100866

【C++20】vs2019使用modules的实际操作相关推荐

  1. Linux常用命令(本篇包括,Linux目录结构介绍、Linux Shell介绍、9个常见命令介绍、文件的概念、文件的操作(20个)、目录的操作、文件和目录的权限、文件压缩及解压缩)

    Linux常用命令(本篇包括,Linux目录结构介绍.Linux Shell介绍.9个常见命令介绍.文件的概念.文件的操作(20个).目录的操作.文件和目录的权限.文件压缩及解压缩)         ...

  2. 【华为软件特战队2023.2.20笔试题】 最小缩进操作次数

    [软件特战队2023.2.20笔试题] 最小缩进操作次数 题目描述 解题思路 伪代码 完整代码 结尾 朋友分享给我的,简单记录下 题目描述 请实现一个简单的代码缩进功能,把一段未缩进的代码,通过多次操 ...

  3. 20 个短小精悍的 pandas 骚操作

    本次为大家整理了一个pandas骚操作操作的大集合,共20个功能,个个短小精悍,一次让你爱个够. 1. ExcelWriter 很多时候dataframe里面有中文,如果直接输出到csv里,中文将显示 ...

  4. 必知之vs2019添加外加库文件操作

    温馨提示 添加的目录如果放在与main.cpp文件的相同目录下,可以使用相对目录即可,"./xxx目录" 配置结束如果没有立即生效,可以重新打开vs2019测试 1.添加工程的头文 ...

  5. C++20 要来了!

    导读:C++的新标准又双叒叕要到来了,是的,C++20要来了! 本文经授权转自公众号CSDN(ID:CSDNnews),作者:祁宇 ▲图片来源:udemy.com 几周前,C++标准委会历史上规模最大 ...

  6. K8Sv1.20二进制多master部署

    二进制搭建 Kubernetes v1.20 k8s集群master01:192.168.80.80 kube-apiserver kube-controller-manager kube-sched ...

  7. linux文件编辑操作,Linux下文本编辑及其文件操作

    文本编辑及其文件操作 Vim 命令模式: dd 删除当前行 yy 2yy nyy 复制 从光标算起,复制n行 p 粘贴 默认粘贴在光标的下一行 u 撤销 末行模式: :wq 保存退出 :wq! 强制保 ...

  8. vue 怎么全局到入常量_午后躺椅上看关于Vue的20点

    1.var,const,let的区别 首先,必须明确let 与const是ES6 中为了修复var的缺陷引入的,那么var有什么缺陷呢? var 1.var没有块级作用域 在JS函数中的var声明,其 ...

  9. python文件对象提供了3个读方法、分别是-python3基础之文件对象操作

    1.向文本文件中写入内容 s = 'Hello world 文本文件的读取方法 文本文件的写入方法 ' # 需要写入文件的字符串 print('显示需要写入的内容: {0:s}'.format(s)) ...

最新文章

  1. java mybatis enum_mybatis处理枚举类的简单方法
  2. 自动渗透测试工具集APT2
  3. 24点——判断4个数能否经过运算使得结果为24
  4. OpenCV将现有算法移植到G-API的实例(附完整代码)
  5. python自动写作软件_开发了一款写作软件(OSX,Windows),附带Electron开发指南
  6. Java中的策略设计模式-示例教程
  7. 突然讨厌做前端,讨厌代码_有关互联网用户最讨厌的广告类型的新数据
  8. [css] 手写一个满屏品字布局的方案
  9. JAVA提示定义常量_如何在Java中定义常量(Constant)
  10. i春秋 死亡ping命令 原理学习(命令执行+shell反弹)+复现
  11. C语言实现安全性极高的游戏存档并读档
  12. python打印N*N乘法表
  13. CTFshow-萌新 Writeup
  14. python爬取公众号文章如何获取发布时间
  15. CodeBlocks下创建.h .cpp文件
  16. linux秘钥对生成器,linux – GPG密钥生成器*不*使用熵
  17. 米转经纬度_经纬度换算米(经纬度精度换算米数)
  18. C#入门学习——飞行棋
  19. 多线程并发篇(1024节日快乐)
  20. Linux系统替换文件内容

热门文章

  1. 饥荒海难机器人怎么用_饥荒开发商又一款神作,难到吐血的生存游戏!
  2. 化工热力学重修补考第三章重点内容
  3. 六、继续学习Java的位运算符,发现真的简单
  4. 90后CV男神Workshop | 祥雨带你畅聊Model设计新视角
  5. 如何独立实现一个基于知识图谱的问答系统
  6. 报名 | “智见AI”SpringCamp:物体检测与深度神经网络模型设计
  7. ECCV 2018论文解读 | DeepVS:基于深度学习的视频显著性方法
  8. LeetCode刷题:滑动窗口模板以及典型例题
  9. java: -source 1.5 中不支持 diamond 运算符 (请使用 -source 7 或更高版本以启用 diamond 运算符)
  10. python生成表格文件_python读取和生成excel文件