ILD

lua c stack
作者:Yuan Jianpeng 邮箱:yuanjp89@163.com
发布时间:2025-5-13 站点:Inside Linux Development

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


Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.