• <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>
            流逝的時光
            總有一天我們都會離去 email: zzxhang@gmail.com
            posts - 21,comments - 111,trackbacks - 0

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

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

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

            8}
            ;
            9

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

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

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

            13    }
            ;

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

             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函數(shù)里,我們這樣調(diào)用:

             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    }

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

             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只是為了區(qū)別參數(shù)類型而加進去的,這里我只舉了void,bool,char,的取出操作作為示范,其實還有很多類型,就不一一列出了,根據(jù)不同的類型,我們調(diào)用不同的lucky_popValueAs..來取出參數(shù),與此類推,對于有返回值的函數(shù),把返回值傳給腳本的操作也是必須封裝的:

            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來取得參數(shù)值及設置返回值了,為了讓事情能更簡單點,我們定義兩個宏:

            #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

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

               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);
                                    
                                    
            //調(diào)用函數(shù)
                        RT retVal = (*func)(p1);
                                    
            //把返回值傳給腳本,這句是ReturnVal::set(state,retVal);
                        __return(retVal);

                        
            return 1;
                    }

            }

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

             1template<typename Func>
             2    void registerFunction(Func func,const char* funcName)
             3    {   
             4        //增加函數(shù)指針大小的UserData
             5        unsigned char* buffer = (unsigned char*)lucky_addUserData(sizeof(func));
             6        
             7        //復制函數(shù)指針數(shù)據(jù)
             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        //清空跟主函數(shù)綁定的UserData
            17        lucky_clearAddedUserData();
            18    }

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

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

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

            14    }
            ;

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

            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中,我們把類的實例跟成員函數(shù)指針連接起來進行調(diào)用,以一個參數(shù)的函數(shù)指針調(diào)用代碼為例:

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

                        __defparam(
            1);

                        
            //調(diào)用成員函數(shù)
                        RT retVal = (callee.*func)(p1);

                        __return(retVal);

                        
            return 1;
                    }


            三、類的注冊

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

            1.構造/析構函數(shù)的封裝

              首先你需要了解的是在luckyScript中創(chuàng)建一個主程序?qū)ο髸r,腳本會創(chuàng)建一塊跟這個主程序?qū)ο蟠笮∫粯拥膬?nèi)存空間,然后再調(diào)用跟這個主程序?qū)ο笸闹鞒绦蚝瘮?shù),并把這塊內(nèi)存壓棧傳給用戶,也就是說,構建類的操作是由用戶完成的,用戶所需要做的就是用這塊內(nèi)存空間創(chuàng)建用戶所需要的對象,如果你對這些一點都不了解,我建議你可以先看看這篇文章中關于注冊對象的部分。ok,那么,我們先來看看統(tǒng)一注冊給腳本的構造函數(shù)是什么樣的:
             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是對象的類型,在這個函數(shù)中,我們?nèi)〕瞿_本所為我們創(chuàng)建的那塊內(nèi)存空間,并把它傳給CONSTRUCTOR::invoke接著處理,這個CONSTRUCTOR指定了構造函數(shù)的類型,但注意,它并不是構造函數(shù)指針的類型,實際上,類的構造函數(shù)是不允許你取址的,在上面的代碼中,它轉(zhuǎn)入的是下面這些代碼中的invoke函數(shù)里面,仍然以擁有0到1個參數(shù)的構造函數(shù)處理為例:
             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}
              前面已經(jīng)多次出現(xiàn)這個__argassert宏,我并不打算具體向你解釋它的構成,但你明白它是用來assert棧上的參數(shù)類型是否匹配的就行了,到這里,我們都應該明白上面那個CONSTRUCTOR就是具體的struct Constructor,在這個結(jié)構體的invoke方法里,我們?nèi)〕鰳嬙旌瘮?shù)的參數(shù),并調(diào)用構造函數(shù),或許需要向你解釋下這個比較少用的new(dataTrunk)..,它所實現(xiàn)的效果就是編譯器在我們提供的那塊內(nèi)存中構建對象,這樣構建出來的對象是不能直接delete的,我們需要顯式調(diào)用對象的析構函數(shù)。
              那么接下來看看類的析構函數(shù)的封裝,所幸的是析構函數(shù)一定是沒有參數(shù)的,省去了很多麻煩:
            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調(diào)用的就是下面的代碼:
             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            //顯式調(diào)用析構函數(shù)
            11            classObj->~Callee();
            12            
            13        }

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

            //有一個參數(shù)的構造函數(shù)
            template<typename C,typename P1>
            void registerClass(const char* className)
            {
                pushClassAndConstructor
            <C>(className,LuckySystem::Constructor<P1>());
            }
            根據(jù)Constructor<P1...>我們就具體指定了構造函數(shù)的類型,接下來看這個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同名的主程序構造函數(shù)
                    lucky_registerGlobalHostFunc(LuckySystem::Creator<Callee>::invoke<CONSTRUCTOR>,className);

                    
            //下劃線+className,注冊主程序析構函數(shù)
                    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指定了構造函數(shù)的類型,所以我們注冊給腳本的構造函數(shù)處理主函數(shù)是:Creator<Callee>::invoke<CONSTRUCTOR>,而析構函數(shù)為Destroytor<Callee>::invoke,上面的Object<Callee>::name把這一class類型的名字保存了下來,如果沒有為name方法指定參數(shù),那么會返回callee這個類型的class的名字(假如之前有存的話),為了限制篇幅,我并不打算貼出它的代碼

            2.往注冊的類添加成員方法
              當我們在luckyScript中調(diào)用一個主程序?qū)ο蟮姆椒〞r,腳本會把這個主程序?qū)ο髩簵鹘o用戶,并按預定義的規(guī)則得到處理主函數(shù)名,然后再調(diào)用它交給用戶處理,所以我們完全可以按照注冊類成員函數(shù)的方法,事先把類成員函數(shù)指針當作用戶數(shù)據(jù)壓棧,在腳本調(diào)用主程序函數(shù)時,一并取出來調(diào)用:
             1template<typename Callee,typename Func>
             2    void addMemFunc(Func func,const char* funcName)
             3    {   
             4        //增加函數(shù)指針大小的用戶數(shù)據(jù)
             5        unsigned char* funcBuffer = (unsigned char*)lucky_addUserData(sizeof(func));
             6        //copy函數(shù)指針數(shù)據(jù)
             7        memcpy(funcBuffer,&func,sizeof(Func));
             8
             9        //得到Callee類型的class的名字
            10        const char* className = LuckySystem::ObjectName<Callee>::name();
            11
            12        //成員函數(shù)處理主函數(shù)name:類型名 + 下劃線 + 成員函數(shù)名
            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就是注冊類成員函數(shù)時使用的那個主函數(shù)封裝,前面已對其做過介紹,在這個函數(shù)里面,我們?nèi)〕鲱惖膶嵗偃〕鲱惓蓡T函數(shù)的指針,合并調(diào)用
             
              對類屬性變量的存取需要我們提供兩個新的主函數(shù)封裝,同樣地,luckyScript在遇到主程序?qū)ο蟮拇嫒r也會調(diào)用用戶提供的主函數(shù)進行處理,并把這個主程序?qū)ο蟾也僮鲾?shù)壓棧傳給用戶,需要特別說明的是:由于賦值操作符有很多種,當遇到主程序成員變量的賦值時,luckyScript除了會把主程序?qū)ο蠛陀也僮鲾?shù)壓棧傳給用戶外,還會把操作符也壓棧傳給用戶以給用戶足夠的信息進行賦值,當然這是在這個成員變量不是對象類型的時候,假如這個成員變量本身也是在腳本中注冊過的對象,那么會調(diào)用操作符重載主函數(shù)進行處理
             1//成員變量為右操作數(shù)
             2    template<typename Callee,typename ValueType>
             3    struct MemberValueGettor
             4    {
             5        static void invoke(RuntimeState* state)
             6        {
             7            //取出成員變量指針數(shù)據(jù)
             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    //成員變量為左操作數(shù)
            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            //取出成員變量指針數(shù)據(jù)
            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                //取出右操作數(shù)的值
            38                setVal = Param::get(TypeWrapper<ValueType>(),state);
            39            }

            40            //取出對象實例
            41            unsigned char* calleeBuffer = (unsigned char*)lucky_popValueAsUserData(state);
            42            
            43
            44            //轉(zhuǎn)換類型
            45            ValueType Callee::** val = (ValueType Callee::**)(valBuffer);
            46            Callee* classObj = (Callee*)(calleeBuffer);
            47
            48            //根據(jù)不同的操作符進行賦值處理
            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        //增加成員變量指針大小的用戶數(shù)據(jù)
             5        unsigned char* valBuffer = (unsigned char*)lucky_addUserData(sizeof(val));
             6        //copy成員變量指針數(shù)據(jù)
             7        memcpy(valBuffer,&val,sizeof(val));
             8
             9        //得到Callee類型的class的名字
            10        const char* className = LuckySystem::ObjectName<Callee>::name();
            11
            12        //成員變量處理主函數(shù)命名規(guī)則:類型名 + 下劃線 + set/get + 下劃線 + 成員函數(shù)名
            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        //得到此成員變量的類型名,假如這個成員變量也是已經(jīng)注冊過的主程序?qū)ο箢愋停敲床粸榭?/span>
            23        std::string typeName = LuckySystem::ObjectName<ValueType>::name();
            24
            25        lucky_addHostMemberVal(className,valName,typeName.c_str());
            26    }

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

            3.操作符重載
            在遇到主程序?qū)ο蟮倪\算時,luckyScript會把操作符兩邊的value都壓棧,并根據(jù)不同的操作符,調(diào)用不同的主函數(shù)進行處理,我們所需要做的,就是在這些操作符重載處理主函數(shù)里,取出這兩個值,并進行運算操作:

            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;
                    }

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

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

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

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

                        Callee
            * classObj = getClassObject(state);

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

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

                    std::
            string realFuncName = className;

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


               寫到這里,我已經(jīng)很累了,就不再對封裝調(diào)用腳本函數(shù)及訪問全局變量的操作進行介紹了,之后源碼發(fā)布后可自行觀看,為了展示這個封裝庫的使用,我把OGRE的一些核心類跟接口注冊給腳本,并重寫了OGRE的兩個例子,本把想腳本源碼和截圖跟這篇文章放到一起,但考慮到這篇文章篇幅已經(jīng)很長,還是把它單獨放到另一篇文章吧
            posted on 2009-04-18 17:37 清風 閱讀(1647) 評論(0)  編輯 收藏 引用 所屬分類: LuckyScript
            久久人人妻人人爽人人爽| 久久精品国产99久久久古代| 久久se这里只有精品| 久久久久人妻一区精品果冻| 亚洲日本久久久午夜精品| 日韩人妻无码精品久久免费一 | 国产综合久久久久| 97久久精品人人做人人爽| 亚洲欧美日韩久久精品| 99久久久国产精品免费无卡顿| 国产成人精品久久综合| 偷偷做久久久久网站| 精品久久久久久| 久久夜色精品国产亚洲| 久久香蕉一级毛片| 欧美日韩精品久久久久| 久久夜色精品国产亚洲| 久久久亚洲欧洲日产国码是AV| 精品久久久久久亚洲| 99久久免费国产精品特黄| 久久精品国产亚洲麻豆| 中文字幕久久波多野结衣av| 国产成人精品久久亚洲| 久久男人Av资源网站无码软件| 久久成人精品视频| 奇米综合四色77777久久| 久久人人爽人人爽AV片| 久久99国产精品二区不卡| 久久精品免费全国观看国产| 国产综合成人久久大片91| 东京热TOKYO综合久久精品 | 中文成人无码精品久久久不卡| 精品久久一区二区| 久久久久亚洲Av无码专| 久久久久久久波多野结衣高潮| 久久人人超碰精品CAOPOREN| 91久久九九无码成人网站| 99国产欧美久久久精品蜜芽| 色综合久久久久综合体桃花网| 免费精品久久久久久中文字幕| 99久久精品国产一区二区|