用C语言操作lua
[ ]The content is recoverd from Wordpress Blog, for more details please check HERE
July 3, 2016
lua提供了一套C语言的API供用户使用C语言与lua进行交互, 本文通过解读lua C Application Program Interface并且提供具体的实例来表明如何使用C操作lua, 本文以lua5.1为例, lua5.3支持了一部分新的特性不过都大同小异
Lua Stack
对于C, Lua之间的交互, 很自然的会想到如何获得lua的数据,如何将处理的结果返回给lua, lua使用一个Virtual Stack将其函数调用的参数传递给C 如下所示 加入我们有一个lua的调用 fun(a, b, c) 这个调用会将 a, b, c三个参数依次压栈, 在Virtual Stack中形成这样的结构:
| c |
|-------|
| b |
|-------|
| a |
+-------+
为了方便用户取用函数的任意参数, lua的 Virtual Stack不仅允许你访问栈顶元素, 同时允许访问栈内任何位置的元素, lua的API均有一个参数为index, 用于指定对栈上的哪一个参数进行操作, 最后, C语言处理之后的结果要返回给lua, 也是通过压栈的形式, 将结果压栈, 因为lua支持多个返回值, C语言通过返回一个int, 表明将自栈顶算起的多少参数作为调用该lua函数的返回值, 其他的参数均被丢弃掉. 下面通过几个小的demo来说明一下如何通过C语言对lua进行操作. 我们使用lua_sandbox来演示如何用C语言实现lua的函数.
获取用户的每一个传递的参数
首先介绍一下如何实现一个C语言函数并且可以导出到lua_sandbox中, 我们需要做两个工作, 第一个是实现这个函数, 函数的实现标准如下:
int my\_function\_name(lua\_State* lua)
然后使用
lsb\_add\_function(lsb, &your\_fun\_name, "the\_name\_to\_export")
将该函数添加到你要运行的lua_sandbox实例中, 即可在lua代码内调用你写的函数啦
下面我们实现这个函数:
int get\_args(lua\_State* lua)
{
int n = lua\_gettop(lua);
for(int i = 1; i <= n; i++)
{
double arg = lua\_tonumber(lua, i);
printf("%lf\n", arg);
}
return 0;
}
然后我们将函数export为 get_args 在lua中调用该函数
get\_args(1, 2, 3, 4, 5)
就会依次输出这几个参数, 下面我们进行一个稍微复杂点的例子, 遍历输出lua的table
输出lua的table
C语言中是没有map, 这种结构的, 即key-value存储结构, C++通过STL对map实现了支持, 而lua则原生支持map, lua支持一种叫做table的数据结构, 为key-value对, 而且一个table内可以存储多种不同类型的数据, 为了让C语言也能访问lua的table结构,lua API提供了 lua_next 这个函数
lua_next首先将Virtual Stack中的一个参数pop出栈, 然后将index所指定的位置的表格内的下一个元素的key, value依次压栈, 即value在栈顶, key 在次栈顶, 将整个表格遍历完毕之后, lua_next将会返回 NULL, 此时终止遍历即可 ,下面是遍历实现的代码
int visit\_table(lua\_State* lua)
{
lua\_debug\_table(lua, lua\_gettop(lua));
return 0;
}
static int lua\_debug\_table(lua\_State *lua, int index)
{
//It's not a table
if(lua\_type(lua, index)!=LUA\_TTABLE)
{
return 0;
}
lua\_pushnil(lua);
while(lua\_next(lua, index) != 0)
{
if(lua\_type(lua, -1) The content is recoverd from Wordpress Blog, for more details please check [HERE](recover-my-blog) LUA\_TTABLE)
{
char *keyname = lua\_tolstring(lua, -2, NULL);
for(int i = 0; i < padding; i++)
fprintf(stderr, " ");
fprintf(stderr,"%s[%s] - %s\n", keyname, lua\_typename(lua, lua\_type(lua, -2))
,lua\_typename(lua, lua\_type(lua, -1)));
padding+=2;
lua\_debug\_table(lsb, lua\_gettop(lua));
lua\_pop(lua, 1);
padding-=2;
}
else
{
char *keyname = lua\_tolstring(lua, -2, NULL);
for(int i = 0; i < padding; i++)
fprintf(stderr, " ");
fprintf(stderr,"%s[%s] - %s\n", keyname, lua\_typename(lua, lua\_type(lua, -2))
,lua\_typename(lua, lua\_type(lua, -1)));
lua\_pop(lua, 1);
}
}
return 0;
}
- 一个警告: 在一个迭代中,小心使用index=-1指定lua栈顶元素, 除非你确定你的确是要指向栈顶元素,而不是要指向当前的栈顶元素, 这么说可能有点绕,我举个例子来说明
我们看一个精简版的visit_table的例子
/* table is in the stack at index 't' */
lua\_pushnil(L); /* first key */
while (lua\_next(L, t) != 0) {
/* uses 'key' (at index -2) and 'value' (at index -1) */
printf("%s - %s\n",
lua\_typename(L, lua\_type(L, -2)),
lua\_typename(L, lua\_type(L, -1)));
/* removes 'value'; keeps 'key' for next iteration */
lua\_pop(L, 1);
}
如果这个table就在栈顶的话, 我们可不可以将t换成 -1呢?指向 栈顶的table? 答案是不可以, 如果换成了-1 那么 lua_next第一次执行之后因为取出了key-value对,目前栈顶的元素发生了改变,变成了这个table内此次迭代的, 而 index = -1还会去取栈顶的元素, 这时候lua_next访问的table就不是你想要访问的那个table了, 而是这个table内的某个value, lua_next就很可能无法继续下去了(说很可能无法继续下去的原因,是因为如果这个value是一个table的话, 那么lua_next还可以继续下去,只不过不可能得到正确的结果), 那么这种情况下如果想指定当前栈顶的元素为lua_next操作的table的话,应该这样指定:
int index = lua\_gettop(lua);
C C. Linux, kernel, Laravel, PHP, Python, Shell, Web, wine
Historical Comments
Post navigation ————— NEXT
[科普向?] Re: 从零开始的操作系统开发 第一集 PREVIOUS 要考试了 QWQ