LUA整合進MFC代碼

來源:互聯網
上載者:User
這幾天研究了一下lua,主要關注的是lua和vc之間的整合,把代碼都寫好放在VC宿主程式裡,然後在lua裡調用宿主程式的這些代碼(或者叫介面、組件,隨便你怎麼叫),希望能用指令碼來控制主程式的行為。這實際上也是一種把業務分離,用指令碼控制的架構,可能有些人把這種指令碼叫做業務引擎,工作流程等。為什麼選擇lua?因為它是一個能和C/C++結合得很緊的指令碼語言,而我們的程式是用VC++ 寫的;另外一點是因為它的名氣,連WOW都用lua來提供API讓玩家修改其遊戲行為,那我是找不到什麼理由拒絕它了。Lua是什嗎?在哪裡擷取LUA?詳細的不說了,在網上一搜大把,只說一下它的官網吧:www.lua.org,在這裡可以查到lua的應用,lua發布的版本,我用的是5.1.4,下載的是原始碼的版本。LUA和VC MFC的整合?1、 包含LUA:要使用LUA,當然要先把它包含進我們的工程裡,可以有lib/dll方式、也可以用靜態lib方式,當然也可以把整個lua的代碼放進我們的工程,然後編譯,因為lua只有幾百K,很小。。。包含整個代碼的方法我說一下:
a) 在VC MFC建立一個工程(例如Dialog base工程)
b) 去到工程裡的檔案tab頁,建立一個檔案夾,然後把所有lua裡的.c、.h檔案包含進來,注意有幾個不用包含,lua.c、wmain.c、luac.c,包含進來之後,選中這個檔案夾下面的所有.c檔案,然後右鍵選setting,選擇Not using precompiler file,在VS2008中,右鍵.CPP檔案,--->屬性---->C/C++----->先行編譯頭----->建立/使用先行編譯頭----->選擇 :不使用先行編譯頭 這步做完就馬上編譯一下,應該是沒問題的了!
c) 還有動態庫和靜態lib兩種方式把lua包含進工程裡的,自己可以嘗試一下。
2、 在某個地方(我是在MFCLua1Dlg.cpp檔案開始處)包含lua的標頭檔,並聲明一個lua_state指標,如下:
extern "C"{#include "lua.h"#include "lualib.h"#include "lauxlib.h"}lua_State *lua; 在某個適當的地方(我是在OnInitDialog裡)調用下面一段代碼,這段代碼的作用是開啟一些必要的庫:       lua = lua_open ();         if(lua)       {              luaopen_base (lua);              luaopen_table (lua);              luaopen_string (lua);              luaopen_math (lua);              luaopen_debug (lua);              //luaopen_io (lua);       }用完lua的時候,調用下面一句來關閉lua庫:lua_close (lua);              好了,到現在為止,lua已經完全變成我們程式的一部分了,試著編譯一下,看看能不能順利通過。。。LUA和MFC的互動?Lua變成我們程式的一部分之後,我們還要使用它,要記住我們的目標是用指令碼程式控制我們宿主程式的執行流程,那我們就要完成兩步,一是用mfc程式調用lua的函數,二是用lua調用mfc的函數,下面的內容對於初學者可能會開始有點難理解了,請打醒十二分精神,我會盡量簡單的說。。。1、 mfc調用lua的函數,這裡用到一個StackDump的函數,是關於主程式和lua的互動棧的問題,下面會對互動棧的問題專門說明。首先我們用記事本建立一個test.lua,內容是一個相加函數:function add ( x, y ) return x + y;end然後再VC裡調用它,如下的一段代碼,看這段代碼的時候,先把StackDump函數忽略,只需要知道它是一個輸出lua和vc互動棧內容的函數,對了,你可以建立一個button的click函數,然後把這段代碼放進去:StackDump(lua);    luaL_dofile(lua, "test.lua");     // 解釋分析lua檔案StackDump(lua);lua_getglobal(lua, "add");       // 取到一個全域標號add,取的同時會把add函數壓棧StackDump(lua);    lua_pushnumber(lua, 1);        // 把第一個參數壓入棧裡StackDump(lua);lua_pushnumber(lua, 2);        // 第二個參數壓棧StackDump(lua);//lua_call(lua, 2, 1); if(lua_pcall(lua, 2, 1, 0) != 0)        // 執行add函數{        AfxMessageBox("lua_pcall error!");             return;}StackDump(lua);int d = (int)lua_tonumber(lua, -1);        // 函數執行完了,執行結果被壓棧,所以取得最頂端的一個數就是結果值,-1就是指取棧頂的值CString str;str.Format("%d", d);AfxMessageBox(str);StackDump(lua);lua_pop(lua, 1);      // 把值從棧裡清除,pop(彈出)一個值StackDump(lua);好好分析一下這段代碼,我們大概知道調用lua函數的一個過程是:dofile--〉函數名壓棧--〉參數依序壓棧--〉lua_pcall執行(執行結果壓棧)--〉取出執行結果(如果有多個,就從棧裡取出多個。。。),這樣我們就能很輕鬆的調用到lua裡的函數,其實就是要知道棧裡發生了什麼。。。2、 lua調用MFC函數,比如我們想在lua裡調用一個Msg函數,能彈出一個視窗來顯示我們想顯示的字串,然後傳回值是1個"MsgOK!"字串。lua檔案是這樣的,第一句是調用Msg函數,第二句是測試返回的字串是不是"MsgOK!":c = Msg ("123");Msg(c);MFC程式裡是這樣的:首先寫一個將要被匯出的函數,很多文章叫它為粘合函數(glue function):static int Msg(lua_State* L){const char *s1 = luaL_checkstring(L, 1);     // 測試第一個參數是否為字串形式,並取得這個字串StackDump(L);MessageBox(NULL, s1, "caption", MB_OK);lua_pop(lua, 1);      // 清除棧裡的這個字串StackDump(L);lua_pushlstring(L, "MsgOK!", 6);  // 把傳回值壓進棧裡// 這個返回是指傳回值的個數return 1;}       然後就匯出這個函數,如下:       lua_pushcfunction(lua, Msg);       lua_setglobal(lua, "Msg");       接著就執行剛才的lua檔案就行了,記得執行之前要先lua_open () 哦:       luaL_dofile(lua, "test.lua");       啟動並執行結果就是連續跳出兩個messagebox,第一個是123,第二個是"MsgOK!",說明我們返回的字串被lua接收到了,lua的第二行我們沒有接收它的傳回值,則這個傳回值會自動被拋棄了。       如果需要多傳回值,則我們要把下面一句:lua_pushlstring(L, "MsgOK!", 6);  // 把傳回值壓進棧裡// 這個返回是指傳回值的個數return 1;改為:lua_pushlstring(L, "MsgOK!", 6);  // 把傳回值壓進棧裡lua_pushlstring(L, "haha!", 5);      // 把傳回值壓進棧裡// 這個返回是指傳回值的個數return 2;這樣我們在lua檔案裡就可以像下面一樣取得兩個傳回值了:c,d = Msg("123");那c和d就分別是"MsgOK!"和"haha!"兩個字串了。 這種自動機制用起來還是比較方便的。3、互動棧上面兩個調用其實都是對lua棧的實用,那我們就要好好理解一個概念,lua和vc的互動棧(棧是什嗎?請參考資料結構的書哈。。。)lua和vc就是通過這個棧來實現互動的,這個棧的訪問函數有lua_gettop,lua_settop,lua_tostring,lua_toXXX等等的函數,我們要清楚當一個函數調用發生的時候,棧裡是發生了什麼。上面我用了一個StackDump函數,當我們調用的時候,能很清楚的看到棧裡發生了什麼。       首先我們要知道從棧頂往下數就是-1、-2,從棧底往上數就是1、2。如果使用lua_gettop(L, 1),就是取得棧底第一個元素。lua_gettop(L, -1)就是取得棧頂的第一個元素。lua_pop() (L, 1)就是把棧頂的一個元素彈出來,lua_pop()(L, 2)就是把棧頂的兩個元素彈出。好了,寫了一通,最後是這個StackDump函數的實現:int StackDump(lua_State* L){       int nTop = lua_gettop(L); //得到棧的元素個數。棧頂的位置。       OutputDebugString("The Length of stack is %d/n", nTop); //輸出棧頂位置       for (int i = 1; i <= nTop; ++i)       {              int t = lua_type(L, i);              OutputDebugString("%s:", lua_typename(L, t)); //這裡的typename是把類型的枚舉變成字串,是類型名。不是棧中的位置。              switch(t)              {              case LUA_TNUMBER:                     OutputDebugString("%f", lua_tonumber(L, i));                     break;              case LUA_TSTRING:                     OutputDebugString("%s", lua_tostring(L, i));                     break;              case LUA_TTABLE:                     //OutputDebugString("%s/n", lua_tostring(L,i));                     break;              case LUA_TFUNCTION:                     //OutputDebugString("%s/n", lua_tostring(L,i));                     break;              case LUA_TNIL:                     OutputDebugString("Is NULL");                     break;              case LUA_TBOOLEAN:                     OutputDebugString("%s", lua_toboolean(L, i) ? "true" : "false");                     break;              default:                     break;              }              OutputDebugString("/n");       }       return 0;}本篇文章主要講了lua和VC的整合、把LUA原始碼和VC工程一起編譯,VC調用LUA的代碼,LUA調用VC的代碼,傳回值以及多個傳回值、互動棧、輸出互動棧裡的元素資訊等內容,下一篇將會說說如何避免阻塞的指令碼,lua和多線程的使用等內容。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.