LUA與C++互動第一篇

工程師WWW發表於2014-03-05

 到公司已經兩個周了,學習Lua已經開始在專案中使用,但是由於使用的lua函式基本上都是公司在上面進行了一次封裝的,沒有原始碼對兩種語言的互動詳情還是不甚瞭解。如:如果向LUA註冊一個物件給LUA使用,如何呼叫LUA中函式這些在公司的SDK看來就是一個簡單的RegisterObject物件的幾個屬性進行填寫就行了。

    今天主要是對在Lua中如何呼叫C++函式和在C++中如何呼叫Lua函式進行學習。

在LUA中要呼叫C++函式,那就要在C++中對Lua進行註冊,如何註冊呢?需要一個巨集來完成功能,就是lua_register(L,"add",add),該巨集對應的是兩個函式,一個是lua_pushcfunction(L,f),lua_setglobal(L,n);可以看出是先對函式進行壓棧、然後給函式設定一個在lua中的呼叫名稱,注意此處的n不是表示一個整數什麼的,是name的意思,表示函式的註冊名。而此處lua_setglobal也是一個巨集,

 #define lua_setglobal(L,s)   lua_setfield(L, LUA_GLOBALSINDEX, s)
可以看出其實lua_setglobal只是lua_setfield的一個特例,因為LUA_GLOBALSINDEX是一直存在的,所以使用幹函式來簡單操作,當然如果我們要註冊的函式不註冊到全域性的,而是在其他某個我們定義的表內可訪問,那就要呼叫lua_setfield函式了。說到這個話題,設計到的函式就會越來越多,比如lua_getglobal與lua_setglobal對於,lua_getfield與lua_getfield對應,我們用到的時候再解釋,不用到時就不說了。
  上面提到了使用lua_register函式進行註冊,這裡要強調的是該函式對註冊的函式原型是有要求的,不是任何函式都可以註冊,函式原型如下:
int (func*)(lua_State* L);我們應該能夠理解,LUA和C++的互動通過棧來進行傳參,因此只需要給一個引數lua_State表當前操作棧就行了,至於該函式有幾個引數,返回值這些在func函式中去處理就好了,該函式返回值表示C++函式會向棧中放多少個值,而至於需要幾個引數可以通過lua_tostring、lua_tointeger等函式通過傳入棧中的序號來獲取。由於註冊函式原型固定了,但我們在寫C++函式的時候不可能程式碼長短什麼的都往該函式中塞,在實際中我們常常是隻使用該函式來獲取從LUA讀取引數和返回引數,所以該函式一般是這樣的形式:
int funcname(lua_State* L)
{
    //此處可以做資料型別檢查這些
    ..
    //取值
    arg1 = lua_tostring(L,-1);
    arg2 = lua_tostring(L,-2);
    //呼叫真正的函式
    dosomething_function
   //進行引數壓棧等也可以再上面呼叫的函式中壓棧
   return n;//此處的n是C++向棧中壓入的引數個數,如果和壓入棧個數不一致,可能導致棧失衡
}
註冊函式來說就上面幾個步驟而已,比起註冊C++類來說簡單得多,註冊C++類現在我也還沒有掌握,在此處就不說了,下面說的是我們註冊了C++函式現在該到LUA中去呼叫了,現在在test.lua檔案中新增這樣一個函式
function lua_add(a,b)
   return add(a,b)
end
現在如果在命令列執行該檔案應該會失敗,因為我上面註冊的C++函式不是註冊成一個動態庫,我是直接在控制檯可執行檔案中寫的,因此我就需要在
C++中來呼叫該lua_add函式了。C++中需要先知道有lua_add這樣一個函式,需要先使用luaL_dofile(test.lua)來載入該檔案,載入進去後該函式就存在於全域性表中了,於是使用lua_getglobal函式來從表中獲取函式地址lua_getglobal(L,"lua_add"),該函式是在全域性表中查詢lua_add函式並把它壓到棧頂,到這裡剛開始學習的時候會很疑惑,當初我看lua中很多函式介紹的時候就覺得很不解,很多函式就是對棧操作,單獨看一個函式實在看不出想達到什麼目的,但和其他函式配合使用功能就強大了,所以需要對lua中這些對棧操作的函式都有一個認識,才能夠組合出自己需要的功能來。前面說把函式找到壓棧了,下面就是要呼叫函式,呼叫函式前需要傳引數,如何傳?還是把引數放到棧中、如果我們需要給函式傳兩個引數,把連個引數壓棧,使用函式lua_pushinteger(L,4)表示向棧中壓入一個整數4,其他型別如lua_pushstring,lua_pushlstring等很多。引數也進棧了,下面才是開始真正呼叫,如何調?lua提供的函式lua_pcall來呼叫,該函式第一個引數為lua_State表示當前的棧吧,第二個參數列示要呼叫函式的引數個數,第三個表示被呼叫函式的返回值個數,這個不要想C++中函式只有一個返回值,這是表示我們要向lua中返回多少個值;第四個參數列示錯誤處理函式在棧上的索引,為0表示沒有錯誤處理函式,和lua_pcall一樣用於呼叫棧中函式的函式有lua_call和lua_cpcall等,這裡不仔細介紹。在上面的函式呼叫中,由於返回值存在與棧中,在取回返回值後,需要呼叫lua_pop(L)對棧進行清空。
下面是一個測試程式碼:
  1. #include <stdio.h>  
  2.   
  3. extern "C"  
  4. {  
  5.     #include <lua.h>  
  6.     #include <lualib.h>  
  7.     #include <lauxlib.h>  
  8. }  
  9.   
  10. #include <string>  
  11. using namespace std;  
  12.   
  13. int add(lua_State* L)  
  14. {  
  15.     int a = lua_tointeger(L,1);//取得函式引數  
  16.     int b = lua_tointeger(L,2);  
  17.   
  18.     lua_pushinteger(L,a+b);//入棧返回值  
  19.     return 1;//1表示壓入棧資料個數  
  20. }  
  21. struct luaL_Reg luaCppReg[] =//可以使用該結構體一次註冊多個函式,則需要呼叫luaL_register函式,此處沒有使用  
  22. {  
  23.     {"add",add},  
  24.     {NULL,NULL}  
  25. };  
  26. int _tmain(int argc, _TCHAR* argv[])  
  27. {  
  28.     char buff[256];  
  29.     int error;'  
  30.     lua_State *L = luaL_newstate();  /* opens Lua,由於我使用的是lua5.2版本,lua_open函式不存在了 */  
  31.     luaopen_base(L);         /* opens the basic library 這些是在引入一些庫,就如如果add函式在編譯成dll後如果在lua中要使用需要require “動態庫名"一樣*/  
  32.     luaopen_table(L);        /* opens the table library這些庫是加在這裡只是測試 */  
  33.     luaopen_io(L);           /* opens the I/O library */  
  34.     luaopen_string(L);       /* opens the string lib. */  
  35.     luaopen_math(L);         /* opens the math lib. */  
  36.   
  37.       
  38.     lua_register(L,"add",add);//註冊add函式,好像還可以使用luaL_register函式註冊,該函式使用結構體的方式  
  39.   
  40.     luaL_dofile(L,"test.lua");//載入lua檔案,回將裡面的函式載入到全域性表中  
  41.     lua_getglobal(L,"lua_add");//查詢lua_add函式,並壓入棧底  
  42.     lua_pushinteger(L,6);//函式引數1  
  43.     lua_pushinteger(L,5);//函式引數2  
  44.     lua_pcall(L,2,1,0);//呼叫lua_add函式,同時會對lua_add及兩個參加進行出棧操作,並壓入返回值  
  45.     int result = lua_tointeger(L,-1);//從棧中取回返回值  
  46.     lua_pop(L,1);//清棧,由於當前只有一個返回值  
  47.     printf("result = %d",result);  
  48.   
  49.     lua_close(L);//關閉lua環境  
  50.     return 0;  
  51. }  

相關文章