pprint 源碼閱讀
上一次一起學(xué)習(xí)完 TinyFormat
的源碼后我們收獲很多,那這一次我們同樣來看一個用于格式化輸出的庫 pprint
,也同樣是一個 ?header only
的庫。
首先我們同樣看一下 pprint
的基礎(chǔ)用法(來自 README.md
):
可以看到用法上和 TinyFormat
有一定區(qū)別,使用上更貼近 sd::cout<<
而不是 ::printf
。
那么就讓我們正式開始吧,首先看一下 PrettyPrinter
的構(gòu)造方式:
可以看到 PrettyPrinter
接收一個 ostream&
類型的參數(shù)用于指明輸出的目的流,默認是 std::cout
,另外還設(shè)置了行結(jié)尾 line_terminator_
, indent_
縮進,以及 quotes_
是否在輸出字符串是加上 "
包裹, compact_
這里指的應(yīng)該是緊湊輸出。
緊接著來分析一下 PrettyPrinter::print
函數(shù)的實現(xiàn):
可以看到 print
做了三個重載,第一個是直接調(diào)用 print_internal
。第二個接收 std::initializer_list<T>
類型的參數(shù)也同樣調(diào)用 print_internal
,這里為什么要單獨調(diào)一下原因我們暫時不知道,可以后面再看一下,至于 std::initializer_list
如果大家不清楚的話這里簡單說明一下,用大括號 {}
做初始化時實際上就是通過這個類型實現(xiàn)的。
接著是一個接受變長模板參數(shù)的重載,首先打印第一個 value
,接著打印分隔符空格,接著遞歸調(diào)用 print
打印剩下的參數(shù)。
這里實際上我們知道 print
的實現(xiàn)可以通過折疊表達式優(yōu)化一下:
怎么樣,是不是看上去好多了,唯一的問題就是對于 std::initializer_list
類型的參數(shù)我們后續(xù)可能還需要特殊處理,那現(xiàn)在先來看一下 print_internal
的實現(xiàn):
emmm, 我也是驚了,上面我列出的只是一小部分,作者驚人的為每一個內(nèi)置類型做了單獨適配,尤其后面這一大堆類型判斷讓我屬實有點繃不住啊,結(jié)合后面的一個給 vector
的一版重載:
還是先簡單分析一下這里的用法:
這里 enable_if
的作用是判斷 Container
的類型是否為 std::vector
,如果不是那么禁用模板,如果是那么通過 ::type
拓展模板實例的返回類型為 void
,當(dāng)然 enable_if
只是判斷表達式結(jié)果為 true_type
還是 false_type
,實際判斷是由 is_specialization
實現(xiàn)的:
這是一個比較常見但是經(jīng)典的實現(xiàn),由 @Databyte 提出,這里通過特化使得忽略模板參數(shù)后類型一致的兩個參數(shù)可以匹配到特化版本,其 ::type
為 std::true_type
,否則匹配到非特化版本,其 ::type
為 std::false_type
這里舉個簡單的例子 is_specialization<std::vector<int>, std::vector>
,那么此時我們就可以看到 Ref
的類型為 std::vector
是可以匹配的上的;而對于 is_specialization<std::list<int>, std::vector>
來說就沒法匹配特化版本,只能匹配非特化版本此時 Test
為 std::list<int>
, Ref
為 std::vector
。
回到 pprint
,那實際上像這樣去實現(xiàn) print_internal
也就是對每一個可能的情況進行處理是非常不推薦的,更不用說這里用了 enable_if
的方式去禁用模板函數(shù),這樣一來一旦 STL 中新增了類型那么除了新增特定的處理外還需要改動上面的 print_internal
聲明否則就會導(dǎo)致編譯器找到多個函數(shù),至少也應(yīng)該用特化去實現(xiàn)。
后續(xù)的代碼那基本也都是一個套路,我們就不繼續(xù)往下細看了。
下次再會!