The simplest module
先看範例,就是Module版的Hello World
1 | export module hello; |
而Consumer Module的一方就這樣寫
1 | import hello; |
Description
從這個範例當中,Consumer這邊不用特別說
這邊要說的是如何寫個Module
Module Unit
在C++20,有了一個新的Compile Unit,就是Module Unit,所有Module Unit的Top Level Statement都是有module
關鍵字的
而module
前面有沒有export
就是決定這是哪一種Module Unit
- 有
export
的叫作Module Interface Unit - 無
export
的叫做Module Implementation Unit
Module Implementation Unit後面再說
The content of a module
一個Module擁有
- 一個以上的Module Interface Unit
- 零個以上的Module Implementation Unit
且每個Module裡面有且唯一一個Primary Module Interface Unit
在Hello World這個範例當然只有Primary Module Interface Unit
的存在,至於什麼是Primary Module Interface Unit,也是後面再說
export
在上面的範例,我們定義了兩個函數
1 | export void hello_world() {}; |
不塗於傳統的header file方式,如果是傳統的header file,兩個function應該都可以被外界可見,而Module Unit只有export
出的符號才能輩Connsumer看到
export的其他用法還有這樣
1 | // export entire namespace |
Module Implementation Unit
就像傳統header/implementation
的方法,我們可以把declaration/implementation
分離,因此我們有了Module Implementation Unit
重寫我們的範例,將implementation分開
因此我們的Module Interface Unit就變成
1 | export module hello; |
而Module Implementation Unit則是
1 | module hello; |
如同之前所說的,module前面沒加export
的就是Module Implementation Unit,而在function implementation前面也沒加export
,就跟傳統的方式很像
My thought on Module Implementation Unit
之前declaration/implementation
被人詬病的一點,就是你要維護兩份狀態,當你declaration改了之後,如果implementation沒改,會產生不可預料的後果,運氣好的話是編譯不過,運氣不好產生深層的Bug更難解
如同之前所說的,一個Module可以不必擁有Module Implementation Unit
那存在的必要是什麼?
我認為是將舊有的Source Code Mitigation到C++ Module的方式
如同現在流行的header only library
一樣,未來的Module應該僅由Module Interface Unit組成
Import other module
寫Module時不免使用到其他Module,讓我們定義一個新的Module
1 | export module world; |
而我們的hello module就變成這樣
1 | export module hello; |
注意,import只能放在top level module declaration之下,不能交換順序
接著要回去看Consumer的部分了
Visibility control
此時我們的Consumer會是這樣
1 | import hello; |
這裡該注意的點,在hello module當中雖然import了world,但是不
會再次輸出symbol到hello module metadata中
因此如果Consumer沒加上import world
時,會發現找不到obj的情形
但如果我們將hello改成這樣
1 | export module hello; |
這邊將我們import進來的Module再度export出去,這也是我們細分module的基礎
那麼Consumer不加import world
也是可以正常運行
Divide module into small parts
當一個Module大起來之後,要降低複雜度,細分成更小的Block是需要的,而其中又有兩種方法
Sobmodule
我們將hello_world分成兩個function
一個放在hello.sub_a
,另外一個放在hello.sub_b
直接看程式碼
1 | export module hello.sub_a; |
而另外一個就不貼了,看看我們hello module的定義
1 | export module hello; |
Reexport出hello.sub_a
和hello.sub_b
的exported symbol
Note
hello.sub_a
和hello_sub_b
是各自獨立完整的Module,submodule機制只是邏輯組合,讓他們看起來像是同一個Module
所以你Consumer這樣寫也是可以的
1 | import hello.sub_a; |
Module partition
不同於submodule,partition所分的sub partition不能個別存在
一樣直接看程式碼
1 | export module hello:part_a; |
跟上面很像,不過將.
改成了:
而我們的hello module則是
1 | export module hello; |
這邊有幾點要注意的
一個module name當中沒有
:
出現的就是Primary Module Interface Unit
,如同之前所說
一個以上的Module Interface Unit,有且唯一一個Primary Module Interface Unit
這個範例有三個Module Interface Unit,只有hello是Primary Module Interface Unit
而hello.sub_a
則是一個獨立的Module,只是邏輯上看起來是同一個MdoulePartition只能接受
import :part_a
的語法,import hello:part_a
是不對的Consumer只能寫
import hello
了
Global Module Fragment
Global Module Fragment是提供preprocessor使用的空間,因此你可以在這邊定義Marco,或是include未被moduleized的header file,而在這邊定義的symbol則不會輸出到module interface中,因此不會汙染全局環境
Global Module Fragment必須在export module
之前,就像這樣
1 | module; |
Reference
- Meeting C++ 2019, Modules - The Beginner’s Guide
- Meeting C++ 2019, Modules are Coming
- Understanding C++ Modules: Part 1: Hello Modules, and Module Units
- Understanding C++ Modules: Part 2: export, import, visible, and reachable
- Understanding C++ Modules: Part 3: Linkage and Fragments
- C++20 四大特性之一:Module 特性详解
- Modules
- C++20 新特性: modules 及实现现状