|
非人磨墨墨磨人 |
學習 c++ 多年, 還是不熟悉 template, 這次專注在 variadic template 只是因為最近迷上 recursive 的思考邏輯, 很可能是因為這陣子寫了 simple c interpreter, parse ebnf 本身就是一種 recursive 的行為, 我體會到 recursive 的強大, 進而想學習他。而最近的 functional programming 思考方式密集地被傳道, 雖然有很多名詞, 但其最根本的思想也是 recursive。我覺得 recursive 是可以好好投資的一個技巧, 我願意花時間在上頭。
而 c++ 11 variadic template 是一個很類似的東西, 他把 recursive 思考方式帶到了編譯階段, 不過得靠《冥想》去想像該 recursive 是怎麼展開的, 這太難了, 我試圖以反組譯來具象化編譯器的神祕行為。
variadic template 有 function 和 class 兩種, 這邊提到的是 variadic template function。
g++ -std=c++14 -g -m32 -fno-stack-protector -g -g -c vt1.cpp
g++ -std=c++14 -g -m32 -fno-stack-protector -g -o vt1 vt1.o
執行結果:
descent@u64:cpp11$ ./vt1
type: PKc, val: ab
type: i, val: 0
type: i, val: 1
last type: i, val: 2
編譯器產生了以下四個函式:
183<f1(int)>: // 這個就是 vt1.cpp L9, 需要事先準備好給編譯器, 否則無法展開 variadic template
294<void f1<char const*, int, int, int>(char const*, int, int, int)>:
339<void f1<int, int, int>(int, int, int)>:
383 08048974 <void f1<int, int>(int, int)>:
然後以下面的方式呼叫他們,
f1("ab", 0, 1, 2) call f1(0, 1, 2) call f1(1, 2) call f1(2)
f1("ab", 0, 1, 2); // 只印出第一個引數 "ab"
| - f1(0, 1, 2); // 只印出第一個引數 0
| - f1(1, 2); // 只印出第一個引數 1
| - f1(2); // 只印出第一個引數 0
每次的函式呼叫都只會處理第一個實際參數, 一直下去直到把所有引數處理完璧。
list 1. 是反組譯的結果, 我加入了一些註解, 有興趣的朋友可以看看, 不過其實知道 c++ 編譯器怎麼展開 variadic template function 已經足夠, 不一定要看 list 1. 的反組譯。
辛苦看懂了反組譯後的程式碼, 再來要問, 這應該要怎麼用呢? 我應該投資時間在這種技巧上嗎?
和 move semantics 不同, 這次我沒有答案, 我不知道如何在我的程式碼上用上這種技巧, 我也不想為了用這種技巧而用, 我只是想寫出容易理解的程式, 並不想耍什麼花招。
而且在看過反組譯之後應該會發現程式碼大幅度的增加, 隨著引數愈多, 產生出的函式愈多, 程式碼會變得龐大。c++ 總是強調其效率, 卻從不提這是以程式碼變大的代價換來的。
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。