[分享] Include What You Use

看板C_and_CPP作者 (pomelocandy)時間3年前 (2020/10/31 11:16), 3年前編輯推噓22(22059)
留言81則, 16人參與, 3年前最新討論串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, 3年前 , 1F
10/31 15:33, 1F

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

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

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

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

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

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

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

11/01 15:46, 3年前 , 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, 3年前 , 10F
發現人會疏漏的問題就是CI的工作了
11/01 19:19, 10F

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

11/04 12:53, 3年前 , 38F
Preprocessing->編譯->處理inline
11/04 12:53, 38F

11/04 12:58, 3年前 , 39F
補充一下 inline 有自己的scope 較穩定
11/04 12:58, 39F

11/05 16:04, 3年前 , 40F
阿展開以後compiler不用看過喔 那這個compiler太妙了
11/05 16:04, 40F

11/05 16:04, 3年前 , 41F
不用看就可以變出來bin 妙不可言
11/05 16:04, 41F

11/05 16:46, 3年前 , 42F
當然要看我又沒有說不用看
11/05 16:46, 42F

11/05 16:51, 3年前 , 43F
但不穩定是因為還要加類似do{}while(0)這種東西
11/05 16:51, 43F

11/05 16:52, 3年前 , 44F
*MACRO
11/05 16:52, 44F

11/05 18:55, 3年前 , 45F
我在思考這裡是怎麼談到 inline 的...
11/05 18:55, 45F

11/06 09:07, 3年前 , 46F
GCC能幫我列出沒被使用的函式,也許以後還能幫忙抓沒
11/06 09:07, 46F

11/06 09:09, 3年前 , 47F
被使用的標頭檔,不過如果你很在乎編譯時間長短的話
11/06 09:09, 47F

11/06 09:11, 3年前 , 48F
那還是要親手整理,把標頭檔藏好別到處引用
11/06 09:11, 48F

11/06 10:13, 3年前 , 49F
像static 就不會被剪貼進去啊
11/06 10:13, 49F

11/06 10:19, 3年前 , 50F
笑死..
11/06 10:19, 50F

11/06 10:37, 3年前 , 51F
...
11/06 10:37, 51F

11/06 12:41, 3年前 , 52F
inline 只是 inline it if you can ,又沒強制
11/06 12:41, 52F

11/06 12:51, 3年前 , 53F
如果 static不會剪貼,那 static inline 又是什麼
11/06 12:51, 53F

11/06 12:51, 3年前 , 54F
include 不就單純的剪貼嗎?還會管你是不是 static 喔?
11/06 12:51, 54F

11/06 12:53, 3年前 , 55F
語法分析可能都還沒開始,不可能管到語意去吧...
11/06 12:53, 55F

11/06 13:01, 3年前 , 56F
好喔最初應該會啦 但你也用不了
11/06 13:01, 56F

11/06 13:02, 3年前 , 57F
喔喔我懂你意思了
11/06 13:02, 57F

11/06 13:03, 3年前 , 58F
我是說在其他source檔定義static同名一起include heade
11/06 13:03, 58F

11/06 13:03, 3年前 , 59F
r的其他source抓不到那個被定義static的symbol
11/06 13:03, 59F

11/06 13:04, 3年前 , 60F
但如果你一開始就把static function 寫在header那當然
11/06 13:04, 60F

11/06 13:04, 3年前 , 61F
都可以用 但是你每個source 檔自己定義都是分開的
11/06 13:04, 61F

11/06 13:05, 3年前 , 62F
這樣沒錯吧?
11/06 13:05, 62F

11/06 13:11, 3年前 , 63F
text剪貼當然是預處理而已
11/06 13:11, 63F

11/06 13:15, 3年前 , 64F
呃... 不論某個 symbol 是不是 static、或它被從哪裡 inc
11/06 13:15, 64F

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

11/06 18:06, 3年前 , 81F
ucrxzero: 討論就是這樣, 不用太在意
11/06 18:06, 81F
文章代碼(AID): #1VdDSE7m (C_and_CPP)