青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

流逝的時光
總有一天我們都會離去 email: zzxhang@gmail.com
posts - 21,comments - 111,trackbacks - 0

   由于luckyScript引擎接口使用上的不便,我為它實現了一個基于C++的封裝庫,使用它可以比較方便地實現:類的注冊,任意C++函數的注冊,調用腳本函數,訪問腳本變量等比較核心的功能,雖然,用luckyScript引擎本身也可以做到上述這些,但我想你不會喜歡為每個主程序對象實現一大堆回調處理函數的,那在需要提供給腳本使用的東西數量比較大的時候會是個讓人崩潰的工作量,所以,必須在luckyScript上再實現一層封裝簡化這個過程,考慮到luckyScript只是一個無名小卒,沒有人會花時間去專門為它做那么個封裝的,所以只好由我自己來完成這個工作了,這個封裝庫的源碼會在發布luckyScript庫的時候附帶一起發布,下面,我詳細介紹下這個封裝庫最核心的幾個功能是如何實現的,雖然是基于luckyScript的封裝,但我想對于理解其他些比較流行的腳本(比如lua)的封裝庫也會是有用的。

一、任意C++函數的注冊
  在luckyScript中,每個提供給腳本使用的主程序都必須有相同的原型,這在腳本中是為了可以方便的統一處理,但是,方便了開發者的東西通常就是給用戶提供了不方便,用戶得為每個在腳本中調用的主程序函數實現一個符合腳本規定原型的封裝,這會是個煩人的工作,為了避免它,我們必須用一層機制把這些細節隱藏起來,有了luckyScript中UserData的概念,我們很容易就能想到可以把注冊給腳本的函數跟真正調用的函數分離開來,事先把需要調用的函數指針當作UserData傳給腳本,在腳本call的那個主程序函數里,我們再把這個函數指針取出來,之后就可以進行我們真正所需要的函數調用了,這的確是個辦法,但馬上我們就會遇到另一個問題,UserData都是void指針類型的,我們如何能在取出來的時候知道它是什么類型的函數指針呢?必須有個辦法能把這個函數指針的類型信息傳過來,于是,你會想到C++最強大的特性,模板,對了,可以把函數指針類型當作模板參數,這樣我們就可以把void指針轉換為正確的函數指針了,想到了這一層,我們就可以寫出給腳本調用的一個通用主函數的定義了:

1template<typename Func>
2struct Functor
3{
4    static void invoke(RuntimeState* state)
5    {
6    
7    }

8}
;
9

Func就是我們需要的函數指針類型,在這個函數里面我們需要做些什么事情呢?恩,我們必須把函數指針數據取出來,轉換為正確的類型,然后再調用它,于是我們就遇到了第二個問題:Func只是一個類型的存貯,我們知道func是一個函數指針,但卻無法知道它具體是什么樣的一個函數指針,它有多少個參數,有沒有返回值我們都不知道,因此,必須把函數的調用再往下移一層,利用模板參數的自動匹配,通過傳遞Func參數讓程序自動轉到我們有足夠信息來調用這個函數指針的地方去,請先看下面一段代碼:

1template <typename RT>
2int call(RT (*func)(), RuntimeState* state)
3{
4}

5template <typename RT, typename P1>
6int call(RT (*func)(P1), RuntimeState* state)
7{
8}

9

RT (*func)()代表沒有參數的函數指針類型,RT (*func)(P1)代表有一個參數的函數指針類型,調用這個call函數,我們需要給一個函數指針參數,根據函數指針的類型,編譯器會自動尋找匹配的模板函數,如果這個函數指針是沒有參數的,那么就會調用第一個call函數,有一個參數則會調用第二個,現在,我們上面的問題是不是已經解決了?在Functor::invoke里面,我們能拿到正確的函數指針,這意味著我們可以調用call函數讓程序轉到能讓我們擁有更多函數信息的地方去,這樣就可以寫出Functor::invoke函數的代碼了

 1template<typename Func>
 2    class Functor
 3    {
 4    public:
 5        static void invoke(RuntimeState* state)
 6        {
 7            //函數指針buffer
 8            unsigned char* buffer = (unsigned char*)lucky_popValueAsUserData(state);
 9
10            //轉換為正確的函數指針類型并調用call
11            call((*(Func*)buffer),state);
12        }

13    }
;

然后在call里面,是不是就可以直接調用了呢?答案是還是不行,因為在call里面,我們還是不知道函數是不是有返回值的,那個RT可能是void或具體的類型,因此,我們得把函數的調用進一步下調:

 1//有返回值
 2template<typename RT>
 3    struct Caller
 4    {
 5        static int call(RT (*func)(), RuntimeState* state)
 6        {
 7        }

 8
 9        template <typename P1>
10        static int call(RT (*func)(P1), RuntimeState* state)
11        {   
12        }

13}

14//沒有返回值
15template<>
16    struct Caller<void>
17    {
18        static int call(void (*func)(), RuntimeState* state)
19        {
20        }

21
22        template <typename P1>
23        static int call(void (*func)(P1), RuntimeState* state)
24        {   
25        }

26}

在第二段代碼中的call函數里,我們這樣調用:

 1template <typename RT>
 2    int call(RT (*func)(), RuntimeState* state)
 3    {
 4        return Caller<RT>::call(func, state);
 5    }

 6
 7
 8    template <typename RT, typename P1>
 9    int call(RT (*func)(P1), RuntimeState* state)
10    {
11        return Caller<RT>::call(func, state);
12    }

這樣就能根據RT的類型進一步轉到Caller<RT>::call或者Caller<void>::call上去了,ok,到這一步,我們已經有足夠的信息來調用這個函數指針了,調用函數,需要先取出參數信息,我們需要的參數都保存在棧上,luckyScript提供了lucky_popValueAs...樣的api來提供得到棧上元素值的功能,但這樣我們就會碰到一開頭的問題:調用這些API需要你事先知道棧頂元素的類型,可我們有的只是P1這樣的參數類型載體,確切的類型在這里是無法知道的,因此,有必要封裝參數取出的操作:

 1struct Param
 2{
 3    static inline void    get(TypeWrapper<void>, RuntimeState*)
 4    {  }
 5    static inline bool    get(TypeWrapper<bool>, RuntimeState* state)
 6    {
 7        //bool對應的是腳本中的int類型 
 8        return lucky_popValueAsInt(state) != 0;  }

 9    static inline char    get(TypeWrapper<char>, RuntimeState* state)
10    {
11        //char也對應int
12         return static_cast<char>(lucky_popValueAsInt(state));  
13    }

14}

上面的TypeWrapper只是為了區別參數類型而加進去的,這里我只舉了void,bool,char,的取出操作作為示范,其實還有很多類型,就不一一列出了,根據不同的類型,我們調用不同的lucky_popValueAs..來取出參數,與此類推,對于有返回值的函數,把返回值傳給腳本的操作也是必須封裝的:

struct ReturnVal
    
{
        
static inline void set(RuntimeState* state, bool value)                {  lucky_setReturnValue(state,(int)value); }
        
static inline void set(RuntimeState* state, char value)                {  lucky_setReturnValue(state,(int)value);  }
        
static inline void set(RuntimeState* state, unsigned char value)    {  lucky_setReturnValue(state,(int)value);  }
    ..

這樣就可以用Param::get和ReturnVal::set來取得參數值及設置返回值了,為了讓事情能更簡單點,我們定義兩個宏:

#ifndef __defparam
    
#define __defparam(arg) P##arg p##arg = Param::get(TypeWrapper<P##arg>(),state);
#endif

#ifndef __return
    
#define __return(retVal)  ReturnVal::set(state,retVal);
#endif

一切準備就緒,可以進行我們真正的函數調用操作了,下面用有一個參數的函數的調用代碼做例子,請注意看代碼的注釋

   template<typename RT>
    
struct Caller
    
{
        template 
<typename P1>
        
static int call(RT (*func)(P1), RuntimeState* state)
        
{   
                       
//測試棧上的元素是不是我們需要的類型
            __argassert(1,-1);
                      
//這句相當于P1 p1 = Param::get(TypeWrapper<P1>(),state);
            __defparam(1);
                        
                        
//調用函數
            RT retVal = (*func)(p1);
                        
//把返回值傳給腳本,這句是ReturnVal::set(state,retVal);
            __return(retVal);

            
return 1;
        }

}

這樣就完成了核心的函數調用,我們一開始的設想已經實現,現在該考慮提供給用戶注冊函數的接口的實現了,我們所需要用戶做的就是提供一個任意類型的函數指針跟一個在腳本中使用的函數名,之后我們就會將這個函數指針作為用戶數據傳進腳本,然后再為這個函數類型注冊一個Functor<Func>::invoke函數:

 1template<typename Func>
 2    void registerFunction(Func func,const char* funcName)
 3    {   
 4        //增加函數指針大小的UserData
 5        unsigned char* buffer = (unsigned char*)lucky_addUserData(sizeof(func));
 6        
 7        //復制函數指針數據
 8        memcpy(buffer,&func,sizeof(func));
 9
10        //得到返回類型名
11        std::string typeName = LuckySystem::ReturnType::name(func);
12
13        //注冊Functor<Func>::invoke
14        lucky_registerHostFunc(mRuntimeState,LuckySystem::Functor<Func>::invoke,funcName,typeName.c_str());
15
16        //清空跟主函數綁定的UserData
17        lucky_clearAddedUserData();
18    }

上面只是舉0到1個參數的函數指針的調用作為例子,通過定義更多的函數類型,我們就可以讓注冊函數的適用范圍進一步擴寬,在我所做的這個腳本封裝中,最多可以支持擁有7個參數的主函數的注冊。

二、任意類成員函數的注冊
原理跟注冊非成員函數是完全一樣的,唯一不同的是,我們需要把這個類成員函數所屬的對象的一個實例也當作用戶數據傳給腳本,然后在腳本調用的那個函數里,我們再把這個實例跟函數指針一并取出來進行調用。如果上面的內容你都已經認真看了,那么理解下面的代碼是很容易的:

 1template<typename Callee,typename Func>
 2    struct MemberFunctor
 3    {
 4        static void invoke(RuntimeState* state)
 5        {
 6            //取出函數指針buffer
 7            unsigned char* funcBuffer = (unsigned char*)lucky_popValueAsUserData(state);
 8            //取出類的實例buffer
 9            unsigned char* calleeBuffer = (unsigned char*)lucky_popValueAsUserData(state);
10
11            //調用
12            call((*(Callee*)calleeBuffer),(*(Func*)funcBuffer),state);
13        }

14    }
;

在這個MemberFuncto::invoke里,除了取出成員函數指針外,我們還取出了這個成員函數所屬的類的一個實例,并把它們作為參數調用call函數,在這里調用的call函數跟注冊非成員函數時的call重載相比,就是多了一個Callee& callee參數

template <typename Callee, typename RT>
    
int call(Callee& callee, RT (Callee::*func)(), RuntimeState* state)
    
{
        
return Caller<RT>::call(callee, func, state);
    }

    template 
<typename Callee, typename RT, typename P1>
    
int call(Callee& callee, RT (Callee::*func)(P1), RuntimeState* state)
    
{
        
return Caller<RT>::call(callee, func,state);
    }

在Caller<RT>::call中,我們把類的實例跟成員函數指針連接起來進行調用,以一個參數的函數指針調用代碼為例:

template <typename Callee, typename P1>
        
static int call(Callee& callee, RT (Callee::*func)(P1), RuntimeState* state)
        
{
            __argassert(
1,-1);

            __defparam(
1);

            
//調用成員函數
            RT retVal = (callee.*func)(p1);

            __return(retVal);

            
return 1;
        }


三、類的注冊

  要完整地將一個c++的類注冊給腳本使用,我們必須為這個類所包含的四個部分:構造函數 ,析構函數,屬性,方法提供主程序函數處理,luckyScript采用一套預定義的規范來命名這些主函數,并在合適地方調用它們,同樣地,我們不可能為所有類都寫單獨的處理函數,所以,必須為這四個部分做一個封裝,以使得能用一種統一的方式處理這個流程。

1.構造/析構函數的封裝

  首先你需要了解的是在luckyScript中創建一個主程序對象時,腳本會創建一塊跟這個主程序對象大小一樣的內存空間,然后再調用跟這個主程序對象同名的主程序函數,并把這塊內存壓棧傳給用戶,也就是說,構建類的操作是由用戶完成的,用戶所需要做的就是用這塊內存空間創建用戶所需要的對象,如果你對這些一點都不了解,我建議你可以先看看這篇文章中關于注冊對象的部分。ok,那么,我們先來看看統一注冊給腳本的構造函數是什么樣的:
 1template<typename Callee>
 2    struct Creator
 3    {
 4        template<typename CONSTRUCTOR>
 5        static void invoke(RuntimeState* state) 
 6        {  
 7            void* dataTrunk = lucky_popValueAsUserData(state);
 8
 9            CONSTRUCTOR::invoke<Callee>(state,dataTrunk);     
10        }

11    }
;
Callee是對象的類型,在這個函數中,我們取出腳本所為我們創建的那塊內存空間,并把它傳給CONSTRUCTOR::invoke接著處理,這個CONSTRUCTOR指定了構造函數的類型,但注意,它并不是構造函數指針的類型,實際上,類的構造函數是不允許你取址的,在上面的代碼中,它轉入的是下面這些代碼中的invoke函數里面,仍然以擁有0到1個參數的構造函數處理為例:
 1template<typename P1>
 2    struct Constructor<P1>
 3    {
 4        template<typename Callee>
 5        static void invoke(RuntimeState* state,void* dataTrunk)
 6        {   
 7            __argassert(1,-1);
 8
 9            __defparam(1);
10
11            new(dataTrunk) Callee(p1);
12        }

13    }
;
14    template<>
15    struct Constructor<void>
16    
17        template<typename Callee>
18        static void invoke(RuntimeState* state,void* dataTrunk) 
19        
20            new(dataTrunk) Callee();
21        }
 
22    }
;
23}
  前面已經多次出現這個__argassert宏,我并不打算具體向你解釋它的構成,但你明白它是用來assert棧上的參數類型是否匹配的就行了,到這里,我們都應該明白上面那個CONSTRUCTOR就是具體的struct Constructor,在這個結構體的invoke方法里,我們取出構造函數的參數,并調用構造函數,或許需要向你解釋下這個比較少用的new(dataTrunk)..,它所實現的效果就是編譯器在我們提供的那塊內存中構建對象,這樣構建出來的對象是不能直接delete的,我們需要顯式調用對象的析構函數。
  那么接下來看看類的析構函數的封裝,所幸的是析構函數一定是沒有參數的,省去了很多麻煩:
1template<typename Callee>
2    struct Destroytor
3{
4        static void invoke(RuntimeState* state)
5     {          
                     
void* dataTrunk = lucky_popValueAsUserData(state);
6                Desconstructor<Callee>::invoke(state,dataTrunk);
7        }

8    }
;
這個Desconstructor<Callee>::invoke調用的就是下面的代碼:
 1template<typename Callee>
 2    struct Desconstructor
 3    {
 4        static void invoke(RuntimeState* state,void* dataTrunk)
 5        {
 6            unsigned char* calleeBuffer = (unsigned char*)dataTrunk;
 7
 8            Callee* classObj = (Callee*)(calleeBuffer);
 9
10            //顯式調用析構函數
11            classObj->~Callee();
12            
13        }

14    }
;
搞定了這些,我們就可以具體設計我們提供給用戶注冊類的接口了,至少,它已經可以在腳本中創建與銷毀了,對吧?所以不需要有太多顧慮,前面已經提到過,構造函數是不能取址的,所以不能以用戶傳進構造函數指針的方式來完成類的注冊,我們需要提供一系列的模板重載函數,像下面這樣:
//沒有參數的構造函數
template<typename C>
void registerClass(const char* className)
{
   pushClassAndConstructor
<C>(className,LuckySystem::Constructor<>());
}

//有一個參數的構造函數
template<typename C,typename P1>
void registerClass(const char* className)
{
    pushClassAndConstructor
<C>(className,LuckySystem::Constructor<P1>());
}
根據Constructor<P1...>我們就具體指定了構造函數的類型,接下來看這個pushClassAndConstructor做了什么事情:
template<typename Callee,typename CONSTRUCTOR>
    
void pushClassAndConstructor(const char* className,CONSTRUCTOR)
    
{   
        lucky_clearAddedUserData();

        LuckySystem::ObjectName
<Callee>::name(className);
        LuckySystem::ObjectName
<Callee*>::name(className);
        LuckySystem::ObjectName
<Callee&>::name(className);
        LuckySystem::ObjectName
<const Callee&>::name(className);
        LuckySystem::ObjectName
<Callee const>::name(className);
        LuckySystem::ObjectName
<const Callee*>::name(className);

        
//注冊與class同名的主程序構造函數
        lucky_registerGlobalHostFunc(LuckySystem::Creator<Callee>::invoke<CONSTRUCTOR>,className);

        
//下劃線+className,注冊主程序析構函數
        std::string destroyFuncName = "";
        destroyFuncName 
+= "_";
        destroyFuncName 
= destroyFuncName + className;
        lucky_registerGlobalHostFunc(LuckySystem::Destroytor
<Callee>::invoke,destroyFuncName.c_str());

        size_t size 
= sizeof(Callee);
        lucky_registerHostClass(className,size);
    }
CONSTRUCTOR指定了構造函數的類型,所以我們注冊給腳本的構造函數處理主函數是:Creator<Callee>::invoke<CONSTRUCTOR>,而析構函數為Destroytor<Callee>::invoke,上面的Object<Callee>::name把這一class類型的名字保存了下來,如果沒有為name方法指定參數,那么會返回callee這個類型的class的名字(假如之前有存的話),為了限制篇幅,我并不打算貼出它的代碼

2.往注冊的類添加成員方法
  當我們在luckyScript中調用一個主程序對象的方法時,腳本會把這個主程序對象壓棧傳給用戶,并按預定義的規則得到處理主函數名,然后再調用它交給用戶處理,所以我們完全可以按照注冊類成員函數的方法,事先把類成員函數指針當作用戶數據壓棧,在腳本調用主程序函數時,一并取出來調用:
 1template<typename Callee,typename Func>
 2    void addMemFunc(Func func,const char* funcName)
 3    {   
 4        //增加函數指針大小的用戶數據
 5        unsigned char* funcBuffer = (unsigned char*)lucky_addUserData(sizeof(func));
 6        //copy函數指針數據
 7        memcpy(funcBuffer,&func,sizeof(Func));
 8
 9        //得到Callee類型的class的名字
10        const char* className = LuckySystem::ObjectName<Callee>::name();
11
12        //成員函數處理主函數name:類型名 + 下劃線 + 成員函數名
13        std::string realFuncName = className;
14        realFuncName = realFuncName + "_" + funcName; 
15        lucky_registerGlobalHostFunc(LuckySystem::MemberFunctor<Callee,Func>::invoke,realFuncName.c_str());
16
17        lucky_clearAddedUserData();
18
19        std::string typeName = LuckySystem::ReturnType::name(func);
20
21        lucky_addHostMemberFunc(className,funcName,typeName.c_str());
22    }
注意這個MemberFunctor<Callee,Func>::invoke就是注冊類成員函數時使用的那個主函數封裝,前面已對其做過介紹,在這個函數里面,我們取出類的實例,再取出類成員函數的指針,合并調用
 
  對類屬性變量的存取需要我們提供兩個新的主函數封裝,同樣地,luckyScript在遇到主程序對象的存取時也會調用用戶提供的主函數進行處理,并把這個主程序對象跟右操作數壓棧傳給用戶,需要特別說明的是:由于賦值操作符有很多種,當遇到主程序成員變量的賦值時,luckyScript除了會把主程序對象和右操作數壓棧傳給用戶外,還會把操作符也壓棧傳給用戶以給用戶足夠的信息進行賦值,當然這是在這個成員變量不是對象類型的時候,假如這個成員變量本身也是在腳本中注冊過的對象,那么會調用操作符重載主函數進行處理
 1//成員變量為右操作數
 2    template<typename Callee,typename ValueType>
 3    struct MemberValueGettor
 4    {
 5        static void invoke(RuntimeState* state)
 6        {
 7            //取出成員變量指針數據
 8            unsigned char* valBuffer = (unsigned char*)lucky_popValueAsUserData(state);
 9            //取出類的實例
10            unsigned char* calleeBuffer = (unsigned char*)lucky_popValueAsUserData(state);
11
12            ValueType Callee::** val = (ValueType Callee::**)(valBuffer);
13            Callee* classObj = (Callee*)(calleeBuffer);
14
15            //返回成員變量值
16            __return(classObj->**(val));
17        }

18    }
;
19
20    //成員變量為左操作數
21    template<typename Callee,typename ValueType>
22    struct MemberValueSettor
23    {
24        typedef ValueType P1;
25
26        static void invoke(RuntimeState* state)
27        {
28            ValueType setVal;
29            //取出成員變量指針數據
30            unsigned char* valBuffer = (unsigned char*)lucky_popValueAsUserData(state);
31            //取出操作符
32            int opType = lucky_popValueAsInt(state);
33            if(opType != LUCKY_OP_TYPE_INC && opType != LUCKY_OP_TYPE_DEC)
34            {
35                __argassert(1,-1);
36
37                //取出右操作數的值
38                setVal = Param::get(TypeWrapper<ValueType>(),state);
39            }

40            //取出對象實例
41            unsigned char* calleeBuffer = (unsigned char*)lucky_popValueAsUserData(state);
42            
43
44            //轉換類型
45            ValueType Callee::** val = (ValueType Callee::**)(valBuffer);
46            Callee* classObj = (Callee*)(calleeBuffer);
47
48            //根據不同的操作符進行賦值處理
49            switch(opType)
50            {
51            case LUCKY_OP_TYPE_INC:
52                classObj->**(val) += 1;
53                break;
54            case LUCKY_OP_TYPE_DEC:
55                classObj->**(val) -= 1;
56                break;
57   //以下略過不貼
58}
在為主程序增加成員變量的接口上,我們同樣要求用戶提供此成員變量的指針以及在腳本中使用的名字
 1template<typename Callee,typename ValueType>
 2    void addMemVal(ValueType Callee::* val,const char* valName)
 3    {   
 4        //增加成員變量指針大小的用戶數據
 5        unsigned char* valBuffer = (unsigned char*)lucky_addUserData(sizeof(val));
 6        //copy成員變量指針數據
 7        memcpy(valBuffer,&val,sizeof(val));
 8
 9        //得到Callee類型的class的名字
10        const char* className = LuckySystem::ObjectName<Callee>::name();
11
12        //成員變量處理主函數命名規則:類型名 + 下劃線 + set/get + 下劃線 + 成員函數名
13        std::string realFuncName = className;
14        realFuncName = realFuncName + "_" + "get_" + valName; 
15        lucky_registerGlobalHostFunc(LuckySystem::MemberValueGettor<Callee,ValueType>::invoke,realFuncName.c_str());
16        realFuncName = className;
17        realFuncName = realFuncName + "_" + "set_" + valName; 
18        lucky_registerGlobalHostFunc(LuckySystem::MemberValueSettor<Callee,ValueType>::invoke,realFuncName.c_str());
19
20        lucky_clearAddedUserData();
21
22        //得到此成員變量的類型名,假如這個成員變量也是已經注冊過的主程序對象類型,那么不為空
23        std::string typeName = LuckySystem::ObjectName<ValueType>::name();
24
25        lucky_addHostMemberVal(className,valName,typeName.c_str());
26    }

 在這個函數里,我們把成員變量指針當作用戶數據傳進腳本,并為此成員變量的存取注冊兩個主程序處理函數:MemberValueGetter<Callee,ValueType>::invoke,MemberValueSettor<Callee,ValueType>::invoke,當腳本遇到這個成員變量的存取操作時,會調用這兩個主函數進行處理。

3.操作符重載
在遇到主程序對象的運算時,luckyScript會把操作符兩邊的value都壓棧,并根據不同的操作符,調用不同的主函數進行處理,我們所需要做的,就是在這些操作符重載處理主函數里,取出這兩個值,并進行運算操作:

template<typename Callee,typename ValueType>
    
struct OperatorOveridor
    
{
        typedef ValueType P1;

        
static Callee* getClassObject(RuntimeState* state)
        
{
            unsigned 
char* calleeBuffer = (unsigned char*)lucky_popValueAsUserData(state);
    
            Callee
* classObj = (Callee*)(calleeBuffer);

            
return classObj;
        }

//自加操作符重載處理主函數
        static void invokeInc(RuntimeState* state)
        
{
            Callee
* classObj = getClassObject(state);

            
//自加操作
           (*classObj)++;
        }

//賦值操作符重載處理主函數
        static void invokeAssign(RuntimeState* state)
        
{
            __argassert(
1,-1);

            
//取出第二個操作數
            __defparam(1);

            Callee
* classObj = getClassObject(state);

            
//進行賦值操作
            (*classObj) = p1;
        }

}
在接口方面,我們僅需要用戶提供模板參數說明兩個操作數的類型,以賦值操作符為例:
template<typename Callee,typename ValueType>
    
void overideAssignOp()
    
{   
        
const char* className = LuckySystem::ObjectName<Callee>::name();

        std::
string realFuncName = className;

        
//操作符重載處理主函數命名規則:類型名 + 下劃線 + "Overide" + 操作符英文符號
        realFuncName = realFuncName + "_" + "Overide_" + "Assign"
        lucky_registerGlobalHostFunc(LuckySystem::OperatorOveridor
<Callee,ValueType>::invokeAssign,realFuncName.c_str());
    }
注冊的主函數是上面的OperatorOveridor<Callee,ValueType>::invokeAssign,callee和valueType為操作符左右兩邊操作數類型


   寫到這里,我已經很累了,就不再對封裝調用腳本函數及訪問全局變量的操作進行介紹了,之后源碼發布后可自行觀看,為了展示這個封裝庫的使用,我把OGRE的一些核心類跟接口注冊給腳本,并重寫了OGRE的兩個例子,本把想腳本源碼和截圖跟這篇文章放到一起,但考慮到這篇文章篇幅已經很長,還是把它單獨放到另一篇文章吧
posted on 2009-04-18 17:37 清風 閱讀(1668) 評論(0)  編輯 收藏 引用 所屬分類: LuckyScript
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲国内精品| 久久精品免费看| 欧美多人爱爱视频网站| 久久精品视频播放| 欧美xart系列在线观看| 亚洲国产精品热久久| 久久亚洲高清| 最新高清无码专区| 亚洲欧美成人一区二区在线电影| 午夜激情综合网| 老司机亚洲精品| 欧美日韩一区二区三区高清| 国产精品久久久久久久午夜片| 国产免费亚洲高清| 91久久黄色| 欧美在线www| 亚洲第一黄色网| 亚洲视频大全| 久久婷婷亚洲| 一区二区欧美在线观看| 亚洲欧洲在线播放| 亚洲精品乱码久久久久久蜜桃麻豆| 99国产一区二区三精品乱码| 亚洲欧美日本视频在线观看| 久久精视频免费在线久久完整在线看| 男女激情视频一区| 国产精品一区视频| 一本久道综合久久精品| 久久久综合网| 亚洲一级免费视频| 欧美激情视频在线播放 | 久久综合国产精品| 最新成人av在线| 久久久久久久综合| 国产精品一区二区欧美| a4yy欧美一区二区三区| 久久婷婷久久一区二区三区| 日韩视频免费观看| 久久综合伊人77777| 国产精品久久午夜| 亚洲午夜av在线| 亚洲高清在线播放| 久久精品国产亚洲一区二区| 国产精品嫩草影院av蜜臀| 99re66热这里只有精品3直播| 免费在线欧美黄色| 欧美在线亚洲在线| 国产麻豆精品theporn| 亚洲综合电影一区二区三区| 亚洲另类一区二区| 欧美日韩国产二区| 亚洲深夜福利视频| 亚洲精品中文字幕在线| 美国成人毛片| 亚洲日本va午夜在线电影| 欧美jjzz| 蜜臀av在线播放一区二区三区| 韩国一区二区三区在线观看 | 黄色成人免费观看| 久久精品道一区二区三区| 亚洲制服av| 国产丝袜一区二区| 久久深夜福利| 久久伊人亚洲| 亚洲精品少妇| 日韩网站免费观看| 国产精品久久久久久久久免费| 在线视频亚洲欧美| 亚洲夜间福利| 国产原创一区二区| 久久综合色影院| 欧美xxxx在线观看| 亚洲夜间福利| 欧美一区二区视频在线观看| 国语自产在线不卡| 亚洲电影毛片| 久久国产精品黑丝| 国产亚洲欧美激情| 欧美成人福利视频| 欧美日本久久| 性欧美办公室18xxxxhd| 午夜日韩在线观看| 亚洲高清成人| 99在线|亚洲一区二区| 国产精品一区二区在线| 久久嫩草精品久久久久| 欧美大片一区二区| 久久国产精品一区二区三区四区 | 亚洲欧美日韩电影| 精品成人久久| 日韩午夜av电影| 很黄很黄激情成人| 亚洲美女av黄| 国内精品久久久久影院 日本资源| 欧美国产日韩免费| 国产精品自在线| 亚洲人成人一区二区在线观看| 国产精品福利在线观看| 欧美.日韩.国产.一区.二区| 国产精品成人免费视频| 欧美电影免费观看| 国产一区二区日韩| 亚洲天堂黄色| 亚洲日本免费| 久久精品国产免费看久久精品| 在线视频免费在线观看一区二区| 久久久www成人免费精品| 亚洲综合色网站| 欧美国产视频在线| 另类尿喷潮videofree| 国产精品视频xxxx| 亚洲美女精品一区| 亚洲国产精品毛片| 久久精品一区二区三区不卡| 亚洲欧美日本精品| 欧美黄色大片网站| 免费观看日韩| 国内外成人免费激情在线视频| 一区二区三区色| 亚洲深夜福利视频| 欧美日韩亚洲一区二| 亚洲区中文字幕| 亚洲美女av电影| 美女黄色成人网| 女人香蕉久久**毛片精品| 国产手机视频精品| 性色av一区二区三区在线观看| 亚洲字幕一区二区| 国产精品magnet| 亚洲视频图片小说| 亚洲女人天堂成人av在线| 欧美日韩综合在线| 99人久久精品视频最新地址| 一本久道久久久| 欧美午夜宅男影院| 宅男噜噜噜66一区二区| 亚洲综合色丁香婷婷六月图片| 欧美亚洲成人免费| 亚洲专区一二三| 久久久天天操| 亚洲国产一成人久久精品| 欧美精品黄色| 亚洲色图在线视频| 欧美日韩一区免费| 亚洲综合999| 久久综合成人精品亚洲另类欧美| 国精产品99永久一区一区| 久久久久久自在自线| 欧美激情免费观看| 99re6热只有精品免费观看| 欧美人与禽猛交乱配| 99国产精品国产精品毛片| 亚洲在线成人| 国产在线观看精品一区二区三区| 久久激情五月婷婷| 欧美激情在线| 亚洲欧美精品| 国产专区综合网| 欧美成ee人免费视频| 一本久久a久久免费精品不卡| 欧美一级欧美一级在线播放| 国产一区日韩欧美| 欧美风情在线观看| 亚洲一区二区三区成人在线视频精品| 久久爱www久久做| 亚洲精品久久久久| 国产精品男gay被猛男狂揉视频| 欧美一区二区三区免费视| 亚洲国产清纯| 欧美中文字幕在线观看| 亚洲美女视频在线观看| 国产色婷婷国产综合在线理论片a| 麻豆国产精品va在线观看不卡| 中文久久精品| 亚洲大片精品永久免费| 欧美一区二区在线播放| 一本色道久久综合狠狠躁篇怎么玩 | 亚洲欧美另类国产| 在线电影一区| 国产精品成人观看视频国产奇米| 久久九九99| 亚洲综合电影一区二区三区| 欧美国产日韩在线观看| 久久国产精品免费一区| 亚洲一区二区三区激情| 亚洲国产另类久久精品| 国产一二三精品| 国产精品久久久久久久久久久久久久 | 裸体一区二区三区| 亚洲欧美日韩久久精品| 91久久久久久久久| 久久亚洲私人国产精品va| 亚洲一区尤物| 日韩午夜av在线| 在线视频国内自拍亚洲视频| 国产欧美日韩精品专区| 国产精品久久久久aaaa樱花 | 最新亚洲激情| 在线播放国产一区中文字幕剧情欧美 | 久久久久久一区二区|