一种C函数到Lua的封装

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

你可能感兴趣的:(lua)