最近看了兩篇關於 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 -------