• <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>

            lua

            游戲中使用lua

            首先,讓我來(lái)簡(jiǎn)單的解釋一下Lua解釋器的工作機(jī)制,Lua解釋器自身維護(hù)一個(gè)運(yùn)行時(shí)棧,通過(guò)這個(gè)運(yùn)行時(shí)棧,Lua解釋器向主機(jī)程序傳遞參數(shù),所以我們可以這樣來(lái)得到一個(gè)腳本變量的值:

            獲取腳本的變量的值


            lua_pushstring(L, "var"); //將變量的名字放入棧
            lua_gettatbl(L, LUA_GLOBALSINDEX);變量的值現(xiàn)在棧頂

            假設(shè)你在腳本中有一個(gè)變量 var = 100
            你可以這樣來(lái)得到這個(gè)變量值:
            int var = lua_tonumber(L, -1);

            怎么樣,是不是很簡(jiǎn)單?

            Lua定義了一個(gè)宏讓你簡(jiǎn)單的取得一個(gè)變量的值:
            lua_getglobal(L, name)

            我們可以這樣來(lái)取得一個(gè)變量的值:
            lua_getglobal(L, "var"); //變量的值現(xiàn)在棧頂
            int var = lua_tonumber(L, -1);

            完整的測(cè)試代碼如下:

            #include "lua.h"
            #inculde "lauxlib.h"
            #include "lualib.h"

            int main(int argc, char *argv[])
            {
            lua_State *L = lua_open();
            luaopen_base(L);
            luaopen_io(L);

            const char *buf = "var = 100";

            lua_dostring(L, buf);

            lua_getglobal(L, "var");
            int var = lua_tonumber(L, -1);

            assert(var == 100);

            lua_close(L);

            return 0;
            }

            關(guān)于全局變量:
            如上面我們所看到的,lua_getglobal()將Lua的一個(gè)全局變量放至棧頂,假如我們的腳本包含一個(gè)全局變量z,下面這段代碼將獲取z的值:

            代碼:
            lua_getglobal(L, "z");
                z = (int)lua_tonumber(L, -1);
                lua_pop(L, 1);



            與之對(duì)應(yīng)的lua_setglobal()用來(lái)設(shè)置Lua的一個(gè)全局變量的值,下面的這段代碼將全局變量z的值設(shè)置為10

            代碼:
            lua_pushnumber(L, 10);
                lua_setglobal(L, "z");


            注意,不需要在你的Lua腳本中顯式的全局變量,如果全局變量不存在,lua_setglobal()將創(chuàng)建一個(gè)新的全局變量。


            在程序中調(diào)用腳本的函數(shù)

            在你的游戲中應(yīng)用Lua(1):調(diào)用函數(shù)

            假設(shè)你在腳本中定義了一個(gè)函數(shù):

            function main(number)
            number = number + 1
            return number
            end

            在你的游戲代碼中,你希望在某個(gè)時(shí)刻調(diào)用這個(gè)函數(shù)取得它的返回值。

            在Lua中,函數(shù)等同于變量,所以你可以這樣來(lái)取得這個(gè)函數(shù):

            lua_getglobal(L, "main");//函數(shù)現(xiàn)在棧頂

            現(xiàn)在,我們可以調(diào)用這個(gè)函數(shù),并傳遞給它正確的參數(shù):

            lua_pushnumber(L, 100); //將參數(shù)壓棧
            lua_pcall(L, 1, 1, 0); //調(diào)用函數(shù),有一個(gè)參數(shù),一個(gè)返回值
            //返回值現(xiàn)在棧頂
            int result = lua_tonumber(L, -1);

            result 就是函數(shù)的返回值

            完整的測(cè)試代碼如下:

            #include "lua.h"
            #include "lauxlib.h"
            #include "lualib.h"

            int main(int argc, char *argv[])
            {
            lua_State *L = lua_open();
            luaopen_base(L);

            const char *buf = "function main(number) number = number + 1 return number end";

            lua_dostring(buf);

            lua_getglobal(L, "main");
            lua_pushnumber(L, 100);
            lua_pcall(L, 1, 1, 0);

            int result = lua_tonumber(L, -1);

            assert(result == 101);

            lua_close(L);

            return 0;
            }





            在你的游戲中應(yīng)用Lua(2):擴(kuò)展Lua


            Lua本身定位在一種輕量級(jí)的,靈活的,可擴(kuò)充的腳本語(yǔ)言,這意味著你可以自由的擴(kuò)充Lua,為你自己的游戲量身定做一個(gè)腳本語(yǔ)言。

            你可以在主機(jī)程序中向腳本提供你自定的api,供腳本調(diào)用。

            Lua定義了一種類型:lua_CFunction,這是一個(gè)函數(shù)指針,它的原型是:
            typedef int (*lua_CFunction) (lua_State *L);

            這意味著只有這種類型的函數(shù)才能向Lua注冊(cè)。

            首先,我們定義一個(gè)函數(shù)

            int foo(lua_State *L)
            {
            //首先取出腳本執(zhí)行這個(gè)函數(shù)時(shí)壓入棧的參數(shù)
            //假設(shè)這個(gè)函數(shù)提供一個(gè)參數(shù),有兩個(gè)返回值

            //get the first parameter
            const char *par = lua_tostring(L, -1);

            printf("%s\n", par);

            //push the first result
            lua_pushnumber(L, 100);

            //push the second result
            lua_pushnumber(L, 200);

            //return 2 result
            return 2;
            }

            我們可以在腳本中這樣調(diào)用這個(gè)函數(shù)

            r1, r2 = foo("hello")

            print(r1..r2)

            完整的測(cè)試代碼如下:

            #include "lua.h"
            #include "lauxlib.h"
            #include "lualib.h"

            int foo(lua_State *L)
            {
            //首先取出腳本執(zhí)行這個(gè)函數(shù)時(shí)壓入棧的參數(shù)
            //假設(shè)這個(gè)函數(shù)提供一個(gè)參數(shù),有兩個(gè)返回值

            //get the first parameter
            const char *par = lua_tostring(L, -1);

            printf("%s\n", par);

            //push the first result
            lua_pushnumber(L, 100);

            //push the second result
            lua_pushnumber(L, 200);

            //return 2 result
            return 2;
            }

            int main(int argc, char *argv[])
            {
            lua_State *L = lua_open();
            luaopen_base(L);
            luaopen_io(L);

            const char *buf = "r1, r2 = foo("hello") print(r1..r2)";

            lua_dostring(L, buf);

            lua_close(L);

            return 0;
            }

            程序輸出:
            hello
            100200




            在你的游戲中應(yīng)用Lua(3):using lua in cpp


            lua和主機(jī)程序交換參數(shù)是通過(guò)一個(gè)運(yùn)行時(shí)棧來(lái)進(jìn)行的,運(yùn)行時(shí)棧的信息放在一個(gè)lua_State的結(jié)構(gòu)中,lua提供的api都需要一個(gè)lua_State*的指針,除了一個(gè):

            lua_open();

            這個(gè)函數(shù)將返回一個(gè)lua_State*型的指針,在你的游戲代碼中,你可以僅僅擁有一個(gè)這樣的指針,也可以有多個(gè)這樣的指針。

            最后,你需要釋放這個(gè)指針,通過(guò)函數(shù):

            lua_close(L);

            注意這個(gè)事實(shí),在你的主機(jī)程序中,open()與close()永遠(yuǎn)是成對(duì)出現(xiàn)的,在c++中,如果有一些事情是成對(duì)出現(xiàn)的,這通常意味著你需要一個(gè)構(gòu)造函數(shù)和一個(gè)析構(gòu)函數(shù),所以,我們首先對(duì)lua_State做一下封裝:

            #ifndef LUA_EXTRALIBS
            #define LUA_EXTRALIBS /* empty */
            #endif

            static const luaL_reg lualibs[] =
            {
            {"base", luaopen_base},
            {"table", luaopen_table},
            {"io", luaopen_io},
            {"string", luaopen_string},
            {"math", luaopen_math},
            {"debug", luaopen_debug},
            {"loadlib", luaopen_loadlib},
            /* add your libraries here */
            LUA_EXTRALIBS
            {NULL, NULL}
            };

            這是lua提供給用戶的一些輔助的lib,在使用lua_State的時(shí)候,你可以選擇打開(kāi)或者關(guān)閉它。

            完整的類實(shí)現(xiàn)如下:


            //lua_State
            class state
            {
            public:
            state(bool bOpenStdLib = false)
            :
            err_fn(0)
            {
            L = lua_open();

            assert(L);

            if (bOpenStdLib)
            {
            open_stdlib();
            }
            }

            ~state()
            {
            lua_setgcthreshold(L, 0);
            lua_close(L);
            }

            void open_stdlib()
            {
            assert(L);

            const luaL_reg *lib = lualibs;
            for (; lib->func; lib++)
            {
            lib->func(L); /* open library */
            lua_settop(L, 0); /* discard any results */
            }
            }

            lua_State* get_handle()
            {
            return L;
            }

            int error_fn()
            {
            return err_fn;
            }

            private:
            lua_State *L;

            int err_fn;
            };


            通常我們僅僅在游戲代碼中使用一個(gè)lua_State*的指針,所以我們?yōu)樗鼘?shí)現(xiàn)一個(gè)單件,默認(rèn)打開(kāi)所有l(wèi)ua提供的lib:


            //return the global instance
            state* lua_state()
            {
            static state L(true);

            return &L;
            }




            在你的游戲中應(yīng)用Lua(3):using lua in cpp(封裝棧操作) 

            前面提到了lua與主機(jī)程序是通過(guò)一個(gè)運(yùn)行時(shí)棧來(lái)交換信息的,所以我們把對(duì)棧的訪問(wèn)做一下簡(jiǎn)單的封裝。

            我們利用從c++的函數(shù)重載機(jī)制對(duì)這些操作做封裝,重載提供給我們一種以統(tǒng)一的方式來(lái)處理操作的機(jī)制。

            向lua傳遞信息是通過(guò)壓棧的操作來(lái)完成的,所以我們定義一些Push()函數(shù):

            inline void Push(lua_State *L, int value);
            inline void Push(lua_State *L, bool value);
            ...

            對(duì)應(yīng)簡(jiǎn)單的c++內(nèi)建類型,我們實(shí)現(xiàn)出相同的Push函數(shù),至于函數(shù)內(nèi)部的實(shí)現(xiàn)是非常的簡(jiǎn)單,只要利用lua提供的api來(lái)實(shí)現(xiàn)即可,例如:

            inline void Push(lua_State *L, int value)
            {
            lua_pushnumber(L, value);
            }

            這種方式帶來(lái)的好處是,在我們的代碼中我們可以以一種統(tǒng)一的方式來(lái)處理壓棧操作,如果有一種類型沒(méi)有定義相關(guān)的壓棧操作,將產(chǎn)生一個(gè)編譯期錯(cuò)誤。

            后面我會(huì)提到,如何將一個(gè)用戶自定義類型的指針傳遞到lua中,在那種情況下,我們的基本代碼無(wú)須改變,只要添加一個(gè)相應(yīng)的Push()函數(shù)即可。

            記住close-open原則吧,它的意思是對(duì)修改是封閉的,對(duì)擴(kuò)充是開(kāi)放的,好的類庫(kù)設(shè)計(jì)允許你擴(kuò)充它,而無(wú)須修改它的實(shí)現(xiàn),甚至無(wú)須重新編譯。

            《c++泛型設(shè)計(jì)新思維》一書提到了一種技術(shù)叫type2type,它的本質(zhì)是很簡(jiǎn)單:

            template <typename T>
            struct type2type
            {
            typedef T U;
            };

            正如你看到的,它并沒(méi)有任何數(shù)據(jù)成員,它的存在只是為了攜帶類型信息。

            類型到類型的映射在應(yīng)用于重載函數(shù)時(shí)是非常有用的,應(yīng)用type2type,可以實(shí)現(xiàn)編譯期的分派。

            下面看看我們?nèi)绾卧趶臈V腥〉胠ua信息時(shí)應(yīng)用type2type:

            測(cè)試類型:由于lua的類型系統(tǒng)與c++是不相同的,所以,我們要對(duì)棧中的信息做一下類型檢測(cè)。

            inline bool Match(type2type<bool>, lua_State *L, int idx)
            {
            return lua_type(L, idx) == LUA_TBOOLEAN;
            }

            類似的,我們要為cpp的內(nèi)建類型提供相應(yīng)的Match函數(shù):

            inline bool Match(type2type<int>, lua_State *L, int idx);
            inline bool Match(type2type<const char*>, lua_State *L, int idx);

            ...

            可以看出,type2type的存在只是為了在調(diào)用Match時(shí)決議到正確的函數(shù)上,由于它沒(méi)有任何成員,所以不存在運(yùn)行時(shí)的成本。

            同樣,我們?yōu)閏pp內(nèi)建類型提供Get()函數(shù):

            inline bool Get(type2type<bool>, lua_State *L, int idx)
            {
            return lua_toboolean(L, idx);
            }

            inline int Get(type2type<int>, lua_State *L, int idx)
            {
            return static_cast<int>(lua_tonumber(L, idx));
            }

            ...

            我想你可能注意到了,在int Get(type2type<int>)中有一個(gè)轉(zhuǎn)型的動(dòng)作,由于lua的類型系統(tǒng)與cpp的類型不同,所以轉(zhuǎn)型動(dòng)作必須的。

            除此之外,在Get重載函數(shù)(s)中還有一個(gè)小小的細(xì)節(jié),每個(gè)Get的函數(shù)的返回值是不相同的,因?yàn)橹剌d機(jī)制是依靠參數(shù)的不同來(lái)識(shí)別的,而不是返回值。

            前面說(shuō)的都是一些基礎(chǔ)的封裝,下來(lái)我們將介紹如何向lua注冊(cè)一個(gè)多參數(shù)的c函數(shù)。還記得嗎?利用lua的api只能注冊(cè)int (*ua_CFunction)(lua_State *)型的c函數(shù),別忘記了,lua是用c寫的。




            在你的游戲中應(yīng)用Lua(3):using lua in cpp(注冊(cè)不同類型的c函數(shù))之一 

            前面說(shuō)到,我們可以利用lua提供的api,向腳本提供我們自己的函數(shù),在lua中,只有l(wèi)ua_CFunction類型的函數(shù)才能直接向lua注冊(cè),lua_CFunction實(shí)際上是一個(gè)函數(shù)指針:
            typedef int (*lua_CFunction)(lua_State *L);

            而在實(shí)際的應(yīng)用中,我們可能需要向lua注冊(cè)各種參數(shù)和返回值類型的函數(shù),例如,提供一個(gè)add腳本函數(shù),返回兩個(gè)值的和:

            int add(int x, int y);

            為了實(shí)現(xiàn)這個(gè)目的,首先,我們定義個(gè)lua_CFunction類型的函數(shù):

            int add_proxy(lua_State *L)
            {
            //取得參數(shù)
            if (!Match(TypeWrapper<int>(), L, -1))
            return 0;
            if (!Match(TypeWrapper<int>(), L, -2))
            return 0;

              int x = Get(TypeWrapper<int>(), L, -1);
              int y = Get(TypeWrapper<int>(), L, -1);
             
              //調(diào)用真正的函數(shù)
              int result = add(x, y);
             
              //返回結(jié)果
              Push(result);
             
              return 1;
            }

            現(xiàn)在,我們可以向lua注冊(cè)這個(gè)函數(shù):

            lua_pushstring(L, “add”);
            lua_pushcclosure(L, add_proxy, 0);
            lua_settable(L, LUA_GLOBALINDEX);

            在腳本中可以這樣調(diào)用這個(gè)函數(shù):

            print(add(100, 200))

            從上面的步驟可以看出,如果需要向lua注冊(cè)一個(gè)非lua_CFunction類型的函數(shù),需要:
            1. 為該函數(shù)實(shí)現(xiàn)一個(gè)封裝調(diào)用。
            2. 在封裝調(diào)用函數(shù)中從lua棧中取得提供的參數(shù)。
            3. 使用參數(shù)調(diào)用該函數(shù)。
            4. 向lua傳遞其結(jié)果。

            注意,我們目前只是針對(duì)全局c函數(shù),類的成員函數(shù)暫時(shí)不涉及,在cpp中,類的靜態(tài)成員函數(shù)與c函數(shù)類似。

            假設(shè)我們有多個(gè)非lua_CFunction類型的函數(shù)向lua注冊(cè),我們需要為每一個(gè)函數(shù)重復(fù)上面的步驟,產(chǎn)生一個(gè)封裝調(diào)用,可以看出,這些步驟大多是機(jī)械的,因此,我們需要一種方式自動(dòng)的實(shí)現(xiàn)上面的步驟。

            首先看步驟1,在cpp中,產(chǎn)生這樣一個(gè)封裝調(diào)用的函數(shù)的最佳的方式是使用template,我們需要提供一個(gè)lua_CFunction類型的模板函數(shù),在這個(gè)函數(shù)中調(diào)用真正的向腳本注冊(cè)的函數(shù),類似于這樣:
            template <typename Func>
            inline int register_proxy(lua_State *L)

            現(xiàn)在的問(wèn)題在于:我們要在這個(gè)函數(shù)中調(diào)用真正的函數(shù),那么我們必須要在這個(gè)函數(shù)中取得一個(gè)函數(shù)指針,然而,lua_CFunction類型的函數(shù)又不允許你在增加別的參數(shù)來(lái)提供這個(gè)函數(shù)指針,現(xiàn)在該怎么讓regisger_proxy函數(shù)知道我們真正要注冊(cè)的函數(shù)呢?

            在oop中,似乎可以使用類來(lái)解決這個(gè)問(wèn)題:

            template <Func>
            struct register_helper
            {
            explicit register_helper(Func fn) : m_func(fn)
            {}
            int register_proxy(lua_State *L);

            protected:
            Func m_func;
            };

            可是不要忘記,lua_CFunction類型指向的是一個(gè)c函數(shù),而不是一個(gè)成員函數(shù),他們的調(diào)用方式是不一樣的,如果將上面的int register_proxy()設(shè)置為靜態(tài)成員函數(shù)也不行,因?yàn)槲覀冃枰L問(wèn)類的成員變量m_func;

            讓我們?cè)儆^察一下lua_CFunction類型的函數(shù):

            int register_proxy(lua_State *L);

            我們看到,這里面有一個(gè)lua_State*型的指針,我們能不能將真正的函數(shù)指針?lè)诺竭@里面存儲(chǔ),到真正調(diào)用的時(shí)候,再?gòu)睦锩嫒〕鰜?lái)呢?

            Lua提供了一個(gè)api可以存儲(chǔ)用戶數(shù)據(jù):
            Lua_newuserdata(L, size)

            在適當(dāng)?shù)臅r(shí)刻,我們可以再取出這個(gè)數(shù)據(jù):

            lua_touserdata(L, idx)

            ok,現(xiàn)在傳遞函數(shù)指針的問(wèn)題我們已經(jīng)解決了,后面再看第二步:取得參數(shù)。





            在你的游戲中應(yīng)用Lua(3):using lua in cpp(注冊(cè)不同類型的c函數(shù))之二

            在解決了傳遞函數(shù)指針的問(wèn)題之后,讓我們來(lái)看看調(diào)用函數(shù)時(shí)會(huì)有一些什么樣的問(wèn)題。

            首先,當(dāng)我們通過(guò)函數(shù)指針調(diào)用這個(gè)函數(shù)的時(shí)候,由于我們面對(duì)的是未知類型的函數(shù),也就是說(shuō),我們并不知道參數(shù)的個(gè)數(shù),參數(shù)的類型,還有返回值的類型,所以我們不能直接從lua棧中取得參數(shù),當(dāng)然,我們可以通過(guò)運(yùn)行時(shí)測(cè)試棧中的信息來(lái)得到lua傳遞進(jìn)來(lái)的參數(shù)的個(gè)數(shù)和類型,這意味著我們?cè)谏院笸ㄟ^(guò)函數(shù)指針調(diào)用函數(shù)時(shí)也需要?jiǎng)討B(tài)的根據(jù)參數(shù)的個(gè)數(shù)和類型來(lái)決議到正確的函數(shù),這樣,除了運(yùn)行時(shí)的成本,cpp提供給我們的強(qiáng)類型檢查機(jī)制的好處也剩不了多少了,我們需要的是一種靜態(tài)的編譯時(shí)的“多態(tài)”。

            在cpp中,至少有兩種方法可以實(shí)現(xiàn)這點(diǎn)。最直接簡(jiǎn)單的是使用函數(shù)重載,還有一種是利用模板特化機(jī)制。

            簡(jiǎn)單的介紹一下模板特化:

            在cpp中,可以針對(duì)一個(gè)模板函數(shù)或者模板類寫出一些特化版本,編譯器在匹配模板參數(shù)時(shí)會(huì)尋找最合適的一個(gè)版本。類似于這樣:

            templat <typename T>
            T foo()
            {
            T tmp();
            return tmp;
            }

            //提供特化版本
            template <>
            int foo()
            {
            return 100;
            }

            在main()函數(shù)中,我們可以顯示指定使用哪個(gè)版本的foo:

            int main(int argc, char **argv)
            {
            cout << foo<int>() << endl;
            return 0;
            }

            程序?qū)⑤敵?00,而不是0,以上代碼在 g++中編譯通過(guò),由于vc6對(duì)于模板的支持不是很好,所以有一些模板的技術(shù)在vc6中可能不能編譯通過(guò)。

            所以最好使用重載來(lái)解決這個(gè)問(wèn)題,在封裝函數(shù)調(diào)用中,我們首先取得這個(gè)函數(shù)指針,然后,我們要提供一個(gè)Call函數(shù)來(lái)真正調(diào)用這個(gè)函數(shù),類似于這樣:
            //偽代碼
            int Call(pfn, lua_State *L, int idx)

            可是我們并不知道這個(gè)函數(shù)指針的類型,現(xiàn)在該怎么寫呢?別忘記了,我們的register_proxy()是一個(gè)模板函數(shù),它有一個(gè)參數(shù)表示了這個(gè)指針的類型:

            template <typename Func>
            int register_proxy(lua_State *L)
            {
            //偽代碼,通過(guò)L參數(shù)取得這個(gè)指針
            unsigned char *buffer = get_pointer(L);

            //對(duì)這個(gè)指針做強(qiáng)制類型轉(zhuǎn)化,調(diào)用Call函數(shù)
            return Call(*(Func*)buffer, L, 1);
            }

            由重載函數(shù)Call調(diào)用真正的函數(shù),這樣,我們可以使用lua api注冊(cè)相關(guān)的函數(shù),下來(lái)我們提供一個(gè)注冊(cè)的函數(shù):

            template <typename Func>
            void lua_pushdirectclosure(Func fn, lua_State *L, int nUpvalue)
            {
            //偽代碼,向L存儲(chǔ)函數(shù)指針
            save_pointer(L);

            //向lua提供我們的register_proxy函數(shù)
            lua_pushcclosure(L, register_proxy<Func>, nUpvalue + 1);
            }

            再定義相關(guān)的注冊(cè)宏:
            #define lua_register_directclosure(L, func) \
            lua_pushstring(L, #func);
            lua_pushdirectclosure(func, L, 1);
            lua_settable(L, LUA_GLOBALINDEX)

            現(xiàn)在,假設(shè)我們有一個(gè)int add(int x, int y)這樣的函數(shù),我們可以直接向lua注冊(cè):

            lua_register_directclosure(L, add);

            看,最后使用起來(lái)很方便吧,我們?cè)僖膊挥檬謱懩敲炊嗟姆庋b調(diào)用的代碼啦,不過(guò)問(wèn)題還沒(méi)有完,后面我們還得解決Call函數(shù)的問(wèn)題。




            在你的游戲中應(yīng)用Lua(3):using lua in cpp(注冊(cè)不同類型的c函數(shù))之三 


            下面,讓我們集中精力來(lái)解決Call重載函數(shù)的問(wèn)題吧。

            前面已經(jīng)說(shuō)過(guò)來(lái),Call重載函數(shù)接受一個(gè)函數(shù)指針,然后從lua棧中根據(jù)函數(shù)指針的類型,取得相關(guān)的參數(shù),并調(diào)用這個(gè)函數(shù),然后將返回值壓入lua棧,類似于這樣:

            //偽代碼
            int Call(pfn, lua_State *L, int idx)

            現(xiàn)在的問(wèn)題是pfn該如何聲明?我們知道這是一個(gè)函數(shù)指針,然而其參數(shù),以及返回值都是未知的類型,如果我們知道返回值和參數(shù)的類型,我們可以用一個(gè)typedef來(lái)聲明它:

            typedef void (*pfn)();

            int Call(pfn fn, lua_State *L, int idx);

            我們知道的返回值以及參數(shù)的類型只是一個(gè)模板參數(shù)T,在cpp中,我們不能這樣寫:

            template <typename T>
            typedef T (*Func) ();

            一種解決辦法是使用類模板:

            template <typename T>
            struct CallHelper
            {
            typedef T (*Func) ();
            };

            然后在Call中引用它:

            template <typename T>
            int Call(typename CallHelper::Func fn, lua_State *L, int idx)

            注意typename關(guān)鍵字,如果沒(méi)有這個(gè)關(guān)鍵字,在g++中會(huì)產(chǎn)生一個(gè)編譯警告,它的意思是告訴編譯器,CallHelper::Func是一個(gè)類型,而不是變量。

            如果我們這樣來(lái)解決,就需要在CallHelper中為每種情況大量定義各種類型的函數(shù)指針,還有一種方法,寫法比較古怪,考慮一個(gè)函數(shù)中參數(shù)的聲明:

            void (int n);

            首先是類型,然后是變量,而應(yīng)用于函數(shù)指針上:

            typedef void (*pfn) ();
            void (pfn fn);

            事實(shí)上,可以將typedef直接在參數(shù)表中寫出來(lái):

            void (void (*pfn)() );

            這樣,我們的Call函數(shù)可以直接這樣寫:

            //針對(duì)沒(méi)有參數(shù)的Call函數(shù)
            template <typename RT>
            int Call(RT (*Func) () , lua_State *L, int idx);
            {
            //調(diào)用Func
            RT ret = (*Func)();

            //將返回值交給lua
            Push(L, ret);

            //告訴lua有多少個(gè)返回值
            return 1;
            }

            //針對(duì)有一個(gè)參數(shù)的Call
            template <typename T, typename P1>
            int Call(RT (*Func)(), lua_State *L, int idx)
            {
            //從lua中取得參數(shù)
            if (!Match(TypeWrapper<P1>(), L, -1)
            return 0;

            RT ret = (*Func) (Get(TypeWrapper<P1>(), L, -1));

            Push(L, ret);
            return 1;
            }

            按照上面的寫法,我們可以提供任意參數(shù)個(gè)數(shù)的Call函數(shù),現(xiàn)在回到最初的時(shí)候,我們的函數(shù)指針要通過(guò)lua_State *L來(lái)存儲(chǔ),這只要利用lua提供的api就可以了,還記得我們的lua_pushdirectclosure函數(shù)嗎:

            template <typename Func>
            void lua_pushdirectclosure(Func fn, lua_State *L, int nUpvalue)
            {
            //偽代碼,向L存儲(chǔ)函數(shù)指針
            save_pointer(L);

            //向lua提供我們的register_proxy函數(shù)
            lua_pushcclosure(L, register_proxy<Func>, nUpvalue + 1);
            }

            其中,save_pointer(L)可以這樣實(shí)現(xiàn):

            void save_pointer(lua_State *L)
            {
            unsigned char* buffer = (unsigned char*)lua_newuserdata(L, sizeof(func));
            memcpy(buffer, &func, sizeof(func));
            }


            而在register_proxy函數(shù)中:

            template <typename Func>
            int register_proxy(lua_State *L)
            {
            //偽代碼,通過(guò)L參數(shù)取得這個(gè)指針
            unsigned char *buffer = get_pointer(L);
            //對(duì)這個(gè)指針做強(qiáng)制類型轉(zhuǎn)化,調(diào)用Call函數(shù)
            return Call(*(Func*)buffer, L, 1);
            }
            get_pointer函數(shù)可以這樣實(shí)現(xiàn):

            unsigned char* get_pointer(lua_State *L)
            {
              return (unsigned char*) lua_touserdata(L, lua_upvalueindex(1));
            }

            這一點(diǎn)能夠有效運(yùn)作主要依賴于這樣一個(gè)事實(shí):

            我們?cè)趌ua棧中保存這個(gè)指針之后,在沒(méi)有對(duì)棧做任何操作的情況下,又把它從棧中取了出來(lái),所以不會(huì)弄亂lua棧中的信息,記住,lua棧中的數(shù)據(jù)是由用戶保證來(lái)清空的。

            到現(xiàn)在,我們已經(jīng)可以向lua注冊(cè)任意個(gè)參數(shù)的c函數(shù)了,只需簡(jiǎn)單的一行代碼:

            lua_register_directclosure(L, func)就可以啦。







            在你的游戲中應(yīng)用Lua(3):Using Lua in cpp(基本數(shù)據(jù)類型、指針和引用)之一

            Using Lua in cpp(基本數(shù)據(jù)類型、指針和引用)

            前面介紹的都是針對(duì)cpp中的內(nèi)建基本數(shù)據(jù)類型,然而,即使是這樣,在面對(duì)指針和引用的時(shí)候,情況也會(huì)變得復(fù)雜起來(lái)。

            使用前面我們已經(jīng)完成的宏lua_register_directclosure只能注冊(cè)by value形式的參數(shù)的函數(shù),當(dāng)參數(shù)中存在指針和引用的時(shí)候(再?gòu)?qiáng)調(diào)一次,目前只針對(duì)基本數(shù)據(jù)類型):

            1、 如果是一個(gè)指針,通常實(shí)現(xiàn)函數(shù)的意圖是以這個(gè)指針傳遞出一個(gè)結(jié)果來(lái)。
            2、 如果是一個(gè)引用,同上。
            3、 如果是一個(gè)const指針,通常只有面對(duì)char*的時(shí)候才使用const,實(shí)現(xiàn)函數(shù)的意圖是,不會(huì)改變這個(gè)參數(shù)的內(nèi)容。其它情況一般都避免出現(xiàn)使用const指針。
            4、 如果是一個(gè)const引用,對(duì)于基本數(shù)據(jù)類型來(lái)說(shuō),一般都避免出現(xiàn)這種情況。

            Lua和cpp都允許函數(shù)用某種方式返回多個(gè)值,對(duì)于cpp來(lái)說(shuō),多個(gè)返回值是通過(guò)上述的第1和第2種情況返回的,對(duì)于lua來(lái)說(shuō),多個(gè)返回值可以直接返回:

            --in Lua
            function swap(x, y)
            tmp = x
            x = y
            y = tmp

            return x, y
            end

            x = 100
            y = 200

            x, y = swap(x, y)

            print(x..y)

            程序輸出:200100

            同樣的,在主機(jī)程序中,我們也可以向Lua返回多個(gè)值:

            int swap(lua_State *L)
            {
            //取得兩個(gè)參數(shù)
            int x = Get(TypeWrapper<int>(), L, -1);
            int y = Get(TypeWrapper<int>(), L, -2);

            //交換值
            int tmp = x;
            x = y;
            y = tmp;

            //向Lua返回值
            Push(L, x);
            Push(L, y);

              //告訴Lua我們返回了多少個(gè)值
            return 2;
            }

            現(xiàn)在我們可以在Lua中這樣調(diào)用這個(gè)函數(shù):

            x = 100
            y = 200

            x, y = swap(x, y)

            在我們的register_proxy函數(shù)中只能對(duì)基本數(shù)據(jù)類型的by value方式有效,根據(jù)我們上面的分析,如果我們能夠在編譯期知道,對(duì)于一個(gè)模板參數(shù)T:
            1、 這是一個(gè)基本的數(shù)據(jù)類型,還是一個(gè)用戶自定義的數(shù)據(jù)類型?
            2、 這是一個(gè)普通的指針,還是一個(gè)iterator?
            3、 這是一個(gè)引用嗎?
            4、 這是一個(gè)const 普通指針嗎?
            5、 這是一個(gè)const 引用嗎?

            如果我們能知道這些,那么,根據(jù)我們上面的分析,我們希望:(只針對(duì)基本數(shù)據(jù)類型)
            1、 如果這是一個(gè)指針,我們希望把指針?biāo)傅膬?nèi)容返回給Lua。
            2、 如果這是一個(gè)引用,我們希望把引用的指返回給Lua。
            3、 如果這是const指針,我們希望將從Lua棧中取得的參數(shù)傳遞
                      給調(diào)用函數(shù)。
            4、 如果這是一個(gè)const引用,我們也希望把從Lua棧中取得的參
                      數(shù)傳遞給調(diào)用函數(shù)。



            Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=772361

            posted on 2008-01-11 19:14 chib 閱讀(5250) 評(píng)論(0)  編輯 收藏 引用


            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            導(dǎo)航

            統(tǒng)計(jì)

            常用鏈接

            留言簿(1)

            隨筆檔案

            牛人錄

            時(shí)政史料

            投資管理

            源碼庫(kù)

            搜索

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            性高湖久久久久久久久| 国产精品久久波多野结衣| 久久SE精品一区二区| 亚洲欧美日韩久久精品第一区| 久久青青草原亚洲av无码app | 国产一区二区三精品久久久无广告| 久久国产色AV免费观看| 四虎国产永久免费久久| 97精品伊人久久大香线蕉| 久久久久精品国产亚洲AV无码| 久久91精品国产91久久小草| 久久综合色之久久综合| 久久亚洲精品国产精品| 久久se精品一区二区影院| 久久久久亚洲Av无码专| 久久成人国产精品免费软件| 丁香五月网久久综合| 久久亚洲精品国产精品婷婷| 狠狠色丁香婷综合久久| 久久热这里只有精品在线观看| 日韩欧美亚洲综合久久影院d3| 久久精品国产2020| 女同久久| 天天综合久久久网| 伊人久久大香线焦AV综合影院| 九九久久精品国产| 国产精品久久国产精品99盘 | 综合久久国产九一剧情麻豆| 国产精品九九久久精品女同亚洲欧美日韩综合区 | 国产情侣久久久久aⅴ免费| 一级做a爰片久久毛片人呢| 亚洲国产另类久久久精品黑人| 久久97久久97精品免视看秋霞| 国产精品久久久久久吹潮| 日日噜噜夜夜狠狠久久丁香五月| 久久精品无码免费不卡| 国产精品久久久久久| 精品一二三区久久aaa片| 久久国产精品一区| 久久精品一区二区| 久久超乳爆乳中文字幕|