<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 綁定Lua和C/C++的庫 * [CPPlua](http://sourceforge.net/projects/cpplua/) * [tolua](http://www.tecgraf.puc-rio.br/~celes/tolua/) * [tolua++](http://www.codenix.com/~tolua/) * [luawrapper](http://www.d2-life.com/LBS/blogview.asp?logID=41) * [luabind](http://luabind.sourceforge.net/) * [luaplus](http://luaplus.org/) ## Lua調用C/C++ ### 簡介 Lua(念“魯啊”)作為一門發展成熟的腳本語言,正在變得越來越流行。它也可以作為和C/C++執行腳本交互的語言。并且Lua的整個庫很小,Lua 5.1版本整個靜態鏈接的lua.dll才164KB,所以Lua很輕量,特別適合輕量級腳本嵌入。 這節要講Lua和C/C++的交互——Lua通過C/C++導出的dll來調用。 **LUA調用C文件中的函數方法** * C中注冊函數 ~~~ lua_pushcfunction(l, l_sin); //注冊在lua中使用的c函數l_sin lua_setglobal(l, "mysin"); //設定綁定到lua中的名字為mysin ~~~ * C中提供的函數其定義要符合: ~~~ typedef int function(lua_State *L) ~~~ ### [](https://github.com/andycai/luaprimer/blob/master/08.md#準備工作)準備工作 安裝完Lua,需要在Visual Studio中配置Lua路徑,使得你的編譯器能搜尋到。關于VS2010的配置,見我的博文《VS2010 C++目錄配置》一文。完成后新建一個Dll工程便可以了。 我們用一個在Lua中顯示Windows對話框的程序來簡要介紹一下,程序雖小,但五臟俱全。程序如下: ~~~ // 將一些有用的Win32特性導出 // 以便在Lua中使用 extern "C" { #include <lua.h> #include <lualib.h> #include <lauxlib.h> #pragma comment(lib, "lua.lib") }; #include <Windows.h> #include <iostream> using namespace std; static const char* const ERROR_ARGUMENT_COUNT = "參數數目錯誤!"; static const char* const ERROR_ARGUMENT_TYPE = "參數類型錯誤!"; // 發生錯誤,報告錯誤 void ErrorMsg(lua_State* luaEnv, const char* const pszErrorInfo) { lua_pushstring(luaEnv, pszErrorInfo); lua_error(luaEnv); } // 檢測函數調用參數個數是否正常 void CheckParamCount(lua_State* luaEnv, int paramCount) { // lua_gettop獲取棧中元素個數. if (lua_gettop(luaEnv) != paramCount) { ErrorMsg(luaEnv, ERROR_ARGUMENT_COUNT); } } // 顯示Windows對話框. // @param [in] pszMessage string 1 // @param [in] pszCaption string 2 extern "C" int ShowMsgBox(lua_State* luaEnv) { const char* pszMessage = 0; const char* pszCaption = 0; // 檢測參數個數是否正確. CheckParamCount(luaEnv, 2); // 提取參數. pszMessage = luaL_checkstring(luaEnv, 1); pszCaption = luaL_checkstring(luaEnv, 2); if (pszCaption && pszMessage) { ::MessageBox( NULL, pszMessage, pszCaption, MB_OK | MB_ICONINFORMATION ); } else { ErrorMsg(luaEnv, ERROR_ARGUMENT_TYPE); } // 返回值個數為0個. return 0; } // 導出函數列表. static luaL_Reg luaLibs[] = { {"ShowMsgBox", ShowMsgBox}, {NULL, NULL} }; // Dll入口函數,Lua調用此Dll的入口函數. extern "C" __declspec(dllexport) int luaopen_WinFeature(lua_State* luaEnv) { const char* const LIBRARY_NAME = "WinFeature"; luaL_register(luaEnv, LIBRARY_NAME, luaLibs); return 1; } ~~~ ### [](https://github.com/andycai/luaprimer/blob/master/08.md#程序解析)程序解析 首先我們包含Lua的頭文件,并鏈入庫文件。注意:Lua的頭文件為C風格,所以用external “C”來含入。在此例中,我們最終的導出函數為“ShowMsgBox”。 每一個導出函數的格式都為: ~~~ extern “C”int Export_Proc_Name(luaState* luaEnv); ~~~ 其中,luaState*所指的結構中包含了Lua調用此Dll時必備的Lua環境。那么Lua向函數傳遞參數該怎么辦呢?實際上是用luaL_check[type]函數來完成的。如下: ~~~ const char* pHelloStr = luaL_checkstring(luaEnv, 1); double value = luaL_checknumber(luaEnv, 2); int ivalue = luaL_checkint(luaEnv, 3); ~~~ luaL_check系列函數的第二個參數是Lua調用該函數時傳遞參數從坐到右的順序(從1開始)。 然后我們看到,static的一個luaL_Reg的結構數組中包含了所有要導出的函數列表。最后通過luaopen_YourDllName的一個導出函數來完成一系列操作。YourDllName就是你最終的Dll的名字(不含擴展名)。因為你在Lua中調用此Dll時,Lua會根據此Dll名字找luaopen_YourDllName對應的函數,然后從此函數加載該Dll。 Dll入口函數格式如下: ~~~ extern "C" __declspec(dllexport) int luaopen_WinFeature(lua_State* luaEnv) { const char* const LIBRARY_NAME = "WinFeature"; luaL_register(luaEnv, LIBRARY_NAME, luaLibs); return 1; } ~~~ 我們通過luaL_register將LIBRARY_NAME對應的庫名,以及luaL_Reg數組對應的導出列表來注冊到lua_State*對應的Lua環境中。 ### [](https://github.com/andycai/luaprimer/blob/master/08.md#lua調用)Lua調用 那么我們要如何調用該Dll呢?首先,把該Dll放到你的Lua能搜尋到的目錄——當前目錄、Lua安裝目錄下的clibs目錄,然后通過require函數導入。 因為Lua中如果你的函數調用參數只有一個,并且該參數為字符串的話,函數調用時的括號是可以省略的,所以:require(“YourLibName”)和requir“YourLibName”都是合法的。我們把剛剛生成的WinFeature.dll文件拷貝到C盤下,然后在C盤啟動Lua。示例如下: ~~~ > require "WinFeature" > for k, v in pairs(WinFeature) do >> print(k, v) >> end ShowMsgBox functon:0028AB90 > ~~~ 可以看到,函數調用方式都是“包名.函數名”,而包名就是你的Dll的名字。我們可以用下面的方式查看一個包中的所有函數: ~~~ for k, v in pairs(PackageName) do print(k, v) end ~~~ 然后我們調用WinFeature.ShowMsgBox函數: ~~~ > WinFeature.ShowMsgBox("Hello, this is a msgBox", "Tip") ~~~ 會彈出對話框顯示內容為"Hello, this is a msgBox"和標題為"Tip"。 ### [](https://github.com/andycai/luaprimer/blob/master/08.md#lua堆棧詳解)Lua堆棧詳解 唔,那么lua_State結構如何管理Lua運行環境的呢?Lua又是如何將參數傳遞到C/C++函數的呢?C/C++函數又如何返回值給Lua呢?……這一切,都得從Lua堆棧講起。 Lua在和C/C++交互時,Lua運行環境維護著一份堆棧——不是傳統意義上的堆棧,而是Lua模擬出來的。Lua與C/C++的數據傳遞都通過這份堆棧來完成,這份堆棧的代表就是lua_State*所指的那個結構。 #### [](https://github.com/andycai/luaprimer/blob/master/08.md#堆棧結構解析)堆棧結構解析 堆棧通過lua_push系列函數向堆棧中壓入值,通過luaL_check系列從堆棧中獲取值。而用luaL_check系列函數時傳遞的參數索引,比如我們調用WinFeature.ShowMsgBox(“Hello”, “Tip”)函數時,棧結構如下: 棧頂 "Tip" 2或者-1 "Hello" 1或者-2 棧底 其中,參數在棧中的索引為參數從左到右的索引(從1開始),棧頂元素索引也可以從-1記起。棧中元素個數可以用lua_gettop來獲得,如果lua_gettop返回0,表示此棧為空。(lua_gettop這個函數名取得不怎么樣!) #### [](https://github.com/andycai/luaprimer/blob/master/08.md#提取參數)提取參數 luaL_check系列函數在獲取值的同時,檢測這個值是不是符合我們所期望的類型,如果不是,則拋出異常。所有這個系列函數如下: ~~~ luaL_checkany —— 檢測任何值(可以為nil) luaL_checkint —— 檢測一個值是否為number(double),并轉換成int luaL_checkinteger —— 檢測一個值是否為number(double),并轉換成lua_Integer(prtdiff_t),在我的機子上,ptrdiff_t被定義為int luaL_checklong —— 檢測一個值是否為number(double),并轉換成long luaL_checklstring —— 檢測一個值是否為string,并將字符串長度傳遞在[out]參數中返回 luaL_checknumber —— 檢測一個值是否為number(double) luaL_checkstring —— 檢測一個值是否為string并返回 luaL_checkudata —— 檢測自定義類型 ~~~ #### [](https://github.com/andycai/luaprimer/blob/master/08.md#傳遞返回值)傳遞返回值 當我們要傳遞返回值給Lua時,可以用lua_push系列函數來完成。每一個導出函數都要返回一個int型整數,這個整數是你的導出函數的返回值的個數。而返回值通過lua_push系列函數壓入棧中。比如一個Add函數: ~~~ extern “C” int Add(lua_State* luaEnv) { CheckParamCount(luaEnv, 2); double left = luaL_checknumber(luaEnv, 1); double right = luaL_checknumber(luaEnv, 2); double result = left + right; lua_pushnumber(luaEnv, result); return 1; } ~~~ 可以看出,我們用lua_pushnumber把返回值壓入棧,最后返回1——1代表返回值的個數。lua_push系列函數如下: ~~~ lua_pushboolean —— 壓入一個bool值 lua_pushcfunction —— 壓入一個lua_CFunction類型的C函數指針 lua_pushfstring —— 格式化一個string并返回,類似于sprintf lua_pushinteger —— 壓入一個int lua_pushlightuserdata —— 壓入自定義數據類型 lua_pushliteral —— 壓入一個字面值字符串 lua_pushlstring —— 壓入一個規定長度內的string lua_pushnil —— 壓入nil值 lua_pushnumber —— 壓入lua_Number(double)值 lua_pushstring —— 壓入一個string lua_pushthread —— 壓入一個所傳遞lua_State所對應的線程,如果此線程是主線程,則返回1 lua_pushvalue —— 將所傳遞索引處的值復制一份壓入棧頂 lua_pushvfstring —— 類似lua_pushfstring ~~~ 通過這些函數,我們可以靈活的使用C/C++的高性能特性,來導出函數供Lua調用。 ## [](https://github.com/andycai/luaprimer/blob/master/08.md#cc調用lua腳本)C/C++調用Lua腳本 ### [](https://github.com/andycai/luaprimer/blob/master/08.md#簡介-1)簡介 **C調用LUA文件中的函數方法** ~~~ lua_getglobal(L, <function name>) //獲取lua中的函數 lua_push*() //調用lua_push系列函數,把輸入參數壓棧。例如lua_pushnumber(L, x) lua_pcall(L, <arguments nums>, <return nums>, <錯誤處理函數地址>) ~~~ 例如: ~~~ lua_settop(m_pLua,0); lua_getglobal(m_pLua,"mainlogic"); lua_pushlstring(m_pLua,(char*)msg.getBuf(),msg.size()); int ret = 0; ret = lua_pcall(m_pLua,1,4,0); ~~~ 上一節介紹了如何在Lua中調用C/C++代碼,本節介紹如何在C/C++中調用Lua腳本。本節介紹一個例子,通過Lua來生成一個XML格式的便簽。便簽格式如下: ~~~ <?xml version="1.0" encoding="utf-8" ?> <note> <fromName>發送方姓名</fromName> <toName>接收方姓名</toName> <sendTime>發送時間</sendTime> <msgContent>便簽內容</msgContent> </note> ~~~ 我們通過C/C++來輸入這些信息,然后讓Lua來生成這樣一個便簽文件。 ### [](https://github.com/andycai/luaprimer/blob/master/08.md#lua代碼)Lua代碼 ~~~ xmlHead = '<?xml version="1.0" encoding="utf-8" ?>\n' -- Open note file to wriet. function openNoteFile(fileName) return io.open(fileName, "w") end -- Close writed note file. function closeNoteFile(noteFile) noteFile:close() end function writeNestedLabel(ioChanel, label, nestCnt) if nestCnt == 0 then ioChanel:write(label) return end for i = 1, nestCnt do ioChanel:write("\t") end ioChanel:write(label) end function generateNoteXML(fromName, toName, msgContent) local noteFile = openNoteFile(fromName .. "_" .. toName .. ".xml") if not noteFile then return false end noteFile:write(xmlHead) noteFile:write("<note>\n") local currNestCnt = 1 writeNestedLabel(noteFile, "<fromName>", currNestCnt) noteFile:write(fromName) writeNestedLabel(noteFile, "</fromName>\n", 0) writeNestedLabel(noteFile, "<toName>", currNestCnt) noteFile:write(toName) writeNestedLabel(noteFile, "</toName>\n", 0) local sendTime = os.time() writeNestedLabel(noteFile, "<sendTime>", currNestCnt) noteFile:write(sendTime) writeNestedLabel(noteFile, "</sendTime>\n", 0) writeNestedLabel(noteFile, "<msgContent>", currNestCnt) noteFile:write(msgContent) writeNestedLabel(noteFile, "</msgContent>\n", 0) noteFile:write("</note>\n") closeNoteFile(noteFile) return true end ~~~ 我們通過openNoteFile和closeNoteFile來打開/關閉XML文件。generateNoteXML全局函數接受發送方姓名、接收方姓名、便簽內容,生成一個XML便簽文件。便簽發送時間通過Lua標準庫os.time()函數來獲取。writeNestedLabel函數根據當前XML的縮進數目來規范XML輸出格式。此文件很好理解,不再贅述。 ### [](https://github.com/andycai/luaprimer/blob/master/08.md#c調用lua腳本)C++調用Lua腳本 ~~~ extern "C" { #include <lua.h> #include <lualib.h> #include <lauxlib.h> #pragma comment(lib, "lua.lib") }; #include <iostream> #include <string> using namespace std; // 初始化Lua環境. lua_State* initLuaEnv() { lua_State* luaEnv = lua_open(); luaopen_base(luaEnv); luaL_openlibs(luaEnv); return luaEnv; } // 加載Lua文件到Lua運行時環境中 bool loadLuaFile(lua_State* luaEnv, const string& fileName) { int result = luaL_loadfile(luaEnv, fileName.c_str()); if (result) { return false; } result = lua_pcall(luaEnv, 0, 0, 0); return result == 0; } // 獲取全局函數 lua_CFunction getGlobalProc(lua_State* luaEnv, const string& procName) { lua_getglobal(luaEnv, procName.c_str()); if (!lua_iscfunction(luaEnv, 1)) { return 0; } return lua_tocfunction(luaEnv, 1); } int main() { // 初始化Lua運行時環境. lua_State* luaEnv = initLuaEnv(); if (!luaEnv) { return -1; } // 加載腳本到Lua環境中. if (!loadLuaFile(luaEnv, ".\\GenerateNoteXML.lua")) { cout<<"Load Lua File FAILED!"<<endl; return -1; } // 獲取Note信息. string fromName; string toName; string msgContent; cout<<"Enter your name:"<<endl; cin>>fromName; cout<<"\nEnter destination name:"<<endl; cin>>toName; cout<<"\nEnter message content:"<<endl; getline(cin, msgContent); getline(cin, msgContent); // 將要調用的函數和函數調用參數入棧. lua_getglobal(luaEnv, "generateNoteXML"); lua_pushstring(luaEnv, fromName.c_str()); lua_pushstring(luaEnv, toName.c_str()); lua_pushstring(luaEnv, msgContent.c_str()); // 調用Lua函數(3個參數,一個返回值). lua_pcall(luaEnv, 3, 1, 0); // 獲取返回值. if (lua_isboolean(luaEnv, -1)) { int success = lua_toboolean(luaEnv, -1); if (success) { cout<<"\nGenerate Note File Successful!"<<endl; } } // 將返回值出棧. lua_pop(luaEnv, 1); // 釋放Lua運行時環境. lua_close(luaEnv); system("pause"); return 0; } ~~~ ### [](https://github.com/andycai/luaprimer/blob/master/08.md#代碼解析)代碼解析 #### [](https://github.com/andycai/luaprimer/blob/master/08.md#初始化lua運行時環境)初始化Lua運行時環境 lua_State*所指向的結構中封裝了Lua的運行時環境。我們用initLuaEnv這個函數來初始化。lua_open函數用來獲取一個新環境,然后我們用luaopen_base打開Lua的基礎庫,然后用luaL_openlibs打開Lua的io、string、math、table等高級庫。 #### [](https://github.com/andycai/luaprimer/blob/master/08.md#加載lua文件)加載Lua文件 然后我們用luaL_loadfile和lua_pcall來將一個Lua腳本加載到對應的Lua運行時環境中——注意:并不自動執行,只是加載。這兩個函數如果返回非0,表示加載失敗——你的Lua腳本文件可能未找到或Lua腳本有語法錯誤…… #### [](https://github.com/andycai/luaprimer/blob/master/08.md#加載lua函數)加載Lua函數 我們用lua_getglobal函數將Lua腳本中的全局函數、全局變量等入棧,放在棧頂。 #### [](https://github.com/andycai/luaprimer/blob/master/08.md#壓入lua函數調用參數)壓入Lua函數調用參數 我們用lua_push系列函數來將要調用函數所需參數全部入棧,入棧順序為Lua函數對應參數從左到右的順序。 #### [](https://github.com/andycai/luaprimer/blob/master/08.md#調用lua函數)調用Lua函數 最后用lua_pcall來調用函數。Lua_pcall函數原型如下: ~~~ int lua_pcall(lua_State* L, int nargs, int nresults, int errfunc); ~~~ 其中,L為此函數執行的Lua環境,nargs為此函數所需的參數個數,nresults為此函數的返回值個數,errfunc為發生錯誤時錯誤處理函數在堆棧中的索引。 #### [](https://github.com/andycai/luaprimer/blob/master/08.md#獲取lua函數返回值)獲取Lua函數返回值 然后,我們可以通過檢測棧頂的位置(從-1開始),來獲取返回值。獲取返回值后,用lua_pop將棧頂元素出棧——出棧個數為返回值個數。 #### [](https://github.com/andycai/luaprimer/blob/master/08.md#釋放lua環境)釋放Lua環境 最后,通過lua_close函數來關閉Lua環境并釋放資源。 ### [](https://github.com/andycai/luaprimer/blob/master/08.md#運行結果)運行結果 我們將GenerateNoteXML.lua腳本和最終的C++生成的GenerateNoteXML.exe放在同一路徑下,并運行GenerateNoteXML.exe,在此目錄下會生成一個XML文件。如下: ~~~ Enter your name: Jack Enter destnation name: Joe Enter message content: Hello, Can you help me? Generate Note File Successful! ~~~ 生成的arnozhang_YaFengZhang.xml文件如下: ~~~ <?xml version="1.0" encoding="utf-8" ?> <note> <fromName>Jack</fromName> <toName>Joe</toName> <sendTime>1317971623</sendTime> <msgContent>Hello, Can you help me?</msgContent> </note> ~~~ ## [](https://github.com/andycai/luaprimer/blob/master/08.md#c-作為動態庫文件被-lua-調用)C 作為動態庫文件被 Lua 調用 ### [](https://github.com/andycai/luaprimer/blob/master/08.md#cc中的入口函數定義)C/C++中的入口函數定義 一定是要定義成: luaopen_(dll或so文件的文件名稱),(dll或so文件的文件名稱)必須和dll或so文件名稱保持一致。 例如(C++ windows情況): ~~~ #ifdef _WIN32 #define _EXPORT extern "C" __declspec(dllexport) #else //unix/linux #define _EXPORT extern "C" #endif _EXPORT int luaopen_capi_mytestlib(lua_State *L) { struct luaL_reg driver[] = { {"average", average1}, {NULL, NULL},}; luaL_register(L, "mylib", driver); //luaL_openlib(L, "mylib", driver, 0); return 1; } ~~~ ### [](https://github.com/andycai/luaprimer/blob/master/08.md#動態庫要供lua調用的function)動態庫要供LUA調用的function 其定義要符合: ~~~ typedef int function(lua_State *L) ~~~ ### [](https://github.com/andycai/luaprimer/blob/master/08.md#在動態庫調用lua注冊)在動態庫調用LUA注冊 將要調用的函數放到這個結構體里: ~~~ struct luaL_Reg lib[] ={} ~~~ 在動態庫的入口函數里調用luaL_register將這個結構體注冊,在這個入口函數注冊結構體時,要注冊成: ~~~ luaL_register(L,"XXX",lib); ~~~ ### [](https://github.com/andycai/luaprimer/blob/master/08.md#在寫腳本的時候使用requirexxx)在寫腳本的時候,使用require("XXX") 就是入口函數的luaopen_后面的XXX,注意大小寫敏感 ### [](https://github.com/andycai/luaprimer/blob/master/08.md#編譯生成的動態庫命令成xxxso或xxxdllwin)編譯生成的動態庫命令成XXX.so或XXX.dll(win) 同入口函數的luaopen_后面的XXX一致 **示例:** **C文件如下:** ~~~ #include <stdio.h> #include "lua/lua.h" #include "lua/lualib.h" #include "lua/lauxlib.h" static int add(lua_State *L) { int a,b,c; a = lua_tonumber(L,1); b = lua_tonumber(L,2); c = a+b; lua_pushnumber(L,c); printf("test hello!!!\r\n"); return 1; } static const struct luaL_Reg lib[] = { {"testadd",add}, {NULL,NULL} }; int luaopen_testlib(lua_State *L) { luaL_register(L,"testlib",lib); return 1; } ~~~ 編譯: gcc test.c -fPIC -shared -o testlib.so **lua腳本編寫:** ~~~ require("testlib") c = testlib.testadd(15,25) print("The result is ",c); ~~~ **示例:** ~~~ int lua_createmeta (lua_State *L, const char *name, const luaL_reg *methods) { if (!luaL_newmetatable (L, name)) return 0; luaL_openlib (L, NULL, methods, 0); lua_pushliteral (L, "__gc"); lua_pushcfunction (L, methods->func); lua_settable (L, -3); lua_pushliteral (L, "__index"); lua_pushvalue (L, -2); lua_settable (L, -3); lua_pushliteral (L, "__metatable"); lua_pushliteral (L, "you're not allowed to get this metatable"); lua_settable (L, -3); return 1; } ~~~ 示例中的luaopen_testlib函數替換為: ~~~ int luaopen_testlib(lua_State *L) { lua_createmeta(L,"testlib",lib); return 1; } ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看