lua和c模块通信是通过stack来交互数据。lua提供一系列c接口,来实现在c模块中,读取和存储数据到stack。
我们关键是需要了解stack是怎么维护的。才能了解stack维护c接口的含义。lua的stack是一个LIFO结构。lua调用的参数和c模块返回的数据,都是存储在stack上的。
比如有一个call.lua和一个c模块test.c
编译c模块的方法:
cc -c -o test.o test.c -fPIC
cc -o test.so test.o -shared
c模块的代码如下:
#include <lua5.1/lua.h> #include <lua5.1/lauxlib.h> static void stackDump (lua_State *L) { int i; int top = lua_gettop(L); /* depth of the stack */ printf("top %d\n", top); for (i = 1; i <= top; i++) { /* repeat for each level */ int t = lua_type(L, i); switch (t) { case LUA_TSTRING: printf("%d string: '%s'\n", i, lua_tostring(L, i)); break; case LUA_TBOOLEAN: printf("%d bool: %s\n", i, lua_toboolean(L, i) ? "true" : "false"); break; case LUA_TNUMBER: printf("%d number: %g\n", i, lua_tonumber(L, i)); break; default: printf("%d type: %s\n", i, lua_typename(L, t)); break; } } printf("\n"); } static int lua_hello(lua_State *L) { stackDump(L); return 0; } static const struct luaL_Reg test_apis[] = { {"hello", lua_hello}, {NULL, NULL}, }; int luaopen_test(lua_State *L) { luaL_register(L, "test", test_apis); return 1; }
call.lua的代码如下
require "test" data = {} data["key1"] = "value" data[2] = 2 errcode, result = test.hello(1, data, 2)
调用call.lua
$ ./call.lua
top 3
1 number: 1
2 type: table
3 number: 2
test.hello(1, data, 2) 传入了3个参数。第一个和第三个是整数。第二个是table。
在c模块里面stackDump()打印出stack里面有3个数据,类型也能打出来。
那么问题来了:怎么读取第二个参数table的内容?
方法是把table的内容一个个弹到栈顶,然后读取。
调用lua_next()把table的元素一个个弹出来,就可以了。改造一下stackDump()代码:
static void dump_v(lua_State *L, int i, int level, int newline) { int t = lua_type(L, i); if (level) printf("%*c", 4 * level, ' '); switch (t) { case LUA_TSTRING: printf("'%s'", lua_tostring(L, i)); break; case LUA_TBOOLEAN: printf("%s", lua_toboolean(L, i) ? "true" : "false"); break; case LUA_TNUMBER: printf("%g", lua_tonumber(L, i)); break; case LUA_TTABLE: printf("%c", newline == 1 ? '{' : '}'); break; default: printf("%s", lua_typename(L, t)); break; } if (newline) printf("\n"); } static void dump(lua_State *L, int i, int level) { int t = lua_type(L, i); dump_v(L, i, level, 1); if (t == LUA_TTABLE) { lua_pushnil(L); while (lua_next(L, i) != 0) { dump_v(L, -2, level + 1, 0); printf(": "); dump(L, -1, 0); lua_pop(L, 1); } dump_v(L, i, level, 2); } } static void stackDump(lua_State *L) { int i; int top = lua_gettop(L); /* depth of the stack */ printf("top %d\n", top); for (i = 1; i <= top; i++) dump(L, i, 1); }
这样就可以打印出json格式的参数了。
top 3
1
{
'key1': 'value'
2: 2
}
2
stack读取table相关的接口
int lua_next (lua_State *L, int index); | 将index处的table,弹出一个key value到栈顶。 弹出前,先弹出栈顶一个元素。所以如上述例子所示,通常要先push一个nil。 |
int lua_gettable (lua_State *L, int index); | 读取index处table的一个特定key的值。需要先将key push到栈顶。然后调用此函数,会弹出value到栈顶。 |
int lua_rawget (lua_State *L, int index); | 和gettable类似,但是是raw get,例如不执行metamethods |
int lua_geti (lua_State *L, int index, lua_Integer i); | 和gettable类似。不过key是通过参数i指定,而不是push到栈顶。参数i是整数。这个接口用来读取数组的value很方便。 |
stack写入table也是类似的。
void lua_newtable (lua_State *L);
先创建一个空的table,然后push到stack上。
将key value push到栈上。然后调用下面这个:
void lua_settable (lua_State *L, int index);
将key/value,加到index指定的table里。加入之后,会把key value pop出来。
void lua_rawset (lua_State *L, int index);
和settable类似,不过不执行metamethods。
例子:
static int lua_hello(lua_State *L) { stackDump(L); lua_pushinteger(L, 1234); lua_newtable(L); lua_pushstring(L, "return_key"); lua_pushstring(L, "return_value"); lua_rawset(L, -3); return 2; }
push第一个返回值1234。
第二个返回值是一个table。它push一个key value,然后将该key value给这个table。
lua_rawset(L, -3),-3是应为table是stack上倒数第3个。倒数第一个是value。倒数第二个是key。
返回2,表示取栈顶的2个元素。
打印:
err 1234
return_key = return_value