LLVM提議向C語言中加入模塊機制

做者 Alex Blewitt ,譯者 臧秀濤 發佈於 十二月 05, 2012
程序員

在今年11月的LLVM開發者大會上,來自Apple的Doug Gregor作了一場講座,主題是向C語言中加入模塊(Module)機制。講座中提到:數據結構

長期以來,C的預處理器就是程序員和工具的問題之源。寫得很差的頭文件導致宏污染和包含順序等問題大量存在,程序員必須不斷地與之鬥爭。爲了緩解這些問題,開發者習慣上採用各類預處理器變通方案,好比LONG_MACRO_PREFIXES這種風格的很長的宏前綴,#include防衛語句,或是臨時使用#undef來處理庫中的宏。框架

另外一方面,工具也必須可以處理重複解析相同頭文件時所面對的內在可伸縮性問題,由於即使程序員並不但願,但不一樣的處理環境仍是可能影響頭文件的解釋方式。函數

模塊試圖解決這一問題,它的理念是:隔離特定庫的接口,並將其一次性編譯爲一種高效的、序列化的表示形式,當使用該庫時,能夠高效地導入,從而改進程序員的體驗和編譯過程的伸縮性。工具

該提議的基本前提是,做爲一種加速編譯並容許複用以前解析過的頭文件的手段,即便編譯最簡單的文件,也要避免使用預處理器來包含大量頭文件。在一個與「Hello World」同名的例子中,他強調到,一個包含64個字符的C程序通過預處理變成了11 074個字符,而一個包含81個字符的C++程序預處理後變成了1 161 033個字符。他還指出,由於包含要依賴於預處理器當時的狀態,因此從新解析頭文件可能讓程序很脆弱(好比,若是在 #include 以前使用#define FILE "myfile.txt",預處理器會破壞頭文件,從而致使構建失敗)。spa

他的建議是使用一個新關鍵字import來加載模塊。不一樣於預處理器的文本包含方式,編譯器可以理解該模塊是一個固定的版本,因此只解析一次。若是屢次使用相同的模塊,可使用前面解析過的同一數據結構,不須要每次都從新解析。調試

模塊也能夠嵌套,這容許導入子模塊;在所給的例子中,他演示了std模塊中的子模塊stdio可使用import std.stdio來包含。導入模塊以後,其中的全部公開API 就都導入到客戶代碼中了,但非公開API是隱藏的。爲了實現這種控制,模塊須要聲明哪些接口是公開的,哪些是非公開的,這能夠利用public 關鍵字:接口

// stdio.c
export std.stdio:
public:
typedef struct {
  …
} FILE;
int printf(const char*, …) {
  … 
}

請注意,在這個例子中,僅提供實現文件就能夠了,不須要頭文件。export包含了模塊的名字,這裏就是std.stdio。public用於區分API 的公開部分與非公開部分。這能夠編譯爲庫以及帶有充分元數據的函數類型和宏,供客戶代碼使用。進程

固然,這只是對將來的一個建議,並不是標準。那麼這種方式應如何實現呢?建議使用頭文件來處理現有模塊的公開API,並將模塊定義爲一組頭文件:開發

// /usr/include/module.map
module std {
  module stdio { header "stdio.h" }
  module stdlib { header "stdlib.h" }
  module math { header "math.h" }
  exclude header "assert.h"
}
module ClangAST {
  umbrella "AST/AST.h"
  module * { }
} // 可使用「import ClangAST.Decl」來導入AST/Decl.h

爲便於之後生成模塊(部分緣由是方便Objective-C框架導出模塊),「umbrella module」機制容許將一個目錄下的一組頭文件做爲單個模塊導出。

適於處理模塊的編譯器能夠在頭文件上利用單獨的一遍(Pass)來構建模塊,以後在隨後的頭文件中複用該模塊的信息。(編譯好的模塊應採用什麼格式還沒有指定,可能交由具體的編譯器定義。)模塊中也能夠加入附加的元信息,好比說明模塊運行所需的庫。這容許編譯器處理每一個模塊所需的連接標記,從而避免了用戶在連接時提供一大堆-l標記。

要使用模塊,客戶代碼惟一須要修改的是將#include替換爲等價的import。此外,由於在預處理後,模塊中帶有導出的函數和類型等信息,所以可以更好地進行編譯診斷;利用這些信息,編譯器報錯和IDE快速修復等功能也能提示所需的import,而不只僅是直接失敗。

最後,複用模塊信息也容許將調試信息與模塊關聯起來,而非讓這些信息重複出如今每一個目標文件中。編譯器和連接器就能夠少生成一些調試信息,反過來又加速了編譯過程。模塊也爲調試器提供了額外的類型信息(而不是將類型信息內聯到每一個目標文件中),所以調試器能夠報告模塊中定義的正確類型。

模塊提議的淨效應是,它提供了一種可以兼容現有工具的遷移途徑,同時,在用戶無需對原有代碼進行多少修改的條件下,還帶來了一些優勢(主要是提高了編譯速度,並改進了診斷錯誤消息和調試)。它也支持文件增量式升級,支持增量式地將單個預處理器指令切換爲基於模塊的導入機制。同時無需將編譯速度測量當作模塊表示的一部分,該工做已經在LLVM實現這些模塊時進行了。雖然模塊機制沒有考慮版本或命名空間(很大程度上是由於必須知足向後兼容性),但該機制若是得以普遍應用的話,可以顯著提高C和C++程序的編譯速度。此外,向後兼容性被明確提了出來,像LLVM的塊(block)規範,當須要的時候極可能用於支持其餘編譯器或規範中的包含。不過,在廣爲使用的C和C++編譯器中,LLVM編譯器工具鏈是惟一的一個保持創新並以身做則的了。其餘編譯器是否會引入這些特性,可能取決於LLVM 實現方案可否成功以及可以帶來哪些益處。

查看英文原文LLVM Proposes Adding Modules to C

相關文章
相關標籤/搜索