[分享] Include What You Use

看板C_and_CPP (C/C++)作者 (pomelocandy)時間4年前 (2020/10/31 11:16), 4年前編輯推噓24(24079)
留言103則, 17人參與, 最新討論串1/1
最近用了 include-what-you-use 這個工具,分享一下 https://yodalee.me/2020/10/2020_iwyu/ 大家都知道,程式不是寫完就算了,是會長大跟更新 這時候 include 就會慢慢過時,可能本來需要的 include 現在不需要了 但通常在改程式碼時不會意識到這點 如果參考 Google 的 cpp coding guide,會看到 Include What You Use 這條準則: If a source or header file refers to a symbol defined elsewhere, the file should directly include a header file which properly intends to provide a declaration or definition of that symbol. It should not include header files for any other reason. 原始碼檔案和標頭檔所需的符號,都應該引入適當的標頭檔來提供宣告或定義; 不能因為其他理由而引入標頭檔。 Do not rely on transitive inclusions. This allows people to remove no-longer-needed #include statements from their headers without breaking clients. This also applies to related headers - foo.cc should include bar.h if it uses a symbol from it even if foo.h includes bar.h. 不可依賴過渡引入。開發者可以隨時移除不需要的引入,又不會破壞客戶端的相依性; 這也適用於相關的標頭檔:即使 foo.h 已經引入 bar.h,foo.cc 還是要引入 bar.h 。 ---- 想當然爾,動輒幾百幾千個原始碼檔和標頭檔 怎麼可能一個一個去分析 include 有沒有寫對? 這就是這篇文章要介紹的工具了,名稱也非常直覺 就叫做 include what you use 簡稱 iwyu。 https://include-what-you-use.org/ iwyu 是專門開發來對付這個問題的 它執行起來就像一個 gcc,會去分析原始碼中所需的符號,並解析 include 檔案 確定符號都是直接引入而不是過渡引入;同時還會幫忙產生修正檔 一次完成引入的修正,簡直是 include 的殺手級工具。 ---- iwyu 如果照文件的建議,是儘量搭配專案本來就有的 Makefile 或 CMake 使用 直接把 CC 或 CXX 代換成 include-what-you-use 加上 -k 編譯 iwyu 就會輸出編譯檔案 include 的修正檔了 (加上 -k 是因為 iwyu 回報 include 錯誤會導致編譯停止); 使用 iwyu 時一定要讓參數和真正編譯時儘量相同 才能分析編譯時使用的標頭檔和 -D 引入的巨集。 也有人會這樣跑: find . -name "*.h" | xargs include-what-you-use <flags> find . -name "*.c" | xargs include-what-you-use <flags> 但要注意,直接對著 .h 使用 iwyu 可能會有問題 如果 .h 是公開有人使用的話,套用修正可能會把這個標頭拆散 要使用者引入其他的標頭檔,這會破壞使用者的相依性, 特別是在大公司裡面其他你管不到的專案可能會用你管的函式庫,此時請小心使用。 ---- 依照 iwyu 文件的建議,使用 iwyu 所附的 fix_includes.py 套用修正: make -k 2> iwyu.out python fix_includes.py < iwyu.out 如此一來就完成 include 的修正。 雖然說我用完之後還是會遇到一些問題啦,像是出現這樣的 include #include<data.h> #include"data.h" 或是還是有些原始碼檔案缺了一些符號必須自己手動補上 include 但整體來說已經比自己手動修正快上不少了 -- ______ |\ / \ | \ / ● ● \ |__\ / ______ \ | /   \__/   \___| /______________\ | -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 220.134.248.249 (臺灣) ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1604114190.A.1F0.html

10/31 15:33, 4年前 , 1F
10/31 15:33, 1F

10/31 16:06, 4年前 , 2F
10/31 16:06, 2F

11/01 15:09, 4年前 , 3F
11/01 15:09, 3F

11/01 15:46, 4年前 , 4F
從軟體架構的品質屬性 (Quality Attributes) 來看,
11/01 15:46, 4F

11/01 15:46, 4年前 , 5F
如果你改了扣, 卻不知道應該要引入哪些標頭檔, 不會
11/01 15:46, 5F

11/01 15:46, 4年前 , 6F
很雷嗎? coding standard 只能告訴你結果, 為了確保
11/01 15:46, 6F

11/01 15:46, 4年前 , 7F
這個結果, 其實人員的教育/訓練才是最重要的; 而不是
11/01 15:46, 7F

11/01 15:46, 4年前 , 8F
透過工具來把訓練不足的問題掩蓋下來, 這也會衍生其
11/01 15:46, 8F

11/01 15:46, 4年前 , 9F
他問題
11/01 15:46, 9F
問題不是這樣的,假設你今天刪了幾行 code,裡面有一行用到某個資料結構 像是 struct smartdata 好了,而且這是最後一個用到 smartdata 的地方 理論上,應該要去 include 的地方,把 #include<smartdata.h> 刪掉 問題是有多少人會注意到這件事?就連 code review 都不一定會注意到了 又或者專案把 a.h 拆分成 a1.h a2.h 然後 a.h 裡面 include a1.h/a2.h 其他專案會記得把所有 a.h 換成 a1.h 或 a2.h 嗎? coding standard 是目標,但無論多少教育/訓練,在實行上就是會有疏失 "因為人就是會犯錯,所以在鉛筆的後面會有橡皮擦" 工具是用來輔助人,抓出人犯錯的地方,而不是說人就不用教育/訓練 ※ 編輯: lc85301 (220.134.248.249 臺灣), 11/01/2020 17:16:48

11/01 19:19, 4年前 , 10F
發現人會疏漏的問題就是CI的工作了
11/01 19:19, 10F

11/01 19:23, 4年前 , 11F
找錯誤是 testing 在做的事情
11/01 19:23, 11F

11/01 19:29, 4年前 , 12F
@@ 找違反coding standard也算testing喔
11/01 19:29, 12F

11/01 19:53, 4年前 , 13F
所以說這個工具就是要丟給 CI 去跑的呀
11/01 19:53, 13F

11/01 20:06, 4年前 , 14F
我的意思是 iwyu 應該單純作為 testing/diagnostic t
11/01 20:06, 14F

11/01 20:06, 4年前 , 15F
ool, 就像 clang-tidy等, 但是找出錯誤時應該是由人
11/01 20:06, 15F

11/01 20:06, 4年前 , 16F
類來修正, 並且要有明確的回報機制. fix_include 不
11/01 20:06, 16F

11/01 20:06, 4年前 , 17F
應該放進開發流程, 因為我們無法從最後的程式碼看出
11/01 20:06, 17F

11/01 20:06, 4年前 , 18F
它和人員素質之間的關係
11/01 20:06, 18F

11/01 20:34, 4年前 , 19F
可以接受
11/01 20:34, 19F

11/01 20:34, 4年前 , 20F
我的使用結果,iwyu 在解完 include 之後還是會有錯
11/01 20:34, 20F

11/01 23:14, 4年前 , 21F
程式語言都不要 typecheck 好了,寫 type safe 的程式是
11/01 23:14, 21F

11/01 23:14, 4年前 , 22F
人類的工作,不該由機器來做,typechecking 是測試在做
11/01 23:14, 22F

11/01 23:14, 4年前 , 23F
的事
11/01 23:14, 23F

11/01 23:15, 4年前 , 24F
人員的教育訓練才是最重要的
11/01 23:15, 24F

11/02 10:03, 4年前 , 25F
感謝分享
11/02 10:03, 25F

11/02 11:13, 4年前 , 26F
同意推文 類似變數宣告了沒用會跳warning這樣我比較喜歡
11/02 11:13, 26F

11/04 10:09, 4年前 , 27F
所以過度引入會有什麼副作用嗎? compile過慢嗎?
11/04 10:09, 27F

11/04 11:26, 4年前 , 28F
compile三級跳的慢 因為這是連鎖反應
11/04 11:26, 28F

11/04 11:26, 4年前 , 29F
另外雖然多半肇因於設計錯誤,但是名稱空間衝突機會也
11/04 11:26, 29F

11/04 11:27, 4年前 , 30F
會變大,當你用兩個3rd party的.h裡面的include還互相
11/04 11:27, 30F

11/04 11:27, 4年前 , 31F
衝突的時候你真的會欲哭無淚 要改都沒辦法改
11/04 11:27, 31F

11/04 11:33, 4年前 , 32F
簡單觀念: #include 是編譯器幫你剪貼標頭檔在引入處
11/04 11:33, 32F

11/04 11:34, 4年前 , 33F
所以 #include 越多最後編譯時要看的東西就越多
11/04 11:34, 33F

11/04 11:36, 4年前 , 34F
那編譯過程中編譯器需要記錄搜尋判斷的東西就越多
11/04 11:36, 34F

11/04 12:48, 4年前 , 35F
所有的MACRO 包括include 不是都是展開的概念嗎
11/04 12:48, 35F

11/04 12:50, 4年前 , 36F
跟inline 的差別在於inline是編譯器做的是真的函式
11/04 12:50, 36F

11/04 12:50, 4年前 , 37F
而preprocessing是text的代換而已
11/04 12:50, 37F

11/04 12:53, 4年前 , 38F
Preprocessing->編譯->處理inline
11/04 12:53, 38F
還有 25 則推文
11/06 13:15, 4年前 , 64F
呃... 不論某個 symbol 是不是 static、或它被從哪裡 inc
11/06 13:15, 64F

11/06 13:15, 4年前 , 65F
lude 到哪裡、或 scope 是什麼 etc...,它被 include 進
11/06 13:15, 65F

11/06 13:15, 4年前 , 66F
來就是會增加編譯器的工作,就可能會導致編譯速度變慢,
11/06 13:15, 66F

11/06 13:15, 4年前 , 67F
就這樣而已...
11/06 13:15, 67F

11/06 13:20, 4年前 , 68F
我用詞錯誤啦 我講到編譯的地方去了
11/06 13:20, 68F

11/06 13:20, 4年前 , 69F
不要再鞭了
11/06 13:20, 69F

11/06 13:21, 4年前 , 70F
更正 講到連結的地方去了
11/06 13:21, 70F

11/06 13:22, 4年前 , 71F
就跟你說從結果論來學習是錯的方法, 買一本書好好把
11/06 13:22, 71F

11/06 13:22, 4年前 , 72F
它看完, 沒看完別來誤導其他人
11/06 13:22, 72F

11/06 13:24, 4年前 , 73F
沒有要鞭,看了就忍不住想講
11/06 13:24, 73F

11/06 13:24, 4年前 , 74F
orz
11/06 13:24, 74F

11/06 13:28, 4年前 , 75F
沒錯,這裡傳道授業的地方講話不能太籠統可能會讓人搞
11/06 13:28, 75F

11/06 13:28, 4年前 , 76F
混 我剛剛剪貼是講最後的連結的地方不會被連結
11/06 13:28, 76F

11/06 13:28, 4年前 , 77F
而不是引用的時候不會被剪貼
11/06 13:28, 77F

11/06 13:29, 4年前 , 78F
我略過太多而且用詞不精
11/06 13:29, 78F

11/06 15:00, 4年前 , 79F
大家我先去看書明年見
11/06 15:00, 79F

11/06 16:20, 4年前 , 80F
看講話的藝術
11/06 16:20, 80F

11/06 18:06, 4年前 , 81F
ucrxzero: 討論就是這樣, 不用太在意
11/06 18:06, 81F

11/10 07:42, , 82F
我能給的建議是,不要急著發表意見
11/10 07:42, 82F

11/10 07:42, , 83F
先把想說的在腦袋裡面順一次 想想要不要發表 再寫
11/10 07:42, 83F

11/10 07:43, , 84F
不過這篇居然沒有人把pImpl拖出來 真令人意外XD
11/10 07:43, 84F

11/10 08:30, , 85F
(離題) 這其實是推文成章的壞處之一: 發完出去了就沒得改
11/10 08:30, 85F

11/10 08:33, , 86F
(再拉回來) 這麼一提我才發現 pImpl 好像和這段 guide 之間
11/10 08:33, 86F

11/10 08:33, , 87F
有些微妙的互動...
11/10 08:33, 87F

11/10 08:53, , 88F
Pimpl 在 header 應該沒有用到 impl class 底下的 symb
11/10 08:53, 88F

11/10 08:53, , 89F
ol,應該還好?
11/10 08:53, 89F

11/10 14:51, , 90F
基本上這條guide就是給沒用pimpl的code補救的方案之一
11/10 14:51, 90F

11/10 14:52, , 91F
你真的寫成pimpl 這東西應該沒辦法再優化了....
11/10 14:52, 91F

11/10 14:53, , 92F
就全部被凝縮成一個opaque pointer是還能怎麼最佳化XD
11/10 14:53, 92F

11/10 14:53, , 93F
應該說 還是能拿掉用不到的header啦 但效果不明顯了
11/10 14:53, 93F

11/10 20:16, , 94F
我是覺得 pimpl 和這條 guide 兩者是獨立的
11/10 20:16, 94F

11/10 22:39, , 95F
是沒錯,不過要是使用pimpl的話,絕大多數的redundent
11/10 22:39, 95F

11/10 22:39, , 96F
header都會在.cpp而不在.h 這條guide最佳化就很有限了
11/10 22:39, 96F

11/10 23:42, , 97F
這條 guide 應該是要人們避免 transitive include,讓
11/10 23:42, 97F

11/10 23:42, , 98F
人可以安心的拿掉不用的 header。編譯速度有機會變快應
11/10 23:42, 98F

11/10 23:42, , 99F
該算是屬於附加的,並不是本來的目的。
11/10 23:42, 99F

11/10 23:42, , 100F
而使用 pimpl 的其中一個目的是可以讓人修改 impl 時不
11/10 23:42, 100F

11/10 23:42, , 101F
用改到 header,可以避免重新編譯其他 .cpp。
11/10 23:42, 101F

11/10 23:42, , 102F
就這樣我覺得 pimpl 跟這條 guide 不衝突也不相關。
11/10 23:42, 102F

12/07 20:54, , 103F
推分享
12/07 20:54, 103F
文章代碼(AID): #1VdDSE7m (C_and_CPP)
文章代碼(AID): #1VdDSE7m (C_and_CPP)