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