LUA和C之間的函數調用

來源:互聯網
上載者:User
LUA和C之間的函數調用 1.1 從C程式調用LUA函數

LUA的函數和普通變數一樣也是First Class Variable類型,可以看作函數指標變數參與棧操作。因此調用過程分為如下幾個步驟:

  1. 請求LUA函數(指標)入(GLOBAL)棧。
  2. 將函數需要的參數入棧,入棧順序按照參數被聲明的順序。
  3. 告知LUA虛擬機器入棧參數的個數、函數傳回值的個數,並調用此LUA函數。
  4. 從棧定獲得傳回值,先返回的先入棧,然後將傳回值顯式出棧。
1.2 從LUA指令碼調用C函數

LUA沒有提供PYTHON那樣豐富的類庫,因此複雜的功能需要在C程式中定義好,然後通過lua決定調用時機。在LUA庫中定義了可以被LUA虛擬機器識別的C函數模型:
int functionName (lua_State* L) {....; return 1;}

這樣的函數被是一個合法的lua_CFunction類型,將函數註冊到LUA虛擬機器中以後,就可以在LUA中以普通LUA函數的方式被調用。註冊一個C函數的步驟如下:

  1. 聲明並定義一個滿足上述模型的函數 (eg. myFunInC)
  2. 用字串為此C函數取一個名稱併入棧(eg. myFunInLua)
  3. 將函數(指標)入棧
  4. 調用LUA庫的註冊函數功能,將上述的名稱與函數指標關聯

這樣就可以在LUA中用myFunInLua()來調用C中的int myFunInC()了

2. 從C調用LUA函數樣本

在下面的代碼中,我們調用了LUA指令碼中的fnEx2函數,傳回值從棧中取得,並且要手動出棧。這裡,入棧的函數參數會由pcall自動清理。

2.1 LUA測試指令碼代碼
function fnex2(str_a, num_b, num_c)print(str_a);return num_b*100 + num_c*10, "Thank you";end;

 

2.2 VC代碼

 

//初始化LUA虛擬機器void InitLuaState(lua_State* L){/* Load Libraries */luaopen_base(L);luaopen_table(L);luaL_openlibs(L);luaopen_string(L);luaopen_math(L);}int call_lua_function(void){const char* szInParam = "This is an [IN] parameter";const int iParam1 = 20, iParam2 = 50;cout << "=================================" << endl<< "02_Call_Function" << endl<< "=================================" << endl<< "This demo calls functions in LUA scripts." << endl<< "Argument 1:" << szInParam << endl<< "Argument 2:" << iParam1 << endl<< "Argument 3:" << iParam2 << endl<< "---------------------------------" << endl<< "#OUTPUTS#" << endl;lua_State* L = lua_open();InitLuaState(L);int iError;/* Load Script */iError = luaL_loadfile(L, "../test02.lua");if (iError){cout << "Load script FAILED!" << lua_tostring(L, -1)<< endl;lua_close(L);return 1;}/* Run Script */iError = lua_pcall(L, 0, 0, 0);if (iError){cout << "pcall FAILED"<< lua_tostring(L, -1)<< iError << endl;lua_close(L);return 1;}/* Push a FUNCTION_VAR to STACK */lua_getglobal(L, "fnex2");/* Push PARAMETERS to STACK */lua_pushstring(L, szInParam);lua_pushnumber(L, iParam1);lua_pushnumber(L, iParam2);/* Call FUNCTION in LUA */iError = lua_pcall( L, //VMachine3, //Argument Count2, //Return Value Count0 );if (iError){cout << "pcall FAILED"<< lua_tostring(L, -1)<< iError << endl;lua_close(L);}/* Check Return Value Types */if (lua_isstring(L, -1) && lua_isnumber(L, -2)){cout << "Ret_1(string): " << lua_tostring(L, -1) << endl;cout << "Rec_2(double): " << lua_tonumber(L, -2) << endl;}else{cout << "Wrong Return Values" << endl;}/* POP STACK */lua_pop(L,2); //只需要清理Return Value,pcall調用的入棧參數會自動清理lua_close(L);return 0;}

 

2.3 工具

下面的宏可以簡化調用lua函數的代碼:

#define CallLuaFunc(FuncName, Params, Results) { lua_getglobal (g_pLuaState, FuncName); lua_call (g_pLuaState, Params, Results); }

 

3. 從LUA調用C函數樣本

在下面的例子中,我們註冊一個名為rmath的LUA函數,他在C中的函數名為RMath_LUA()

3.1 LUA指令碼代碼
print (">>> LUA程式開始運行了 ");function fnex3(num_a, num_b)local c = rmath(num_a, num_b);print("LUA PRINTTING:", c);return c;end;

 

3.2 VC程式碼
//LUA指令碼調用C函數int call_c_function(void){int iArg1 = 3, iArg2 = 10, iError;cout << "=================================" << endl<< "下面的程式示範從LUA指令碼中調用C函數" << endl<< "Argument 1:" << iArg1 << endl<< "Argument 2:" << iArg2 << endl<< "---------------------------------" << endl<< "#OUTPUTS#" << endl;lua_State* L = lua_open();InitLuaState(L);iError = luaL_loadfile(L, "../test03.lua");if (iError) cout << "載入指令碼失敗" << endl;iError = lua_pcall(L, 0, 0, 0);if (iError) cout << "執行LUA指令碼失敗" << endl;/* 將C函數(指標)壓棧 */lua_pushstring(L, "rmath");lua_pushcfunction(L, RMath_LUA);lua_settable(L, LUA_GLOBALSINDEX);/* LUA函數也是變數(指標),可以壓入棧 */lua_getglobal(L, "fnex3");/* 將提供給LUA函數的參數入棧 */lua_pushnumber(L, iArg1);lua_pushnumber(L, iArg2);/* 調用LUA函數(pcall函數會自動清除入棧的變數) */int Error = lua_pcall( L, //虛擬機器指標2, //2個參數1, //1個傳回值0 );if (Error) cout << "pcall調用fnex3函數失敗" << endl;/* 檢驗傳回值類型 */if (lua_isnumber(L, -1)){cout << "有1個(double)傳回值 = "<< lua_tonumber(L, -1)<< endl;}/* 將LUA函數傳回值出棧 */lua_pop(L, 1);lua_close(L);return 0;}//可供LUA調用的C函數原型int RMath_LUA(lua_State* L){if (!lua_isnumber(L, 1)){lua_pushstring(L, "Arg_1不是數字");lua_error(L);}if (!lua_isnumber(L, 2)){lua_pushstring(L, "Arg_2不是數字");lua_error(L);}/* GET ARGUMENT FROM STACK */double a = lua_tonumber(L, 1); double b = lua_tonumber(L, 2);/* PUSH RESULT TO STACK */lua_pushnumber(L, a * b);/* COUNT OF RETURN VARS*/return 1;}

 

4. 程式解釋4.1 調用LUA指令碼中的函數

調用LUA指令碼函數主要用到如下幾個LUA庫函數:

/* Push a FUNCTION_VAR to STACK */lua_getglobal(L, "fnex2");/* Push PARAMETERS to STACK */lua_pushstring(L, szInParam);lua_pushnumber(L, iParam1);lua_pushnumber(L, iParam2);/* Call FUNCTION in LUA */iError = lua_pcall( L,3,2,0);

 

通過lua_getglobal請求函數(指標)入棧,然後將函數參數按聲明順序入棧,調用lua_pcall執行函數。lua_pcall的第一個參數 指向LUA虛擬機器,第二個參數表示棧頂有多少個函數參數,第三個參數表示此函數將返回幾個值。(pcall自動清理入棧的參數,傳回值則需要手動 pop。)

4.2 從LUA調用C函數

主要用到如下幾個函數,為求方便您也可以自己定義這樣的一個宏。
lua_pushstring(L, "rmath");
lua_pushcfunction(L, RMath_LUA);
lua_settable(L, LUA_GLOBALSINDEX);

函數名入棧
lua_CFunction類型的函數指標入棧
調用lua_settable註冊函數
這樣就可以在lua指令碼中調用rmath()函數了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.