1.1 從C程式調用LUA函數
LUA的函數和普通變數一樣也是First Class Variable類型,可以看作函數指標變數參與棧操作。因此調用過程分為如下幾個步驟:
- 請求LUA函數(指標)入(GLOBAL)棧。
- 將函數需要的參數入棧,入棧順序按照參數被聲明的順序。
- 告知LUA虛擬機器入棧參數的個數、函數傳回值的個數,並調用此LUA函數。
- 從棧定獲得傳回值,先返回的先入棧,然後將傳回值顯式出棧。
1.2 從LUA指令碼調用C函數
LUA沒有提供PYTHON那樣豐富的類庫,因此複雜的功能需要在C程式中定義好,然後通過lua決定調用時機。在LUA庫中定義了可以被LUA虛擬機器識別的C函數模型:int functionName (lua_State* L) {....; return 1;}
這樣的函數被是一個合法的lua_CFunction類型,將函數註冊到LUA虛擬機器中以後,就可以在LUA中以普通LUA函數的方式被調用。註冊一個C函數的步驟如下:
- 聲明並定義一個滿足上述模型的函數 (eg. myFunInC)
- 用字串為此C函數取一個名稱併入棧(eg. myFunInLua)
- 將函數(指標)入棧
- 調用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, //VMachine
3, //Argument Count
2, //Return Value Count
0 );
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);