C调用Lua函数
不要怀疑,对Lua函数的调用也是通过栈来进行的。请看如下代码:
Lua代码:
function lua_func (x, y)
print("Parameters are: ", x, y)
return (x^2 * math.sin(y))/(1-x)
end
C代码:
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.lua
Parameters are: 9 2
Return from lua:-9.206636
Lua调用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 = BLUE
C语言中使用如下方法读取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.lua
Read from lua table: 1.000000
C语言写入Lua中的表:
1、将要写入的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(lua_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在虚拟堆栈中的位置 */
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全局命名空间 */
}
运行结果:
pi@raspberrypi ~/Programming/article_lua $ ./a.out print_table.lua
Read talbe.r 0
Read talbe.g 0
Read talbe.b 1