Lua和C语言的交互
- 格式:docx
- 大小:44.02 KB
- 文档页数:19
Lua和C语言的交互(一)分类:lua2014-03-27 18:04700人阅读评论(1)收藏举报Lua生来就是为了和C交互的,因此使用C扩展Lua或者将Lua嵌入到C当中都是非常流行的做法。
要想理解C和Lua的交互方式,首先要回顾一下C语言是如何处理函数参数的。
C函数和参数大家知道C语言是用汇编实现的,在汇编语言中可没有函数的概念,与函数对应的是叫做子过程的东西,子过程就是一段指令,一个子过程与它调用的子过程之间通过栈来进行参数的传递交互。
在一个子过程在调用别的子过程之前,会按照约定的格式将要调用的子过程需要的参数入栈,在被调用的子过程中,可以按照约定的规则将参数从栈中取出。
同理,对于返回值的传递也同样是通过堆栈进行的。
C语言约定的参数放入栈中的格式,就是“调用惯例”。
C语言的函数原型则决定了压入栈中的参数的数量和类型。
Lua的虚拟堆栈Lua和C之间的交互巧妙的模拟了C语言的堆栈,Lua和C语言之间的相互调用和访问都通过堆栈来进行,巧妙的解决了不同类型之间变量相互访问的问题。
具体的,我们想象如下一个图 +-------+ +-------+| | | || | +-------+ | || C | <==> | | <==> | Lua || Space | |Virtual| | Space || | | Stack | | || | | | | |+-------+ +-------+ +-------+由于C和Lua是不同层次的语言,因此C语言的变量和Lua中的变量以及函数不能直接的交互,我们假定C语言和Lua都有自己的“空间(C Space和Lua Space)”。
而这两个空间之间的交互就通过上图中的这个虚拟堆栈来解决。
为何采用虚拟堆栈的方式来进行交互呢?其目的是在提供强大的灵活性的同时避免交互时两种语言变量类型的组合爆炸。
C语言读写Lua全局变量(基本类型)C语言读取Lua的全局变量是一种最简单的操作。
通过上图我们可以猜测到,如果通过C语言读取Lua中的全局变量需要两步:1、将全局变量从Lua Space压入虚拟堆栈;2、从堆栈将全局变量读取到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读取出来放入虚拟堆栈中 */global_var1 = lua_tonumber(L, -1); /* 从虚拟堆栈中读取刚才压入堆栈的变量,-1表示读取堆栈最顶端的元素 */printf("Read global var from C: %d\n", global_var1);}......执行结果:pi@raspberrypi ~/Programming/article_lua $ ./a.out global_var.luaPrint global var from lua 5Read global_var: 5Lua中对堆栈的操作都是通过索引来进行的,索引为1表示从栈底数第一个元素,索引为2表示从栈底数第二个元素;同样也可以使用负数从栈顶开始计算,-1表示从栈顶数第一个元素,-2表示从栈顶数第二个元素。
更多堆栈的操作函数请参考lua的官方手册/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"); /* 从lua的变量空间中将全局变量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(L, "global_var1");printf("set global var from C:9\n");}执行结果:pi@raspberrypi ~/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)收藏举报转载请标明出处/?p=666C调用Lua函数不要怀疑,对Lua函数的调用也是通过栈来进行的。
请看如下代码:Lua代码:function lua_func (x, y)print("Parameters are: ", x, y)return (x^2 * 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(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:%f\n", z);return z;}执行结果:pi@raspberrypi ~/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函数必须自己从堆栈中读取参数。
注意在Lua中调用函数时是不会做原型检查的,Lua代码调用C函数时传递几个参数,虚拟堆栈中就会有几个参数,因此C代码在从堆栈中读取参数的时候最好自己检查一下堆栈的大小和参数类型是否符合预期。
这里为了简化起见我们就不做类型检查了 */d = sin(d); /* 这里是C函数实现自己功能的代码*/lua_pushnumber(L, d); /* 在完成计算后,只需将结果重新写入虚拟堆栈即可(写入的这个值就是函数的返回值) */return 1; /* 函数的返回值是函数返回参数的个数。
没错,Lua函数可以有多个返回值。
*/}static void 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的table是一种重要的数据结构,因此对table的读写也是很重要的内容。
读取Table基本需要如下几步:1、使用lua_getglobal将表从Lua命名空间读取到虚拟堆栈中;2、使用lua_pushstring将要读取的字段的名称压入堆栈;3、使用函数lua_gettable,这个函数会将table和key出栈,然后把对应字段的值入栈;4、最后使用lua_toXXXX从堆栈中读取值并使用lua_pop将数值出栈将堆栈恢复到调用前的样子;不要纠结,我也觉得复杂,而且十分怀疑性能问题,但是Lua作者说Lua是一门快速的语言。
好吧,暂且听他的等,回来理解深入之后再读下代码一探究竟。
Lua代码中定义如下的table:BLUE = {r=0, g=0, b=1}background = 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读取table,其第二个参数为table在虚拟堆栈中的索引(-1为key,所以-2为table) */resault = lua_tonumber(L, -1); /* 将读取出的结果复制到C空间 */lua_pop(L, 1); /* 将结果出栈,将堆栈恢复成调用前的样子 */printf("Read from lua table: %f\n", resault);}...运行结果:pi@raspberrypi ~/Programming/article_lua $ ./a.out table.luaRead from lua table: 1.000000C语言写入Lua中的表:1、将要写入的table放入堆栈,可以新建也可以写入现有table;2、将要写入的键压入堆栈;3、将要写入的值压入堆栈;4、调用lua_settable执行table的写入5、如果是新建table的话,最后需要使用lua_setglobal,将修改后的table写会lua全局变量。