爲什麼應該用模塊取代C/C++中的頭文件?

 

發表於2012-11-28 11:29| 2685次閱讀| 來源CSDN| 14 條評論| 作者王然
 摘要:本文整理自Apple C++工程師Doug Gregor的演講Slide,他表示希望使用模塊(Module)這一概念替代C/C++中的頭文件,現已被C++標準化委員會任命爲Module研究組的主席,研究該提議的可能性。考慮到Apple的開源項目LLVM在編輯器領域中的地位,這一提議非常值得重視。

爲什麼應該使用模塊(Module)替代頭文件(Header)?

頭文件糟透了!


衆所周知,C程序在編譯時一般會預處理頭文件:

常規解決辦法如下:

  1. LLVM_WHY_PREFIX_UPPER_MACROS
  2. LLVM_CLANG_INCLUDE_GUARD_H
  3. template<class _Tp>
  4. const _Tp& min(const _Tp &__a,
  5. const _Tp &__b);
  6. #include <windows.h>
  7. #undef min // because #define NOMINMAX
  8. #undef max // doesn’t

但結果依然不夠理想,比較一下代碼與程序大小你會發現:

另外,頭文件形式的可擴展性天生不足。假設有n個源文件,每個源文件引用了m個頭文件,那麼構建過程的開銷會是m×n。這在C++中表現得尤爲糟糕。所以預說處理頭文件是一個非常糟糕的解決方案。

C家族的模塊系統

模塊是什麼?

  • 庫的接口(API)
  • 庫的實現

使用“import”導入已命名的模塊:

import會在源文件中忽略預處理狀態,並且選擇性導入,所以彈性(resilience)非常好。

使用“import”會導入什麼?

  • 函數、變量、類型、模板、宏,等等;
  • 公開API——其它的都隱藏;
  • 沒有特別的命名空間機制。

C/C++引入模塊會怎麼樣?


引入模塊的目標在於:

  • 在源文件中指定模塊名稱;
  • API公開;
  • 沒有頭文件!

要編寫一個模塊非常簡單,只需要使用export:

但是這麼做會遇到很多遺留問題:

  • 需要遷移現在基於頭文件的類庫;
  • 與不支持模塊的編譯器的互操作性;
  • 工具需要理解模塊;

所以應該使用引入模塊的過渡方案——直接從頭文件中構建模塊。這麼做有以下好處:

  • 頭文件有利於互操作;
  • 程序員不需要完全改變自己習慣的開發模式;

模塊地圖(Module Map)

模塊地圖是模塊的關鍵,用來定位模塊相關(子)模塊,包含以下功能:

  • 模塊定義命名(子)模塊

  • 頭文件在(子)模塊中包含命名頭文件的內容

保護傘頭文件(Umbrella Header)

  • 保護傘頭文件會在其目錄下包含所有頭文件信息
  • 使用通配符submodules (module *) 可以爲每一個包含的頭文件創建一個子模塊:
    1. AST/Decl.h -> ClangAST.Decl
    2. AST/Expr.h -> ClangAST.Expr

模塊編譯過程:

  1. 找到命名模塊的module map;
  2. 產生一個獨立編譯器實例;
  3. 在module map中解析頭文件。

編輯模塊文件過程:

  1. 在“import”聲明處導入模塊文件;
  2. 把模塊文件保存在緩存中待重用。

從頭文件到模塊化


從頭文件編程轉換到使用模塊非常簡單:

庫方面:合併複合定義的結構、函數、宏,並且爲頭文件導入依賴,最後編寫好模塊地圖;

開發者方面只需要從“#include”過渡到“import”:

  1. 把“#inlude”都換成“import”;
  2. 使用module maps確定(子)模塊(類似頭文件裏的“#include”);

當然,你也可以使用工具來自動化重寫代碼,非常簡單。

工具


編輯性能

使用模塊能夠提升語法解析性能:

  • 模塊化的頭文件只需要解析一次,之後放在緩存中,於是m×n --> m+n
  • 所有基於源(source-based)工具都能帶來好處
  • 自動鏈接大大簡化了庫的使用

  • 自動導入可以阻止“#include”帶來的可怕的調試結果

調試流

通過DWARF的雙程調試有損耗:

  • 只能獲得“用過”類型和函數
  • 丟失了行內函數、模板

另外調試過程還會出現信息冗餘

使用模塊的調試又會怎樣?

1.提高了構建性能

  • 編譯器發出的DWARF更少
  • 鏈接器清除重複的DWARF也更少

2.提高了調試體驗

  • 調試器的ASF精度非常完美
  • 調試器不需要尋找DWARF

總結


總而言之,C/C++使用模塊化非常有潛力:

  • 編譯/構建時間的縮短
  • 修復各種預處理問題
  • 更好的工具體驗
  • 通過設計,能夠平穩地過渡
  • Clang實現已經在進行了

這個Slide在Hacker News上引起了激烈討論,大部分網友還是贊成模塊化的方式:

baberman:對我來說沒什麼不便,而且還給出了過渡方案,可能會很適合某些C/C++項目。我們應該對任何提升C/C++性能的想法持開放態度。

greggman:預處理有什麼不好嗎?如果我不想用預處理,我完全可以使用Objective-C等。現在的機器性能已經夠強大了,import在編譯上的性能優勢對我來說沒有任何吸引力,我更喜歡C/C++的傳統方式。

msbarnett反駁greggman:我認爲這正是這份提議的精髓所在,你既可以保留使用#include,也可以從現在開始就轉向import模塊的方式。

_djo_:這個想法會非常有前途!要說缺點的話就是來得太遲了!

nkurz:我不同意“m個頭文件+n個源文件 --> m×n倍編譯性能”。只要使用“#ifndef _HEADER_H”就不會出現這個問題,或者,爲什麼不使用#include_once <header.h>來解決呢?預處理很可怕嗎?預處理確實有一些問題,但是卻是可以克服的。

SeoxyS:我不關心性能,現在性能已經不是問題了,我更關心怎樣給開發者更少的負擔。

各位CSDN網友是怎樣看的呢?歡迎一起討論。

相關鏈接:Doug的SlideHacker News上的討論

發佈了20 篇原創文章 · 獲贊 4 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章