最近看了两篇关于 C++ 20 Modules 颇有意思的文章,戳:html
《Understanding C++ Modules: Part 1: Hello Modules, and Module Units》ios
《Understanding C++ Modules: Part 2: export, import, visible, and reachable》git
众所周知,C++靠预处理器处理头文件的方法被诟病已久。在 C++17 Module TS 后,标准委员会的 dalao 们终于在 C++20 把 Modules 并入标准= =。(此处应该有掌声?程序员
那么咱们要怎么建立一个 Module 呢?标准引入了新的关键字 import、module,并使用保留关键字 export 来导入、定义和导出 Module。github
// 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 编译的效果,缩短编译时间。ui
最后说下 import <iostream> 这样的用法。这是为了兼容之前老的头文件的写法,意思是把一个头文件看成一个独立的 TU 来编译,而且避免了命名空间污染和主文件中符号的影响。编码
-------------华丽分割线1--------------spa
一些语言,如 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(); }
这样 father 和 mother 就成为 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_dog_name() { return "DOGE"; }
// main.cpp import animal; int main() { auto cat_name = get_cat_name(); auto dog_name = get_dog_name(); }
Partition cats 和 dogs 就是两个 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_dog_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 Beast 、Reachability & Visibility 巴拉巴拉,你们能够参考上面两篇文章……
我的以为 Modules 做为重量级特性,仍是给 C++ 带来了革新。引用一句某老外的说法:“Make CPP great again!”。
期待编译器这边的实现和新的构建系统(Build System)及可能的包管理工具(Tooling & Package Manager),貌似一直在推动。
------- Over -------