Lua也可以調用C函數,只是以更加崎嶇的方式——通過一個私人棧來傳遞參數和傳回值。提供給Lua API的C函數都是這個樣子的:
static int sum(lua_State *L){ int a = lua_tonumber(L, -1); int b = lua_tonumber(L, -2); lua_pop(L, 2); lua_pushnumber(L, a + b); return 1; /* return one value. */}
Lua將其定義為:
typedef int (*lua_CFunction)(lua_State *L);
還須在全域棧如此註冊C函數:
lua_pushcfunction(L, sum);lua_setglobal(L, "sum");
看了些開源的Lua封裝庫,對此也作了封裝。受此啟發,筆者也為自己的Lucy庫增加了C函數封裝。
承之前的設計思路,用lucy_Data類型表示Lua資料,那麼要讓Lua調用的C函數應該是這樣的:
static lucy_List sum(const lucy_List *args){ int a = args->datas_[0].cntnt_.num_; int b = args->datas_[1].cntnt_.num_; lucy_Data r = lucy_Num(a + b); return lucy_GetList(1, &r);}
不妨定義這樣的C函數為:
typedef lucy_List (*lucy_CFuncWithList)(const lucy_List *args);
易寫一個函數,作為lucy_CFuncWithList與lua_CFunction的翻譯:
void lucy_CallCFunc(lua_State *state, lucy_CFuncWithList cfunc, int rc, int ac);
須要寫一個宏,讓其產生lua_CFunction類型的函數:
#define lucy_GenLuaCFunction(cfunc, rc, ac) \static int LUA_CFUNCTION_NAME(cfunc)(lua_State *state) \{\ lucy_CallCFunc(state, cfunc, rc, ac);\ return rc;\}
最後寫一個宏註冊C函數:
#define lucy_SetCFunc(file, name, cfunc) \ lua_pushcfunction((file)->state_, LUA_CFUNCTION_NAME(cfunc));\ lua_setglobal((file)->state_, name)
這樣設計的好處依然是,Lua資料在C函數中視作第一類型——比如可以在被Lua調用的C函數中調用Lua函數。
比如有如下的Lua代碼:
Do(3, print, "Hello world!")
這行代碼希望Do是一個C函數,通過Lua函數print,輸出3次“Hello world!”。
通過Lucy可以這麼做:
static lucy_List Do(const lucy_List *args){ int times = args->datas_[0].cntnt_.num_; const lucy_Data *func = args->datas_ + 1; const lucy_Data *str = args->datas_ + 2; int i; for (i=0; i<times; ++i) { lucy_Call(func, 0, 1, str); } return lucy_GetList(0);}lucy_GenLuaCFunction(Do, 0, 3);int main(){ lucy_File file = lucy_CreateFile(); lucy_OpenFile(&file, "/Code/a.lua"); lucy_SetCFunc(&file, "Do", Do); lucy_Run(&file); lucy_CloseFile(&file); return 0;}
輸出結果:
更新已push至:https://github.com/chncwang/Lucy