原本打算先和大家說(shuō)怎么在C++里面操作的lua的,畢竟這個(gè)Frame的主要功能便是靠lua實(shí)現(xiàn)的,但是想想可能有些朋友對(duì)lua不甚了解所以在說(shuō)怎么和lua通信之前那么大家可以先了解lua是個(gè)什么鬼,哦,對(duì)了,我用的是5.3版本的,雖然這個(gè)版本沒什么第三方庫(kù),但是現(xiàn)在很多常用的東西我自己已經(jīng)添加好,所以順手了也就無(wú)所謂了。
為伊犁等地區(qū)用戶提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及伊犁網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、伊犁網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!
那么今天我們從什么地方入手呢?嗯,好吧,就從“屬性”這玩意而已開始吧,屬性這個(gè)東西可能不是大家想想的屬性,我取名字這事已經(jīng)被同事吐槽了不下10次了,總覺得我名字莫名其妙,好吧,不說(shuō)這些,先來(lái)認(rèn)識(shí)下我所謂的屬性到底是個(gè)什么鬼吧。
假設(shè)對(duì)象A表示一個(gè)矩形,那么他將有長(zhǎng)和寬的概念,另一個(gè)對(duì)象B表示一個(gè)橢圓,當(dāng)對(duì)象A的尺寸發(fā)生變化時(shí)而對(duì)象B能夠知道A的尺寸發(fā)生變化而做出相應(yīng)的改變,這就是我所謂的屬性的概念,長(zhǎng)和寬是矩形的屬性,屬性改變將會(huì)通知關(guān)心他屬性的對(duì)象所作相應(yīng)的動(dòng)作,現(xiàn)在我用代碼模擬這種情況出來(lái):
//===================================
class A{
public:
void SetValue(const MString& value){
if(mValue == value)
return;
mValue = value;
}
private:
MString mValue;
};
class B{
public:
void setValue(const MString& value){
if(mValue == value)
return;
mValue = value;
}
private:
MString mValue;
};
void testFun(const MString& value){
ErrorBox("testFun called")
}
int main(){
A objA;
B objB;
objA.SetValue(123);
return 0;
}
//======================================
現(xiàn)在我們要做的事就是當(dāng)A類的SetValue被調(diào)用時(shí)B類的setValue也相應(yīng)的被調(diào)用,也就是A的mValue的值被修改時(shí)那么B也應(yīng)當(dāng)修改自己的mValue值,現(xiàn)在我們要解決的就是該問題。
解決該問題的方法可能有很多,如果在Qt里面我們可以考慮使用信號(hào)槽來(lái)解決,在MFC中可以考慮使用事件通知來(lái)完成,在C#里面使用事件委托來(lái)完成,嗯不過(guò)好像不論是哪一種方式感覺都不是最理想的,因?yàn)槿绻褂眯盘?hào)槽那么如果我們需要觀察多個(gè)屬性的話可能會(huì)要定義好多個(gè)信號(hào),同樣如果使用事件的話那么我們同樣需要定義很多事件類型,所以這些都不是一個(gè)一勞永逸的方法,這都是我所認(rèn)為最理想的方法。
大家可能想起了前面我們所說(shuō)的boost里面的信號(hào)槽,不錯(cuò)的主意,但是這個(gè)Qt的信號(hào)槽本身是差不多的東西,所以一樣不滿意,當(dāng)然最重要的一點(diǎn),如果我們通過(guò)objA調(diào)用SetValue的時(shí)候會(huì)觸發(fā)objB的setValue,而objB在調(diào)用setValue的時(shí)候就會(huì)觸發(fā)testFun,那么無(wú)論是Qt的信號(hào)槽還是boost的信號(hào)槽都沒法做到,那么好吧,這樣一來(lái)似乎沒有什么選擇的余地了,要么是函數(shù)對(duì)象要么就是函數(shù)指針。
接下來(lái)要說(shuō)的東西可能以我之口舌沒法很好的說(shuō)清楚,重要的還是大家多理解代碼,我覺得一碼勝千言。要讓objA和objB聯(lián)系起來(lái)那么必然通過(guò)一種介質(zhì),假如這個(gè)函數(shù)為Connect,那么我們先將設(shè)會(huì)是下面這種形式:
Connect(SIGANL(objA,xxxx),SLOT(objB,xxxx));
這個(gè)形式模仿的是Qt的connect,這就不說(shuō)了,我們現(xiàn)在來(lái)看看如果實(shí)現(xiàn)這個(gè)Connect函數(shù),而他的參數(shù)又應(yīng)該是什么?想一下,當(dāng)我們操作objA到時(shí)候objB會(huì)得到相應(yīng)的響應(yīng),那么這個(gè)函數(shù)里面我們至少要知道objA和objB關(guān)聯(lián)的到底是那個(gè)函數(shù),所以此處應(yīng)該需要將函數(shù)傳遞到Connnect里面。ok,現(xiàn)在我們來(lái)看一個(gè)原型:
template
void Connect(
const MString& funName,
void(T::*fun1)(Args...),
T* obj1,
std::function eventfun
)
從這個(gè)原型我們幾乎可以肯定這就是我們所需要的了,funName是函數(shù)的名字fun1就是objA要調(diào)用的函數(shù),obj1就是objA,eventfun即為我們被觸發(fā)的函數(shù),從這個(gè)函數(shù)原型來(lái)看他是支持無(wú)限參數(shù)的,所以他所完成的事是Qt的信號(hào)槽亦或是boost的信號(hào)都做不到的,大家也許會(huì)像何以見得呢?當(dāng)然更多的可能是怎么來(lái)調(diào)用這個(gè)函數(shù)?和上面一樣我們可以再進(jìn)一步封裝然后如下調(diào)用:
Connect(MSIGNAL(TestA, SetValue, &objA),MSLOT(&B::setValue,&objB));
這樣一來(lái)是不是很清晰了呢?MSIGNAL包裝的是類的名字,函數(shù),對(duì)象的指針,MSLOT的參數(shù)為成員函數(shù)的地址以及對(duì)象指針,好吧,這就作為我們的調(diào)用約定,那么問題來(lái)了,MSIGNAL和MSLOT是如何實(shí)現(xiàn)的呢?為什么兩個(gè)參數(shù)調(diào)用方式差距還如此之大呢?為了滿足我們上面Connect的原型,SIGNAL必須能夠生成一個(gè)和函數(shù)名等同的字符串以及該函數(shù)的函數(shù)指針和一個(gè)對(duì)象指針,而MSLOT將會(huì)通過(guò)成員函數(shù)的地址以及對(duì)象指針生成一個(gè)函數(shù)對(duì)象出來(lái)。
看到這里大家是不是覺得開始有些懵了呢?好吧,我們正在C++的路上越走越深,因?yàn)檫@些如果不是出于構(gòu)建庫(kù)的目的話正常使用過(guò)程中幾乎可能肯定不會(huì)使用得到。
MSIGNAL和MSLOT的調(diào)用差別如此之大正是因?yàn)閮烧叩膶?shí)現(xiàn)方式天差地別,因?yàn)镸SIGNAL的現(xiàn)實(shí)相當(dāng)簡(jiǎn)單,其實(shí)他就是一個(gè)宏定義:
#ifndef MSIGNAL
#define MSIGNAL(className,memFun,obj) #memFun,&className::memFun,obj
#endif
但是MSLOT的實(shí)現(xiàn)卻相當(dāng)復(fù)雜,要說(shuō)清楚真心不是那么容易,代碼雖然不多,但是想要理清還是需要好好琢磨一下:
//===================================
//
// 下面只是一些輔助函數(shù),不需要看懂,只需要知道怎么使用MSLOT即可
//
template
struct MPropertyFunHelp{
template
static inline auto Apply(T t,F fun,K obj, Args...args)->
decltype(
MPropertyFunHelp::Apply
(
t,fun, obj,
std::get(t),
args...)
)
{
return MPropertyFunHelp::Apply
(
t,fun,obj,
std::get(t),
args...
);
}
};
template<>
struct MPropertyFunHelp<0>{
template
static inline auto Apply(T t, F fun,K obj,Args...args)->decltype(
std::bind(fun,obj,args...)
)
{
return std::bind(fun,obj,args...);
}
};
template
struct ToFun{
template
static std::function Apply(T t,F fun,K obj){
auto __t = std::tuple_cat(t,std::make_tuple(std::_Ph()));
return ToFun::Apply
(__t,fun,obj);
}
};
template
struct ToFun{
template
static std::function Apply(T t,F fun,K obj){
return MPropertyFunHelp::Apply(t,fun,obj);
}
};
//
// 包裝成員函數(shù),我們需要用的就是下面的函數(shù)
//
template
std::function MSLOT(R(T::*fun)(Args...),T* obj){
auto t = std::make_tuple();
return ToFun<0,sizeof...(Args),R>::Apply
(
t,fun,obj
);
}
//
// 包裝自由函數(shù)
//
template
std::function MSLOT(R(*fun)(Args...)){
return std::forward>(fun);
}
//================================
到此下面的函數(shù)便可被正確調(diào)用了:
Connect(MSIGNAL(TestA, SetValue, &objA),MSLOT(&B::setValue,&objB));
該調(diào)用模式適用于任何類型包括自由函數(shù)而且不受參數(shù)個(gè)數(shù)限制都是同樣的調(diào)用方式,也就是說(shuō)無(wú)論SetValue和setValue的參數(shù)有多少個(gè)上面的函數(shù)調(diào)用依舊不變。