類型萃?。╰rait)的概念我們前面有介紹過??梢詫rait看做是一種靜態(tài)反射技術(shù),通過trait我們可以自動提取出想要的代碼元信息,避免讓客戶代碼顯示去提供這些信息,從而使得客戶代碼更加的簡潔。
在dates中,客戶可使用FakeSystem定義一個fake系統(tǒng),與SUT交互。FakeSystem擁有send和recv接口,分別向SUT發(fā)送消息,以及從SUT接收消息。send的入?yún)⑹且粋€原型為void(Msg&)的lambda函數(shù),用于描述如何構(gòu)造Msg消息。
visitor.send([this](AccessReq& req)
{
req.capability = CAPABILITY;
});
由于一個FakeSystem可以發(fā)送多種msg,所以send接口無法確定lambda的具體類型,因此send的參數(shù)只能定義為泛型參數(shù),它的原型為:
template<typename BUILDER>
void send(const BUILDER& builder);
這樣send就可以傳入各種構(gòu)造不同消息類型的lambda了,而且還可以調(diào)用原型一致的普通函數(shù)或者仿函數(shù),客戶使用起來非常簡潔。
現(xiàn)在我們來實現(xiàn)send。send中需要創(chuàng)建一個消息,然后交給builder去構(gòu)造。如下偽代碼:
template<typename BUILDER>
void send(const BUILDER& builder)
{
Msg msg; // 這里Msg到底應(yīng)該是什么類型?
builder(msg);
// ...
}
上面代碼的問題在于,我們不知道Msg的類型!Msg的類型是由客戶傳入的不同builder決定的,例如visitor.send([this](AccessReq& req){...})中,Msg是AccessReq。換句話說,我們需要從傳入的lambda表達(dá)式的類型中獲取Msg的類型。
模板元編程可以幫助我們解決這個問題。還記得我們前面介紹的TLP庫中trait工具中的__lambda_para()嗎?于是代碼修改如下:
template<typename BUILDER>
void send(const BUILDER& builder)
{
using Msg = __lambda_para(BUILDER, 0); // 獲取BUILDER的參數(shù)列表中的第一個參數(shù)類型
Msg msg;
builder(msg);
// ...
}
如上我們通過類型萃取,從客戶傳入的函數(shù)類型中取出了參數(shù)的類型,使得框架的接口保持了簡潔和靈活性。