C++標(biāo)準(zhǔn)庫(kù)STL中的type_traits
文件中,已經(jīng)有了比較全面的C++ trait組件,可以用來(lái)對(duì)代碼做各種靜態(tài)反射。
TLP庫(kù)中補(bǔ)充了如下幾個(gè)有用的trait工具,這些trait在后面介紹的TLP的sample代碼中會(huì)用到。
__is_convertible(T, U)
:用于判斷類(lèi)型T是否可以默認(rèn)轉(zhuǎn)型為U類(lèi)型;__is_both_convertible(T, U)
:用于判斷類(lèi)型T和U之間是否可以互相轉(zhuǎn)型;__is_base_of(T, U)
:用于判斷類(lèi)型T是否是類(lèi)型U的父類(lèi);__is_built_in(T)
:用于判斷類(lèi)型T是否為C++內(nèi)置類(lèi)型;__lambda_return(Lambda)
:針對(duì)一個(gè)Lambda表達(dá)式類(lèi)型,計(jì)算其返回值類(lèi)型;__lambda_paras(Lambda)
:針對(duì)一個(gè)Lambda表達(dá)式類(lèi)型,計(jì)算其參數(shù)類(lèi)型;將所有參數(shù)類(lèi)型放在一個(gè)TypeList中返回;__lambda_para(Lambda,Index)
:針對(duì)一個(gè)Lambda表達(dá)式類(lèi)型,返回其Index位置的參數(shù)的類(lèi)型。如果沒(méi)有參數(shù)返回NullType;
這些trait工具的實(shí)現(xiàn)大多在前面都已經(jīng)介紹過(guò),除過(guò)最后三個(gè)關(guān)于lambda的。
C++11引入了lambda特性,這是一個(gè)非常有用的特性,尤其對(duì)于編寫(xiě)框架。現(xiàn)實(shí)中我們經(jīng)常把變化的算法交給客戶(hù)自定義,然后通過(guò)語(yǔ)言提供的技術(shù)手段再注入給框架。相比傳統(tǒng)使用虛接口做注入,支持lambda會(huì)讓客戶(hù)的代碼更加簡(jiǎn)潔和緊湊。在框架中,我們可能會(huì)要提取用戶(hù)注入的lambda表達(dá)式的返回值類(lèi)型或者參數(shù)類(lèi)型。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))
如上,我們主要靠模板特化的模式匹配特性來(lái)萃取出我們想要的類(lèi)型信息的:template<typename C, typename R, typename ... Args> struct LambdaTraits<R (C::*)(Args...) const>
。我們把返回值類(lèi)型保存在ReturnType
,而把所有的參數(shù)類(lèi)型保存在一個(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)行了萃取,判斷其返回類(lèi)型是int
,參數(shù)類(lèi)型分別是const int *
和char
。
上面的測(cè)試中,之所以要定義testLambdaTraits
函數(shù),主要是為了從lambda表達(dá)式對(duì)象中提取出它的類(lèi)型,然后再傳給__lambda_return()
等。這種通過(guò)函數(shù)模板來(lái)對(duì)具體對(duì)象或變量進(jìn)行推導(dǎo)以獲取其類(lèi)型的手段,在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)門(mén)EST本質(zhì)上是一個(gè)類(lèi),而auto f
不能是定義為類(lèi)的成員的,但是可以定義到函數(shù)里面。
關(guān)于TLP中的trait就介紹到這里,更多的關(guān)于trait的應(yīng)用在后面的還會(huì)專(zhuān)門(mén)介紹。