1、Lua 和 C 语言的交互(一) 分类: lua2014-03-27 18:04700 人阅读评论(1)收藏举报Lua 生来就是为了和 C 交互的,因此使用 C 扩展 Lua 或者将 Lua 嵌入到 C 当中都是非常流行的做法。要想理解 C 和 Lua 的交互方式,首先要回顾一下 C 语言是如何处理函数参数的。C 函数和参数大家知道 C 语言是用汇编实现的,在汇编语言中可没有函数的概念,与函数对应的是叫做子过程的东西,子过程就是一段指令,一个子过程与它调用的子过程之间通过栈来进行参数的传递交互。在一个子过程在调用别的子过程之前,会按照约定的格式将要调用的子过程需要的参数入栈,在被调用的子过程中
2、,可以按照约定的规则将参数从栈中取出。同理,对于返回值的传递也同样是通过堆栈进行的。C 语言约定的参数放入栈中的格式,就是“ 调用惯例”。C 语言的函数原型则决定了压入栈中的参数的数量和类型。Lua 的虚拟堆栈Lua 和 C 之间的交互巧妙的模拟了 C 语言的堆栈,Lua 和 C 语言之间的相互调用和访问都通过堆栈来进行,巧妙的解决了不同类型之间变量相互访问的问题。具体的,我们想象如下一个图+-+ +-+| | | | | +-+ | | C | | | | Lua | Space | |Virtual| | Space | | | Stack | | | | | | | |+-+ +-+ +
3、-+由于 C 和 Lua 是不同层次的语言,因此 C 语言的变量和 Lua 中的变量以及函数不能直接的交互,我们假定 C 语言和 Lua 都有自己的“ 空间(C Space 和 Lua Space)” 。而这两个空间之间的交互就通过上图中的这个虚拟堆栈来解决。为何采用虚拟堆栈的方式来进行交互呢?其目的是在提供强大的灵活性的同时避免交互时两种语言变量类型的组合爆炸。C 语言读写 Lua 全局变量(基本类型)C 语言读取 Lua 的全局变量是一种最简单的操作。通过上图我们可以猜测到,如果通过 C 语言读取 Lua 中的全局变量需要两步:1、将全局变量从 Lua Space 压入虚拟堆栈;2、从堆栈
4、将全局变量读取到 C 语言 Space 中。在 Lua 和 C 的交互中,Lua 无法看到和操作虚拟堆栈,仅在 C 语言中有操作堆栈的权利,因此前面说到的两步全都是在 C 语言中完成的。我们看一个简单的例子Lua 代码:global_var1 = 5;print(“Print global varb from lua“, global_var1);C 代码:void get_global(lua_State *L)int global_var1;lua_getglobal(L, “global_var1“); /* 从 lua 的变量空间中将全局变量 global_var1 读取出来放入虚拟堆
5、栈中 */global_var1 = lua_tonumber(L, -1); /* 从虚拟堆栈中读取刚才压入堆栈的变量, -1 表示读取堆栈最顶端的元素 */printf(“Read global var from C: %dn“, global_var1);执行结果:piraspberrypi /Programming/article_lua $ ./a.out global_var.luaPrint global var from lua 5Read global_var: 5Lua 中对堆栈的操作都是通过索引来进行的,索引为 1 表示从栈底数第一个元素,索引为 2 表示从栈底数第二个元
6、素;同样也可以使用负数从栈顶开始计算,-1 表示从栈顶数第一个元素,-2表示从栈顶数第二个元素。更多堆栈的操作函数请参考 lua 的官方手册 http:/www.lua.org/manual/5.2/manual.html。 同样从堆栈中获取元素,除了我们使用的 lua_tonumber 之外,还有lua_tolstring,lua_toboolean 等。通常情况下在读取变量之前还需要对堆栈中元素的实际类型做出检查:C 代码:void get_global(lua_State *L)int global_var1;lua_getglobal(L, “global_var1“); /* 从 l
7、ua 的变量空间中将全局变量 global_var1 读取出来放入虚拟堆栈中 */if (!lua_isnumber(L, -1) /* 检查堆栈中栈顶第一个元素是否是数字 */error(L, “Is not number.“);global_var1 = lua_tonumber(L, -1); /* 从虚拟堆栈中读取刚才压入堆栈的变量, -1 表示读取堆栈最顶端的元素 */写入全局变量也一样简单:首先将数据压入堆栈,然后再将堆栈中的数据存入全局变量。C 代码:void set_global(lua_State *L)lua_pushinteger(L, 9);lua_setglobal(
8、L, “global_var1“);printf(“set global var from C:9n“);执行结果:piraspberrypi /Programming/article_lua $ ./a.out global_var.luaset global var from C:9Print global var from lua 9Lua 和 C 语言的交互(二) 分类: lua2014-03-27 18:03467 人阅读评论(0)收藏举报转载请标明出处 http:/www.grati.org/?p=666C 调用 Lua 函数不要怀疑,对 Lua 函数的调用也是通过栈来进行的。请看
9、如下代码:Lua 代码:function lua_func (x, y)print(“Parameters are: “, x, y)return (x2 * math.sin(y)/(1-x)endC 代码:double c_func(lua_State *L, double x, double y)double z;lua_getglobal(L, “lua_func“); /* 首先将 lua 函数从 Lua Space 放入虚拟堆栈中 */lua_pushnumber(L, x); /* 然后再把所需的参数入栈 */lua_pushnumber(L, y);if (lua_pcall(
10、L, 2, 1, 0) != 0) /* 使用 pcall 调用刚才入栈的函数,pcall 的参数的含义为:pcall(Lua_state, 参数格式, 返回值个数, 错误处理函数所在的索引),最后一个参数暂时先忽略 */error(L, “error running lua function: $s“, lua_tostring(L, -1);z = lua_tonumber(L, -1); /* 将函数的返回值读取出来 */lua_pop(L, 1); /* 将返回值弹出堆栈,将堆栈恢复到调用前的样子 */printf(“Return from lua:%fn“, z);return z;
11、执行结果:piraspberrypi /Programming/article_lua $ ./a.out lua_func.luaParameters are: 9 2Return from lua:-9.206636Lua 调用 C 函数Lua 调用 C 函数其实就是用 C 编写 Lua 的扩展,使用 C 为 Lua 编写扩展也非常简单。所有 C 扩展的函数都有一个固定的函数原型,如下所示:C 代码:static int l_sin (lua_State *L)double d = lua_tonumber(L, 1); /* 不出意外,Lua 中的参数也是通过虚拟堆栈传递的。因此 C 函
12、数必须自己从堆栈中读取参数。注意在 Lua 中调用函数时是不会做原型检查的,Lua 代码调用 C 函数时传递几个参数,虚拟堆栈中就会有几个参数,因此 C 代码在从堆栈中读取参数的时候最好自己检查一下堆栈的大小和参数类型是否符合预期。这里为了简化起见我们就不做类型检查了 */d = sin(d); /* 这里是 C 函数实现自己功能的代码 */lua_pushnumber(L, d); /* 在完成计算后,只需将结果重新写入虚拟堆栈即可(写入的这个值就是函数的返回值) */return 1; /* 函数的返回值是函数返回参数的个数。没错, Lua 函数可以有多个返回值。 */static voi
13、d regist_func(lua_State *l) /* 这个函数将 C 函数写入 Lua 的命名空间中。 */lua_pushcfunction(l, l_sin);lua_setglobal(l, “mysin“);将函数写入 Lua 全局命令空间的代码很简单,和写入全局变量的代码一样,都是先将 C 函数压入堆栈,然后再将虚拟堆栈中的函数指针写入 Lua 全局命名空间并将其命名为”mysin”。之后在Lua 中就可以使用”ret = mysin(30)”这样的形式调用我们的 C 函数了。C 语言读取 Lua 中的表C 语言读取 Lua table 会稍微复杂一点,不过 Lua 的 ta
14、ble 是一种重要的数据结构,因此对 table 的读写也是很重要的内容。读取 Table 基本需要如下几步:1、使用 lua_getglobal 将表从 Lua 命名空间读取到虚拟堆栈中;2、使用 lua_pushstring 将要读取的字段的名称压入堆栈;3、使用函数 lua_gettable,这个函数会将 table 和 key 出栈,然后把对应字段的值入栈;4、最后使用 lua_toXXXX 从堆栈中读取值并使用 lua_pop 将数值出栈将堆栈恢复到调用前的样子;不要纠结,我也觉得复杂,而且十分怀疑性能问题,但是 Lua 作者说 Lua 是一门快速的语言。好吧,暂且听他的等,回来理解
15、深入之后再读下代码一探究竟。Lua 代码中定义如下的 table:BLUE = r=0, g=0, b=1background = BLUEC 语言中使用如下方法读取 table:.static void read_table(lua_State *L)double resault;lua_getglobal(L, “background“); /* 将表从 lua 空间复制到虚拟堆栈(应该是仅拷贝索引,否则速度无法保证) */lua_pushstring(L, “b“); /* 将要读取的键压入虚拟堆栈 */lua_gettable(L, -2); /* 使用 lua_gettable 读取
16、 table,其第二个参数为 table 在虚拟堆栈中的索引(-1 为 key,所以-2 为 table) */resault = lua_tonumber(L, -1); /* 将读取出的结果复制到 C 空间 */lua_pop(L, 1); /* 将结果出栈,将堆栈恢复成调用前的样子 */printf(“Read from lua table: %fn“, resault);.运行结果:piraspberrypi /Programming/article_lua $ ./a.out table.luaRead from lua table: 1.000000C 语言写入 Lua 中的表:1
17、、将要写入的 table 放入堆栈,可以新建也可以写入现有 table;2、将要写入的键压入堆栈;3、将要写入的值压入堆栈;4、调用 lua_settable 执行 table 的写入5、如果是新建 table 的话,最后需要使用 lua_setglobal,将修改后的 table 写会 lua 全局变量。Lua 代码:print (“Read talbe.r“, background.r)print (“Read talbe.g“, background.g)print (“Read talbe.b“, background.b)C 代码:static void write_table(lu
18、a_State *L)lua_newtable(L); /* 新建 table 并放入堆栈。对于 lua 空间中没有 table 的情况可以使用 lua_newtable 新建一个 table;如果是写入已有 table,则应该使用lua_getglobal 将数据从 lua 空间读入虚拟堆栈 */lua_pushstring(L, “r“); /* 将要写入的键压入堆栈 */lua_pushnumber(L, (double)0); /* 将要写入的值压入堆栈 */lua_settable(L, -3); /* 执行 table 的写入,函数的第二个参数是 table 在虚拟堆栈中的位置 *
19、/lua_pushstring(L, “b“); /* 重复三次,一共写入了“r“, “g“, “b“ 三个成员 */lua_pushnumber(L, (double)1);lua_settable(L, -3);lua_pushstring(L, “g“);lua_pushnumber(L, (double)0);lua_settable(L, -3);lua_setglobal(L, “background“); /* 最后将新 table 写入 lua 全局命名空间 */运行结果:piraspberrypi /Programming/article_lua $ ./a.out prin
20、t_table.luaRead talbe.r 0Read talbe.g 0Read talbe.b 1Lua 和 C 语言的交互(三) 分类: lua2014-03-27 17:58307 人阅读评论(0)收藏举报自定义数据类型:我们通过使用 C 语言实现一个 Lua 数组来演示 Lua 实现自定义用户数据。数组的结构如下所示:typedef struct NumArrayint size; /表示数组的大小double values; /此处的 values 仅代表一个 double*类型的指针,values 指向 NumArray 结构后部紧跟的数据的地址 NumArray;我们实现四
21、个函数 new,get,set 和 size,分别用来完成数组的新建,读取,写入和获取大小。在 Lua 中用来实现自定义数据结构的类型叫做 userdata。Lua 提供了如下接口用来创建userdata: void *lua_newuserdata(lua_State *L, size_t size),该函数分配 size 大小的内存作为 userdata 并将其压入栈,函数的返回值为新建立的 userdata,可以自由转换为所需的数据结构。实现自定义数据结构的 C 代码如下:/* 新建 array */static int newarray (lua_State *L)int n = lu
22、aL_checkint(L, -1); /检查参数,数组的个数必须是整数size_t nbytes = sizeof(NumArray) + n*sizeof(double); /计算 C 语言结构所需的内存空间,nbytes 的长度包括数组结构头和其后部的数据NumArray *a = (NumArray *)lua_newuserdata(L, nbytes); /新建一个大小为 nbytes 的 userdata 并压入堆栈。a-size = n; /初始化 NumArray 的大小return 1;/* 设置 array 中的数值 */static int setarray(lua_S
23、tate *L) NumArray *a = (NumArray *)lua_touserdata(L, -3); /将堆栈中的 userdata 读取出来int index = luaL_checkint(L, -2); /读取索引double value = luaL_checknumber(L, -1); /读取数值luaL_argcheck(L, NULL != a, 1, “array expected“); /检查参数的返回,如果第二个表达式为假,则抛出最后一个参数指定的错误信息luaL_argcheck(L, index = 0 a-valuesindex - 1 = value
24、; /将 lua 中写入的数值设置到 C 数组中return 0;/* 读取 array 中的数值 */static int getarray(lua_State *L) NumArray *a = (NumArray *)lua_touserdata(L, -2); /前面的步骤和 setarray 中的相同int index = luaL_checkint(L, -1);luaL_argcheck(L, NULL != a, 1, “array expected“);luaL_argcheck(L, index = 1 lua_pushnumber(L, a-valuesindex - 1
25、); /将 C 数组中的数值压入堆栈return 1;/* 获取 array 的大小 */static int getsize(lua_State *L) NumArray *a = (NumArray *)lua_touserdata(L, -1);luaL_argcheck(L, NULL != a, 1, “array expected“);lua_pushnumber(L, a-size);return 1;/* 将我们定义的四个函数写成数组的形式,在主函数中可以使用 luaL_openlib 将四个函数一口气注册到 lua 空间 */static const struct luaL_
26、Reg arraylib = “new“, newarray,“set“, setarray,“get“, getarray,“size“, getsize,NULL, NULL;int main(int argc, char* argv)luaL_openlib(L, “array“, arraylib, 0); /注册刚才实现的四个函数到全局变量 array 下,其名称分别为 new,set,get 和 size。return 0;对应的 Lua 程序为:a = array.new(1000)print(a)print(array.size(a)for i=1,999 doarray.se
27、t(a, i, 1/i)endprint (“Print first 10 elements“)for i=1,10 doprint(array.get(a, i) )end运行结果是:piraspberrypi /Programming/article_lua $ ./a.out userdata3.luauserdata: 0x20237b01000Print first 10 elements10.50.333333333333330.250.20.166666666666670.142857142857140.1250.111111111111110.1但是,上述程序有两个问题:1、参
28、数中仅检查了用户的输入参数是否是 userdata,并没有区分实际的类型,如果用户传递 array.get(io.stdin, 200),就会造成 Sagement fault,这样的行为是不可接受的。2、用户仅能使用 array.size(a),和 array.get(a,40)的形式访问内容,能否使用类似于面向对象特性的方式如 a:size()和 a:get(40)的形式呢? 答案是肯定的,因此必须引入metatable 的机制。我们假定读者已经对 lua 语言本身和在 Lua 中使用 metatable 有一定的了解,这里仅介绍在 C 语言中如何为 userdata 添加 metatab
29、le。在对于 userdata 的 metatable 有如下几点需要注意:1、metatable 在 lua 中仅是一个普通的 table,但是在 lua 提供给 C 语言的接口中,metabtable 需要使用专门的接口来创建和读取。(原因暂时不详)2、lua 的 metatable 和 javascript 的 prototype 不同。如果访问对象缺失的方法,javascript 会直接从 prototype 指向的对象中查找缺失的方法,但 lua 不同,他不会直接从 metatable 中查找,而是会从 metatable 的_index 域所指向的对象中查找。如果我们设置 obje
30、ct.metatable._index = object.metatable,这样就和 javascript 类似了。对于对象缺失的方法,会直接从 metatable中查找。3、对于 userdata 而言其什么方法都没有,因此对他的成员的访问都是通过访问 metatable._index 来实现的(如果设置 metatable._index = metatable,那么访问 userdata 的成员就相当于访问 userdata 的 metatable)。使用 metatable 修改后的程序如下所示:/* 将要注册的函数拆分为两部分arraylib_f 注册给全局变量 arrayarray
31、lib_m 注册给 metatable,作为 methord*/static const struct luaL_Reg arraylib_f = “new“, newarray,NULL, NULL;static const struct luaL_Reg arraylib_m = “set“, setarray,“get“, getarray,“size“, getsize,NULL, NULL;/* C 函数的安装使用如下函数整个函数中我们创建了一个匿名的 metatable,后续注释中的 metatable 一词代指这个新建的 metatable 的实例*/static void in
32、stall_func(lua_State *L)luaL_newmetatable(L, “LuaBook.array“); /新建一个 metatable,一方面压入堆栈,另一方面将 metatable 以“LuaBook.array“为 key 放入 register 中(register 类似于一个全局哈希表)lua_pushstring(L, “_index“);lua_pushvalue(L, -2);lua_settable(L, -3); /这三步的作用是将新建的 metatable 的_index 字段赋值为他自己,相当于 metatable._index = metatabl
33、e,尽是为了方便,否则还需要另外建立一个 table2,并设置 metatable._index = table2luaL_openlib(L, NULL, arraylib_m, 0); /将 set、get、size 这三个函数注册给 metatable(给 luaL_openlib 函数传递 NULL 时,表示要操作的表已经放在堆栈中)luaL_openlib(L, “array“, arraylib_f, 0); /将 new 这个函数注册给全局变量 arraystatic int newarray (lua_State *L)int n = luaL_checkint(L, 1);s
34、ize_t nbytes = sizeof(NumArray) + n*sizeof(double);NumArray *a = (NumArray *)lua_newuserdata(L, nbytes);luaL_getmetatable(L, “LuaBook.array“);lua_setmetatable(L, -2); /新建 userdata 的时候将 register 中名为 LuaBook.array 中 metatable 设置给新建的 array 对象,从此所有 array 共用一个 metatablea-size = n;return 1;static int seta
35、rray(lua_State *L) NumArray *a = (NumArray *)luaL_checkudata(L, -3, “LuaBook.array“); /调用函数的时候,除了检查参数是否为 userdata 之外,还要判断该 userdata 的 metatable 是否是“LuaBook.array“,这样在使用 io.stdin 调用我们的函数时就可以检查出错误,不至造成 Sagement faultint index = luaL_checkint(L, -2);double value = luaL_checknumber(L, -1);luaL_argcheck(
36、L, NULL != a, 1, “array expected“);luaL_argcheck(L, index = 0 a-valuesindex - 1 = value;return 0;static int getarray(lua_State *L) NumArray *a = (NumArray *)luaL_checkudata(L, -2, “LuaBook.array“); /同样增加参数类型和 metatable 的检查int index = luaL_checkint(L, -1);luaL_argcheck(L, NULL != a, 1, “array expecte
37、d“);luaL_argcheck(L, index = 0 lua_pushnumber(L, a-valuesindex - 1);return 1;static int getsize(lua_State *L) NumArray *a = (NumArray *)luaL_checkudata(L, -1, “LuaBook.array“); /同样增加参数类型和 metatable 的检查luaL_argcheck(L, NULL != a, 1, “array expected“);lua_pushnumber(L, a-size);return 1;int main(int ar
38、gc, char* argv)install_func(L); /使用 install_func 将函数注册到 lua 空间对应 Lua 的例子变为:a = array.new(1000)print(a)print(a:size()for i=1,999 doa:set(i, 1/i)endprint (“Print first 10 elements“)for i=1,10 doprint(a:get(i)end文章中用到的示例程序:最后给出一个 main 函数的例子,在这个函数中我们可以添加前面说到的示例代码从而组合出完整的示例程序。int main(int argc, char* arg
39、v)char* filename;double ret = 0;/新建一个 lua statelua_State *L = luaL_newstate();if (argc =2 )filename = argv1; else printf(“usage: %s filenamen“, argv0);return 1;/这个函数加载 lua 标准库luaL_openlibs(L);/* 在这里添加代码注册 C 语言实现的函数 */* 从指定的文件名加载 lua 代码(实际上代码被编译成 chunk 压入栈中) */if (luaL_loadfile(L, filename)error(L, “
40、cannot run file: %s“, lua_tostring(L, -1);/* 执行刚才读取的 lua 代码 */if (lua_pcall(L, 0, 0, 0)error(L, “cannot run file: %s“, lua_tostring(L, -1);/* 如果调用 lua 函数,要放在这里 */return 0;写在最后:本文假定读者对 Lua 的基本语法已经有了一定的了解。由于 Lua 是原型继承语言,和我们之前使用的基于类型的语言有些区别(倒是和 Javascript 类似,Javascript 也是原型继承语言)。因此在开始学习的过程一定要跳出类和对象的思维才
41、能真正理解 Lua。最后推荐两个学习 Lua 的优秀材料:官方手册:http:/www.lua.org/manual/查起来很方便,如果用它来学习会很困难。作者的著作:Programming in Lua,第一版免费基于 Lua5.0 的 http:/www.lua.org/pil/contents.html,网上可以搜到一个中译的版本,个人感觉学习足够了,当然也可以在 amazon 上购买英文第三版。lua 和 C/C+语言之间的调用 分类: lua2013-11-29 11:21148 人阅读评论(0)收藏举报gccc 脚本 lua 与 cplain view plaincopyprint
42、?1.lua 作为小巧精悍的脚本语言,易于嵌入 c/c+中 , 广泛应用于游戏 AI ,实际上在任何经常变化的逻辑上都可以使用 lua 实现,配合 c/c+实现的底层接口服务,能够大大降低系统的维护成本。下面对 lua 和 c/c+的交互调用做一个实例分析:lua 提供了 API 用于在 c/c+中构造 lua 的运行环境,相关接口如下:/创建 lua 运行上下文lua_State* luaL_newstate(void) ;/加载 lua 脚本文件int luaL_loadfile(lua_State *L, const char *filename);lua 和 c/c+的数据交互通过“栈
43、“进行 ,操作数据时,首先将数据拷贝到“栈“ 上,然后获取数据,栈中的每个数据通过索引值进行定位,索引值为正时表示相对于栈底的偏移索引,索引值为负时表示相对于栈顶的偏移索引,索引值以 1 或-1 为起始值,因此栈顶索引值永远为-1 ,栈底索引值永远为 1 。 “栈“相当于数据在 lua 和 c/c+之间的中转地。每种数据都有相应的存取接口。数据入“栈“ 接口:void (lua_pushnil) (lua_State *L);void (lua_pushnumber) (lua_State *L, lua_Number n);void (lua_pushinteger) (lua_State
44、*L, lua_Integer n);void (lua_pushlstring) (lua_State *L, const char *s, size_t l);void (lua_pushstring) (lua_State *L, const char *s);void (lua_pushboolean) (lua_State *L, int b);void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);数据获取接口:lua_Number (lua_tonumber) (lua_State*L, int idx);l
45、ua_Integer (lua_tointeger)(lua_State *L, int idx);int (lua_toboolean)(lua_State *L, int idx);const char *(lua_tolstring)(lua_State *L, int idx, size_t *len);lua_CFunction (lua_tocfunction)(lua_State *L, int idx);“栈“操作接口:int (lua_gettop) (lua_State *L);void (lua_settop) (lua_State *L, int idx);void (
46、lua_pushvalue) (lua_State *L, int idx);void (lua_remove) (lua_State *L, int idx);void (lua_insert) (lua_State *L, int idx);void (lua_replace) (lua_State *L, int idx);int (lua_checkstack) (lua_State *L, intsz);lua 中定义的变量和函数存放在一个全局 table 中,索引值为LUA_GLOBALSINDEX,table 相关操作接口:void (lua_gettable) (lua_Sta
47、te *L, int idx);void (lua_getfield) (lua_State *L, int idx, const char *k);void (lua_settable) (lua_State *L, int idx);void (lua_setfield) (lua_State *L, int idx, const char *k);当“栈 “中包含执行脚本需要的所有要素( 函数名和参数)后,调用 lua_pcall 执行脚本:int (lua_pcall) (lua_State *L, intnargs, int nresults, int errfunc);func.l
48、ua -变量定义 width, height = 1, 2 ; -lua 函数定义,实现加法 function sum(a, b) return a + b;end -lua 函数定义,实现字符串相加 function mystrcat(a, b) return a b;end -lua 函数定义,通过调用 c 代码中的 csum 函数实现加法 function mysum(a, b) return csum(a, b);endC 程序:test_lua.ccpp view plaincopyprint?1. #include 2. #include 3. #include 4. #include 5. #include 6.7. /lua 头文件 8. #include 9. #include 10. #include 11.12. /lua 中调用的 c 函数定义,实现加法 13. int csum(lua_State *l) 14. 15. int a=lua_tointeger(l, 1); 16. int b=lua_tointeger(l, 2); 17. lua_pushinteger(l, a+b) ; 18. return 1; 19. 20.21. int main(int argc, char *a