VS2019 v16.5新特性出爐:增強對C++ Modules的支持

宣告

C++ 20標準馬上就要來了。伴隨著新標準的發佈,我們一直期待的Modules特性終於粉墨登場了。早在2017年,編譯器開發團隊就寫了一篇文章表明他們正在圍繞著Modules TS開展了工作,從那個時候開始,我們就一直努力的工作來改進這一特性並提升編譯器對這一標準特性的適應性。今天,我們終於覺得是時候來分享一些我們在Modules支持上的一些進展了。

有哪些新東西?

  • Header Units: 是一種新的翻譯單元(Translation Unit)形式,可以將它看做是一個可移植的PCH。
  • 新增上下文可感知的關鍵字module和import: 用於在代碼中使用這些標識符時提升足夠的靈活性。
  • Global module fragment: 當組合一個模塊的接口時,這是一種從模塊接口代碼中分離出非模塊代碼的新方式。
  • Module Partitions: 一種模塊接口的類型,用來組合一個大型模塊接口。

Header Units支持

在C++20 [module.import]/5中描述了一種新的翻譯單元的引入,這個新的翻譯單元就是所謂的Header Unit。有關它的具體語義已經在[module.import]/5中做出了詳細的解釋。其中最為重要的一點是:在被導出的頭文件中,定義在其中的宏也會被同樣的導出。我們舉個例子如下:

myheader.h

VS2019 v16.5新特性出爐:增強對C++ Modules的支持

myheader.cpp

VS2019 v16.5新特性出爐:增強對C++ Modules的支持

以上代碼可以使用新的編譯開關/module:exportHeader進行編譯,如下所示:

VS2019 v16.5新特性出爐:增強對C++ Modules的支持

請注意這裡對/module:exportHeader編譯開關的使用,它的輸入參數是一個頭文件的路徑(相對路徑或絕對路徑)。/module:exportHeader的輸出是我們內部定義的.ifc格式的文件。同時,在導入端,我們使用了/module:reference編譯開關,它的參數是:,它們中的任何一個都可以是相對路徑或絕對路徑。另外一個重要的地方是:如果沒有指定/Fo選項,則編譯器將不會自動生成object文件,它智慧生成.ifc文件。

/module:exportHeader的另一個使用場景是用於向用戶或構建系統提供一個文本參數,這個文本參數將會作為某些頭文件的名字被編譯器所看見。下面是一個例子:

VS2019 v16.5新特性出爐:增強對C++ Modules的支持

在上面的例子中,我們使用了/module:exportHeader來提示編譯器:編譯Header Units時使用頭文件的搜索順序,就像這個參數被寫到源文件中一樣。我們為這個功能也單獨提供了一個開關:/module:showResolvedHeader,這個開關將會在查找階段輸出被找到的頭文件的絕對路徑。

請注意:目前還不能同時使用/module:exportHeader和/experimental:preprocessor這兩個編譯開關,因為它們是不兼容的。在將來的版本中,這個問題將會得到解決。

上下文可感知的關鍵字module和import

在Modules TS中,module和import是兩個新的關鍵字。但是長久以來,我們觀察到這兩個名詞被大量的用在了用戶代碼中,因此在C++ 20中,有大量的關於這兩個關鍵字相關的提議。例如,R1703R1就是這樣一項提議,在這個提議中添加了對import關鍵字的上下文可感知性。另一項類似的提議是P1857R1(該提議還未被採納)。從目前看來,P1857R1是將module和import作為關鍵字的最為嚴格的提議。從VS2019 v16.5開始,MSVC將實現P1703R1和P1857R1這兩個提議。下面是一個例子:

VS2019 v16.5新特性出爐:增強對C++ Modules的支持

在上面的代碼中,MODULE和IMPORT將被視為標識符,而不是關鍵字。在相關的報告中我們將會看到更多類似的案例,特別是在P1857R1中,提供了一些有用的對比表格來描述此變更所帶來的影響。

Global module fragment

除了modules被引入到C++ 20之外,還有另外一項新的特性被引入,那就是所謂的”global module fragment”。global module fragment只會在組合模塊接口的情況下被使用,它的語義借用了Module TS中有關掛接到Global Module的實體的概念。global module fragment的目的是用來作為預處理指令例如#include的一個空間。因此,模塊接口可以被編譯,但是在global module fragment中的代碼卻不被模塊接口所擁有或者直接導出。以下是一個例子:

VS2019 v16.5新特性出爐:增強對C++ Modules的支持

在以上的代碼中,用戶希望使用vector和string但是不希望導出它們,它們只是導出函數f的實現細節。global module fragment是位於module;和export module m;之間的一個代碼區域。只有類似於#if和#define這樣的預處理語句才可以被寫入到這片代碼區域中。需要特別注意的是,前兩個定義的token不能看作是module。接口單元被看做是一個global module fragment,這一特性通過[cpp.global.frag]/1被強制執行。

Module Partitions

Module Partitions為用戶提供了一種新的組合模塊接口單元和組織模塊代碼的方式。在核心層面,Module Partitions是一些大型模塊接口單元的組成部分,它們並不作為接口用於外部模塊單元的導入。下面的例子中,我們定義了一個簡單的模塊接口,它使用到了兩個Partitions。

VS2019 v16.5新特性出爐:增強對C++ Modules的支持

為了編譯上面的例子代碼,可以使用如下的編譯指令:

VS2019 v16.5新特性出爐:增強對C++ Modules的支持

之前提到過的,上述代碼中的:internals partition只能被用在模塊接口m的實現部分,而不能被直接使用。

總結

C++ 20為C++世界帶來許多新的概念,其中最大的貢獻就是Modules,它徹底改變了我們編寫代碼的方式。MSVC將會幫助用戶重新認識我們組織API的方式以及背後的一些思想。當我們準備宣佈C++ 20實現全部完成之時,上文中提到的所有編譯開關和有關模塊的編譯器行為都有可能會改變。


VS2019 v16.5新特性出爐:增強對C++ Modules的支持


分享到:


相關文章: