一种Lua到C的封装

在Lua的官方C API中,Lua与C通过一个虚拟栈来交互数据。例如有个a.lua的文件中有求和函数:

function sum(a, b)
    return a + b
end

要在C代码中打开lua文件,并调用求和函数,大致要这样写:

lua_State *s = luaL_newstate();
luaL_openlibs(s);
luaL_loadfile(s, "a.lua");
lua_getglobal(s, "sum");
lua_pushinteger(s, 1234);
lua_pushinteger(s, 4321);
lua_pcall(s, 2, 1, 0);
int sum = lua_tointeger(s, -1); /* sum = 5555 */
lua_pop(s, 1);
lua_close(s);

可以想见官方API的设计专注于正交性,而不考虑便利性。

自然希望有一个封装,让程序员在C代码中,尽可能地像Lua一样调用Lua。

github上没搜到特别中意的,只好自己写一个,取名Lucy。设计目标:

1) 隐藏虚拟栈——因此也隐藏基于虚拟栈的官方C API。

2) 在C代码中,尽量用Lua的方式调用Lua代码。

3) function和table在C代码中也该是第一类型,可以作为函数参数传递——其实是第2) 点的补充说明。

在Lua这样的动态类型语言中,变量本身没有类型,被赋值后才有相应的类型。

因此在Lucy中只需定义一个名为lucy_Data的结构体用以表示Lua变量:

typedef struct {
    lucy_Type type_;
    lucy_Content cntnt_;
} lucy_Data;

lucy_Type是一个枚举类型,lucy_Content则是联合体。

如此定义,则对lucy_Data作任何操作,编译期都没问题,而只会在运行期断言失败,算是对Lua变量的模拟。

lucy_Content中有个类型lucy_Ref:

typedef struct {
    lua_State *state_;
    int index_;
} lucy_Ref;

保存function, table这样的引用类型,在虚拟栈上的索引。于是它们就可以作为第一类型了。

function传入参数,传出返回值的基础类型,都可以是lucy_Data的数组类型lucy_List。

不难设计与实现剩下的接口。

例如有以下Lua代码:

function Loc(x, y)
    return function() return x, y end
end

function GetArea(loc)
    local x, y = loc()
    return x * y
end

给定x与y,通过调用GetArea函数来取得结果,通过Lucy封装后可以这么写(比如x与y都为5):

lucy_File file = lucy_CreateFile();
lucy_OpenFile(&file, "a.lua");
lucy_Data Loc = lucy_GetData(&file, "Loc");
lucy_Data five = lucy_Num(5);
lucy_Data loc = lucy_Call(&Loc, 1, 2, &five, &five).datas_[0];
lucy_Data GetArea = lucy_GetData(&file, "GetArea");
lucy_Data area = lucy_Call(&GetArea, 1, 1, &loc).datas_[0];

lucy_PrintData(&area);

运行结果:

代码已上传至: https://github.com/chncwang/Lucy

已有的接口包括取变量,调用function,查询table元素,长度等。

posted @ 2012-08-24 03:02  chncwang  阅读(2475)  评论(1编辑  收藏  举报