C++標(biāo)準(zhǔn)庫STL中的type_traits文件中,已經(jīng)有了比較全面的C++ trait組件,可以用來對(duì)代碼做各種靜態(tài)反射。
TLP庫中補(bǔ)充了如下幾個(gè)有用的trait工具,這些trait在后面介紹的TLP的sample代碼中會(huì)用到。
__is_convertible(T, U):用于判斷類型T是否可以默認(rèn)轉(zhuǎn)型為U類型;__is_both_convertible(T, U):用于判斷類型T和U之間是否可以互相轉(zhuǎn)型;__is_base_of(T, U):用于判斷類型T是否是類型U的父類;__is_built_in(T):用于判斷類型T是否為C++內(nèi)置類型;__lambda_return(Lambda):針對(duì)一個(gè)Lambda表達(dá)式類型,計(jì)算其返回值類型;__lambda_paras(Lambda):針對(duì)一個(gè)Lambda表達(dá)式類型,計(jì)算其參數(shù)類型;將所有參數(shù)類型放在一個(gè)TypeList中返回;__lambda_para(Lambda,Index):針對(duì)一個(gè)Lambda表達(dá)式類型,返回其Index位置的參數(shù)的類型。如果沒有參數(shù)返回NullType;
這些trait工具的實(shí)現(xiàn)大多在前面都已經(jīng)介紹過,除過最后三個(gè)關(guān)于lambda的。
C++11引入了lambda特性,這是一個(gè)非常有用的特性,尤其對(duì)于編寫框架?,F(xiàn)實(shí)中我們經(jīng)常把變化的算法交給客戶自定義,然后通過語言提供的技術(shù)手段再注入給框架。相比傳統(tǒng)使用虛接口做注入,支持lambda會(huì)讓客戶的代碼更加簡(jiǎn)潔和緊湊。在框架中,我們可能會(huì)要提取用戶注入的lambda表達(dá)式的返回值類型或者參數(shù)類型。TLP中的__lambda_return()、__lambda_paras()和__lambda_para()就是幫助代碼完成這些事情的。它們的實(shí)現(xiàn)如下:
// "tlp/traits/LambdaTraits.h"
template<typename T>
struct LambdaTraits
: LambdaTraits<decltype(&T::operator())>
{
};
template<typename C, typename R, typename ... Args>
struct LambdaTraits<R (C::*)(Args...) const>
{
using ReturnType = R;
using ParaTypes = typename TypeList<Args...>::Result;
};
#define __lambda_return(...) typename LambdaTraits<__VA_ARGS__>::ReturnType
#define __lambda_paras(...) typename LambdaTraits<__VA_ARGS__>::ParaTypes
#define __lambda_para(lambda, index) __at(__lambda_paras(lambda), __int(index))
如上,我們主要靠模板特化的模式匹配特性來萃取出我們想要的類型信息的:template<typename C, typename R, typename ... Args> struct LambdaTraits<R (C::*)(Args...) const>。我們把返回值類型保存在ReturnType,而把所有的參數(shù)類型保存在一個(gè)TypeList中:using ParaTypes = typename TypeList<Args...>::Result。
可以如下測(cè)試這些lambda traits:
TEST("calculate the return type and parameter types of a lambda")
{
template<typename T>
void testLambdaTraits(const T&)
{
ASSERT_EQ(__lambda_return(T), int);
ASSERT_EQ(__lambda_paras(T), __type_list(const int*, char));
ASSERT_EQ(__lambda_para(T, 0), const int*);
ASSERT_EQ(__lambda_para(T, 1), char);
ASSERT_EQ(__lambda_para(T, 2), __null());
}
void run()
{
testLambdaTraits([](const int* x, char y){ return *x + y; });
}
};
上面我們對(duì)lambda表達(dá)式[](const int* x, char y){ return *x + y; }進(jìn)行了萃取,判斷其返回類型是int,參數(shù)類型分別是const int *和char。
上面的測(cè)試中,之所以要定義testLambdaTraits函數(shù),主要是為了從lambda表達(dá)式對(duì)象中提取出它的類型,然后再傳給__lambda_return()等。這種通過函數(shù)模板來對(duì)具體對(duì)象或變量進(jìn)行推導(dǎo)以獲取其類型的手段,在C++11之前是一種常用技巧。由于C++11引入了decltype關(guān)鍵字,所以以后都不用再這么繞了!我們重新實(shí)現(xiàn)測(cè)試用例如下:
TEST("calculate the return type and parameter types of a lambda")
{
void run()
{
auto f = [](const int* x, char y){ return *x + y; };
using Lambda = decltype(f);
ASSERT_EQ(__lambda_return(Lambda), int);
ASSERT_EQ(__lambda_paras(Lambda), __type_list(const int*, char));
ASSERT_EQ(__lambda_para(Lambda, 0), const int*);
ASSERT_EQ(__lambda_para(Lambda, 1), char);
ASSERT_EQ(__lambda_para(Lambda, 2), __null());
}
};
之所以還需要run()方法,是因?yàn)門EST本質(zhì)上是一個(gè)類,而auto f不能是定義為類的成員的,但是可以定義到函數(shù)里面。
關(guān)于TLP中的trait就介紹到這里,更多的關(guān)于trait的應(yīng)用在后面的還會(huì)專門介紹。