c/c++语言开发共享C++20 的 Modules

最近看了两篇关于 ” C++ 20 Modules ” 很有意思的文章,戳: ” 《Understanding C++ Modules: Part 1: Hello Modules, and Module Units》 ” ” 《Understanding C++ Modules: Part 2: …

最近看了两篇关于 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_dog_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_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 beastreachability & visibility 巴拉巴拉,大家可以参考上面两篇文章……

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

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

——- over ——-

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请点击右边联系管理员删除。

如若转载,请注明出处:https://www.ctvol.com/c-cdevelopment/602732.html

(0)
上一篇 2021年5月11日
下一篇 2021年5月11日

精彩推荐