TinyFormat 源碼閱讀(三)
回顧一下昨天的部分我們讀到了 formatImpl
的實(shí)現(xiàn), printFormatStringLiteral
函數(shù)找到字符串格式化字符前的部分并寫入流,返回剩余的部分。
接著我們看一下 streamStateFromFormat
的實(shí)現(xiàn):
我們可以看到函數(shù)的主要功能就是解析諸如 %04d
這樣的格式化字符并為 ostream
設(shè)置輸出格式,這里遵循的是 C99
標(biāo)準(zhǔn)中的規(guī)范:
具體的就不多展開了,有興趣可以自己了解一下,這里我們專注語(yǔ)言上的一些風(fēng)格和技巧。
回到 formatImpl
函數(shù)中,一部分的流格式設(shè)置和 arg
填充在 streamStateFromFormat
中完成了,而另一部分則通過 arg.format
完成:
這里可以看到通過模板和宏配合聲明并定義了四個(gè) formatValue
函數(shù):
因此對(duì)于 char/signed char/unsigned char
這三種類型優(yōu)先調(diào)用非模板版本也就是直接向流中寫入數(shù)據(jù),而對(duì)于其他的版本則調(diào)用模板版本做進(jìn)一步的處理。
在模板版本的 formatValue
中對(duì)傳入的 value
分四種情況進(jìn)行處理:
能隱式轉(zhuǎn)化成
char
并且格式化字符也確實(shí)是c
的,通過formatValueAsType::invoke
調(diào)用static_cast
轉(zhuǎn)成char
類型并寫入流能隱式轉(zhuǎn)為
const void*
且格式化字符為p
的,同樣轉(zhuǎn)化后寫入流需要截?cái)嗟?,只寫入截?cái)嗲安糠值搅髦?/p>
其他情況的直接通過
<< vaule
寫入,依賴類型自身支持
以上過程又被 formatImpl
在一個(gè) while
循環(huán)中重復(fù),就這樣完成了字符串格式化的整個(gè)過程并由 oss.str()
返回。
到這里的話 TinyFormat
庫(kù)的基本邏輯我們都說完了,接下來說說關(guān)于變長(zhǎng)參數(shù)部分的拓展,之前在第一節(jié)分析 tinyformat_test.cpp
的時(shí)候我們分析了 TINYFORMAT_ARGTYPES
和 TINYFORMAT_FOREACH_ARGNUM
?等宏的實(shí)現(xiàn),以及如何依靠這些宏完成對(duì)變長(zhǎng)參數(shù)的支持,那么在 c++11
加入了對(duì)變長(zhǎng)參數(shù)的支持后,我們又該怎樣實(shí)現(xiàn)這些函數(shù)呢。
以 FormatListN
的構(gòu)造函數(shù)為例, c++98
版本的實(shí)現(xiàn)如下 :
簡(jiǎn)單回顧一下,這里使用一個(gè)空的 init
函數(shù)作為中止函數(shù),接著借助宏實(shí)例化出最多支持 16 個(gè)參數(shù)的構(gòu)造函數(shù)和 init
函數(shù),嘗試初始化時(shí)由對(duì)應(yīng)參數(shù)數(shù)量版本的構(gòu)造函數(shù)從下標(biāo) i=0
開始調(diào)用 init
函數(shù),逐次遞增下標(biāo)并去除已賦值的參數(shù)直到所有參數(shù)全部初始化完成。
這是 c++11
支持變長(zhǎng)參數(shù)的版本:
可以看到這里通過 template<typename ... Args>
聲明 FormatListN
的構(gòu)造函數(shù)是一個(gè)模板函數(shù),并且接受變長(zhǎng)模板參數(shù)。同時(shí)很精巧的令 m_formatterStore
是一個(gè) FormatArg
類型的數(shù)組,且其構(gòu)造函數(shù)可以接收任意類型 T
,還通過 FormatListN
的模板參數(shù) template <size_t N>
來保證傳入的模板參數(shù)個(gè)數(shù)和數(shù)組長(zhǎng)度一直,因此可以直接通過 m_formatterStore { FormatArg(args)... }
把模板參數(shù)一次性復(fù)制給數(shù)組,非常有趣的一種寫法,否則的話一般來說即使是變長(zhǎng)模板參數(shù)也需要通過遞歸的方式完成賦值( c++17
可以用括號(hào)展開, c++11
用初始化列表魔法)。
特別注意由于這里使用內(nèi)置數(shù)組,因此我們要通過偏特化的方式對(duì)長(zhǎng)度為零的 FormatListN
進(jìn)行處理:
最后再來看一下通過 makeFormatList
構(gòu)造參數(shù)的部分,同樣是 c++11
實(shí)現(xiàn)的版本:
可以看到這里借助了 sizeof...(Args)
拿到了 FormatListN
所需要的模板參數(shù) N
,并傳入所有參數(shù)用以構(gòu)建和初始化參數(shù)列表。
好了,那么對(duì) TinyFormat
庫(kù)的整個(gè)閱讀到這基本上就結(jié)束了,從這個(gè)簡(jiǎn)小但功能強(qiáng)大的庫(kù)中我們收獲了一些常見的宏語(yǔ)法、對(duì)變長(zhǎng)模板參數(shù)的使用以及一個(gè)非常精妙的利用變長(zhǎng)模板參數(shù)向數(shù)組復(fù)制的小技巧。
下次再會(huì)!