为什么应该用模块取代C/C++中的头文件?
為什么應(yīng)該使用模塊(Module)替代頭文件(Header)?
頭文件糟透了!
眾所周知,C程序在編譯時(shí)一般會(huì)預(yù)處理頭文件:
常規(guī)解決辦法如下:
但結(jié)果依然不夠理想,比較一下代碼與程序大小你會(huì)發(fā)現(xiàn):
另外,頭文件形式的可擴(kuò)展性天生不足。假設(shè)有n個(gè)源文件,每個(gè)源文件引用了m個(gè)頭文件,那么構(gòu)建過(guò)程的開(kāi)銷(xiāo)會(huì)是m×n。這在C++中表現(xiàn)得尤為糟糕。所以預(yù)說(shuō)處理頭文件是一個(gè)非常糟糕的解決方案。
C家族的模塊系統(tǒng)
模塊是什么?
- 庫(kù)的接口(API)
- 庫(kù)的實(shí)現(xiàn)
使用“import”導(dǎo)入已命名的模塊:
import會(huì)在源文件中忽略預(yù)處理狀態(tài),并且選擇性導(dǎo)入,所以彈性(resilience)非常好。
使用“import”會(huì)導(dǎo)入什么?
- 函數(shù)、變量、類(lèi)型、模板、宏,等等;
- 公開(kāi)API——其它的都隱藏;
- 沒(méi)有特別的命名空間機(jī)制。
C/C++引入模塊會(huì)怎么樣?
引入模塊的目標(biāo)在于:
- 在源文件中指定模塊名稱(chēng);
- API公開(kāi);
- 沒(méi)有頭文件!
要編寫(xiě)一個(gè)模塊非常簡(jiǎn)單,只需要使用export:
但是這么做會(huì)遇到很多遺留問(wèn)題:
- 需要遷移現(xiàn)在基于頭文件的類(lèi)庫(kù);
- 與不支持模塊的編譯器的互操作性;
- 工具需要理解模塊;
所以應(yīng)該使用引入模塊的過(guò)渡方案——直接從頭文件中構(gòu)建模塊。這么做有以下好處:
- 頭文件有利于互操作;
- 程序員不需要完全改變自己習(xí)慣的開(kāi)發(fā)模式;
模塊地圖(Module Map)
模塊地圖是模塊的關(guān)鍵,用來(lái)定位模塊相關(guān)(子)模塊,包含以下功能:
- 模塊定義命名(子)模塊
- 頭文件在(子)模塊中包含命名頭文件的內(nèi)容
保護(hù)傘頭文件(Umbrella Header)
- 保護(hù)傘頭文件會(huì)在其目錄下包含所有頭文件信息
- 使用通配符submodules (module *) 可以為每一個(gè)包含的頭文件創(chuàng)建一個(gè)子模塊:
- AST/Decl.h?->?ClangAST.Decl?
- AST/Expr.h?->?ClangAST.Expr?
模塊編譯過(guò)程:
編輯模塊文件過(guò)程:
從頭文件到模塊化
從頭文件編程轉(zhuǎn)換到使用模塊非常簡(jiǎn)單:
庫(kù)方面:合并復(fù)合定義的結(jié)構(gòu)、函數(shù)、宏,并且為頭文件導(dǎo)入依賴(lài),最后編寫(xiě)好模塊地圖;
開(kāi)發(fā)者方面只需要從“#include”過(guò)渡到“import”:
當(dāng)然,你也可以使用工具來(lái)自動(dòng)化重寫(xiě)代碼,非常簡(jiǎn)單。
工具
編輯性能
使用模塊能夠提升語(yǔ)法解析性能:
- 模塊化的頭文件只需要解析一次,之后放在緩存中,于是m×n --> m+n
- 所有基于源(source-based)工具都能帶來(lái)好處
- 自動(dòng)鏈接大大簡(jiǎn)化了庫(kù)的使用
- 自動(dòng)導(dǎo)入可以阻止“#include”帶來(lái)的可怕的調(diào)試結(jié)果
調(diào)試流
通過(guò)DWARF的雙程調(diào)試有損耗:
- 只能獲得“用過(guò)”類(lèi)型和函數(shù)
- 丟失了行內(nèi)函數(shù)、模板
另外調(diào)試過(guò)程還會(huì)出現(xiàn)信息冗余
那使用模塊的調(diào)試又會(huì)怎樣?
1.提高了構(gòu)建性能
- 編譯器發(fā)出的DWARF更少
- 鏈接器清除重復(fù)的DWARF也更少
2.提高了調(diào)試體驗(yàn)
- 調(diào)試器的ASF精度非常完美
- 調(diào)試器不需要尋找DWARF
總結(jié)
總而言之,C/C++使用模塊化非常有潛力:
- 編譯/構(gòu)建時(shí)間的縮短
- 修復(fù)各種預(yù)處理問(wèn)題
- 更好的工具體驗(yàn)
- 通過(guò)設(shè)計(jì),能夠平穩(wěn)地過(guò)渡
- Clang實(shí)現(xiàn)已經(jīng)在進(jìn)行了
這個(gè)Slide在Hacker News上引起了激烈討論,大部分網(wǎng)友還是贊成模塊化的方式:
baberman:對(duì)我來(lái)說(shuō)沒(méi)什么不便,而且還給出了過(guò)渡方案,可能會(huì)很適合某些C/C++項(xiàng)目。我們應(yīng)該對(duì)任何提升C/C++性能的想法持開(kāi)放態(tài)度。
greggman:預(yù)處理有什么不好嗎?如果我不想用預(yù)處理,我完全可以使用Objective-C等。現(xiàn)在的機(jī)器性能已經(jīng)夠強(qiáng)大了,import在編譯上的性能優(yōu)勢(shì)對(duì)我來(lái)說(shuō)沒(méi)有任何吸引力,我更喜歡C/C++的傳統(tǒng)方式。
msbarnett反駁greggman:我認(rèn)為這正是這份提議的精髓所在,你既可以保留使用#include,也可以從現(xiàn)在開(kāi)始就轉(zhuǎn)向import模塊的方式。
_djo_:這個(gè)想法會(huì)非常有前途!要說(shuō)缺點(diǎn)的話(huà)就是來(lái)得太遲了!
nkurz:我不同意“m個(gè)頭文件+n個(gè)源文件 --> m×n倍編譯性能”。只要使用“#ifndef _HEADER_H”就不會(huì)出現(xiàn)這個(gè)問(wèn)題,或者,為什么不使用#include_once <header.h>來(lái)解決呢?預(yù)處理很可怕嗎?預(yù)處理確實(shí)有一些問(wèn)題,但是卻是可以克服的。
SeoxyS:我不關(guān)心性能,現(xiàn)在性能已經(jīng)不是問(wèn)題了,我更關(guān)心怎樣給開(kāi)發(fā)者更少的負(fù)擔(dān)。
各位CSDN網(wǎng)友是怎樣看的呢?歡迎一起討論。
相關(guān)鏈接:Doug的Slide、Hacker News上的討論
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的为什么应该用模块取代C/C++中的头文件?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 国外较好的IT网站
- 下一篇: MinGW下编译ffmpeg静态库给Vi