C++20 的 Modules

最近看了两篇关于 C++ 20 Modules 很有意思的文章,戳:

《Understanding C++ Modules: Part 1: Hello Modules, and Module Units》

《Understanding C++ Modules: Part 2: export, import, visible, and reachable》

众所周知,C++靠预处理器处理头文件的方法被诟病已久。在 C++17 Module TS 后,标准委员会的 dalao 们终于在 C++20Modules 并入标准= =。(此处应该有掌声?

那么我们要怎么创建一个 Module 呢?标准引入了新的关键字 importmodule,并使用保留关键字 export 来导入、定义和导出 Module。

// hello_world.cpp
export module demos.hello.world;

export auto get_text()
{
  return "Hello C++ Modules!";
}
// main.cpp
import demos.hello.world;
import <iostream>;

int main()
{
  std::cout << get_text() << std::endl;
}

这是一个 C++20 Modules 版的 Hello World。注意 export module xxx.yyy.zzz 是一个 Module 的导出定义,函数 get_text 加上 export 就成为 Module 的导出符号。

Module 的名字可以是 aaa.bbb.ccc.ddd 这样的,此处借鉴了其他语言的命名规范,提升了可读性。

根据标准,每个 Module 都是一个 TU(Translation Unit) ,这样就可以在构建时得到更好的 Cache 编译的效果,缩短编译时间。

最后说下 import <iostream> 这样的用法。这是为了兼容以前老的头文件的写法,意思是把一个头文件当作一个独立的 TU 来编译,并且避免了命名空间污染和主文件中符号的影响。

-------------华丽分割线1--------------

一些语言,如 C# 有 partial class 的语法,可以把一个完整的 class 拆分为多个文件,最后合并编译。C++ Modules 提供了一个特性叫 Module Partition,可以把 Module 拆分在多个 .cpp 文件中定义。

在标准中,使用 Module 名称 + 冒号 + Partition 名称 的方式来定义一个 Module Partition

// home.cpp
export module home;
export import :father;
export import :mother;
// home_father.cpp
export module home:father;

export auto get_father_name()
{
  return "Xiaoming";
}
// home_mother.cpp
export module home:mother;

export auto get_mother_name()
{
  return "Xiaohong";
}
// main.cpp
import home;

int main()
{
  auto father = get_father_name();
  auto mother = get_mother_name();
}

这样 fathermother 就成为 Module home 的两个 Partition。吐槽一下 export import :xxx 这种语法,它的意思是把 Partition 先 import 进来再 re-export 出去,让用户可见。

这里多了一个概念:Module Interface Unit。很 Simple,对于一个 .cpp 文件,如果是 export module xxx 这样的,就是一个 Module Interface Unit,意思是导出接口单元。

比如上面的 home.cpp, home_father.cpp 和 home_mother.cpp 都是 Module Interface Unit

-------------华丽分割线2--------------

除了 Module Interface Unit,还有一个对应的东西叫 Module Implementation Unit ,模块实现单元。

同样很 Easy,如果一个 .cpp 文件中定义的是 module xxx,注意前面没有 export ,那么它就是一个 Module Implementation Unit

// animal.cpp
export module animal;
import :dogs;
import :cats;

export auto get_cat_name();
export auto get_dog_name();
// animal_cats.cpp
module animal:cats;

// "export" is not allowed here.
auto get_cat_name()
{
  return "狗·德川家康·薛定鄂·保留";
}
// animal_dogs.cpp
module animal:dogs;

// "export" is not allowed here.
auto get_cat_name()
{
  return "DOGE";
}
// main.cpp
import animal;

int main()
{
  auto cat_name = get_cat_name();
  auto dog_name = get_dog_name();
}

Partition catsdogs 就是两个 Module Implementation Unit ,而 animal.cpp 是一个 Module Interface Unit

注意 Implementation Unit 里面不允许有 export 出现,且需要在 Interface Unit 中 import 对应的 Partition。

想偷懒?同志好想法,还有种不需要 Partition 的简化写法。

// animal.cpp
export module animal;

export auto get_cat_name();
export auto get_dog_name();
// animal_impl.cpp
module animal;

auto get_cat_name()
{
  return "狗·德川家康·薛定鄂·保留";
}

auto get_cat_name()
{
  return "DOGE";
}

是不是感觉似曾相识?这个和目前的老式头文件声明和定义分离的用法如出一辙。

animal_impl.cpp 定义的还是叫 Module Implementation Unit ,animal.cpp 叫 Module Interface Unit

WHY? 既然可以写在一起,为什么要分离呢。一个是满足代码习惯,还有一个就是如果频繁修改实现,可以不动接口,提高增量编译速度~

-------------华丽分割线3--------------

考虑到程序员编码的方便性,标准规定了五花八门的 export 语法。如下所示,我们来欣赏一下。

// Export everything within the block.
export
{
  int some_number = 123;

  class foo
  {
  public:
    void invoke() { }
  private:
    int count_ = 0;
  };
}

// Export namespace.
export namespace demo::test
{
  struct tips
  {
    int abc;
  }

  void free_func() { }
}

// Export a free function.
export void here_is_a_function() { }

// Export a global variable.
export int global_var = 123;

// Export a class.
export class test
{

};

下面几种是非法的 export 写法,是无法编译的。

// Anonymous namespace cannot be exported.
export namespace
{

}

// Cannot export static variables.
export static int static_variable = 123;

// Cannot export static functions.
export static void foo()
{

}

// OK, export a namespace.
export namespace test
{
  // Error, cannot define static members in an exported namespace.
  static int mine = 123;

  // Error, as mentioned above.
  static void geek() { }
}

-------------华丽分割线4--------------

文中还介绍了其他的一些坑,比如 Implementation Unit BeastReachability & Visibility 巴拉巴拉,大家可以参考上面两篇文章……

个人觉得 Modules 作为重量级特性,还是给 C++ 带来了革新。引用一句某老外的说法:“Make CPP great again!”。

期待编译器这边的实现和新的 Build System 及可能的包管理工具,貌似一直在推进。

------- Over -------

原文地址:https://www.cnblogs.com/star-in-the-sky/p/11177009.html

时间: 2024-10-18 12:36:25

C++20 的 Modules的相关文章

Linux下性能测量和调试诊断工具Systemtap

一.简介 SystemTap是一个诊断Linux系统性能或功能问题的开源软件.它使得对运行时的Linux系统进行诊断调式变得更容易.更简单.有了它,开发者或调试人员不再需要重编译.安装新内核.重启动等烦人的步骤. 为了诊断系统问题或性能,开发者或调试人员只需要写一些脚本,然后通过SystemTap提供的命令行接口就可以对正在运行的内核进行诊断调试,以前需要的修改或插入调试代码.重新编译内核.安装内核和重启动等这些琐碎的工作完全消除.目前该工具并不支持对用户态应用的诊断调试,但是它们在以后会被添加

Jenkins持续集成学习及企业级应用

文档声明 该文档主体为去年末自主学习时总结,旨在为我司提供一套企业级持续集成解决方案.这篇文章现在看上去很稚嫩,但是当时花费了许多心血.希望将当时的学习心得拿出来与大家交流.该文档主要说明了jenkins持续集成部署的相关步骤,并着重实现了权限分组,邮件配置,插件配置的jenkins实现过程.对出现的问题进行解决,是一套持续集成的解决方案. 持续集成Continuous integration 提出 针对复杂度高的项目提出“早集成,常集成,频繁集成”来帮助项目在早期发现项目风险和质量问题 作用

Linux内核驱动编程

Linux内核驱动编程 2015-02-12 驱动程序基础的东西这儿就不罗嗦了,百度上有更好的资料,此处我们只是注重实际用处. 下面我们开始写程序: 一.初步helloword程序 首先是来一个简单的hello. hello.c代码: 1 /****************************** 2 3 the first program 4 5 Hello World! 6 7 ******************************/ 8 9 #include <linux/mod

JavaScript SandBox沙箱设计模式

沙箱模式常见于YUI3 core,它是一种采用同一构造器(Constructor)生成彼此独立且互不干扰(self-contained)的实例对象,而从避免污染全局对象的方法. 命名空间 JavaScript本身中没有提供命名空间机制,所以为了避免不同函数.对象以及变量名对全局空间的污染,通常的做法是为你的应用程序或者库创建一个唯一的全局对象,然后将所有方法与属性添加到这个对象上.缙云县外国专家局 代码清单1 : 传统命名空间模式 01 /* BEFORE: 5 globals */ 02 //

httpd2.4的新特性、以及基本应用

httpd-2.4: 新特性: (1) MPM支持运行为DSO机制:以模块形式按需加载: (2) event MPM生产环境可用: (3) 异步读写机制: (4) 支持每模块及每目录的单独日志级别定义: (5) 每请求相关的专用配置: (6) 增强版的表达式分析式: (7) 毫秒级持久连接时长定义: (8) 基于FQDN的虚拟主机也不再需要NameVirutalHost指令: (9) 新指令,AllowOverrideList: (10) 支持用户自定义变量: (11) 更低的内存消耗: 新模块

android studio 克隆git项目以及设置项目的全过程

项目挂在oschina(github也一样)上,之前没试过怎么clone下来,clone下来后发现好多error, 一时间解决不了,尝试了很多次,最后终于搞定,再次从头到尾做了一遍,并记录下来,如下:(共24张图片) 0.环境为window7+android studio1.0 1.安装TortoiseGit和git环境,自己根据实际的系统下载安装. 2.在oschina或者github上已经存在repository. 3.在本地新建一个文件夹,右键,找到Git Clone,如图 4.在URL里

网页入侵(适用于windows xp,只看思路)

[email protected]:~# setoolkit Do you agree to the terms of service [y/n]:y Select from the menu: 1)Social-Engineering Attacks 2)Penetration Testing (Fast-Track) 3)Third Party Modules 4)Update the Social-Engineer Toolkit 5)Update SET configuration 6)

Asp.net Mvc4 基于Authorize实现的模块访问权限

在MVC中,我们可以通过在action或者controller上设置Authorize[Role="xxx"] 的方式来设置用户对action的访问权限.显然,这样并不能满足我们的需求, 对于一般的MVC系统来说,如果我们定义一个controller来处理一个模块的话,我们大致有以下需求: 一,单个action的访问权限.如果删除,列表action 二,一个action两种权限,如edit(int? id)如果id为null则添加,或者修改 三,在此cotroller验证其它模块权限,

kernel panic 分析(NULL pointer dereference)

It is another typical kernel panic due to invalid address. Panic log: [ 20.896935] c3 554 (netd) Unable to handle kernel NULL pointer dereference at virtual address 00000012 [ 20.906200] c3 554 (netd) pgd = ffffffc02f746000 [ 20.910793] c3 554 (netd)