Lua调用C语言:
C语言函数的原型必须为:
int C_Func(lua_State* L);
其中L为lua的状态机,返回值为返回给lua脚本的返回值的个数。
方法一:
利用lua_register函数,通过lua_register函数将被调用的C语言函数注册到Lua中,然后再Lua中就可以被调用了
如下:
Main.c:
int C_Func(lua_State* L)
{
printf("C_Func is run rn");
return 0;
}
int main(void)
{
lua_State* L = NULL;
L = luaL_newstate();
lua_register(L, "C_Func", C_Func);/*将C语言函数注册到Lua中*/
luaL_dofile(L, "test_01.lua");/*执行Lua脚本*/
lua_close(L);
return 0;
}
test_01.lua:
C_Func()
运行结果:
方法二:
利用函数luaL_newlib将C语言函数以库的方式加载到Lua环境中
例子如下:
Main.c:
/*需要被调用的C语言函数*/
int C_Func_01(lua_State* L)
{
printf("C_Func_01 is run rn");
return 0;
}
int C_Func_02(lua_State* L)
{
printf("C_Func_02 is run rn");
return 0;
}
/*定义函数表*/
static const luaL_Reg CFuncLib[] = {
{"C_Func1",C_Func_01},
{"C_Func2",C_Func_02},
{NULL,NULL}
};
/*创建一个新库*/
int luaopen_CFuncLib(lua_State* L) {
luaL_newlib(L, CFuncLib);
return 1;
}
int main(void)
{
lua_State* L = NULL;
L = luaL_newstate();
luaL_requiref(L, "CFuncLib", luaopen_CFuncLib, 1);/*将C语言函数库注册到Lua环境中*/
luaL_dofile(L, "test_01.lua");
lua_close(L);
return 0;
}
大致流程就是通过一个个函数组成一个函数表,这个表包含函数名和函数指针,然后利用luaopen_CFuncLib函数创建一个函数库,再通过luaL_requiref函数把这个函数库注册到Lua环境中,luaopen_CFuncLib函数通过参数将自己传入到luaL_requiref函数内部,luaL_requiref会检查库曾经有没有被加载过,如果没有,就调用luaopen_CFuncLib函数生成新库,然后注册到Lua环境中。
test_01.lua:
--注意这里,必须以"库名.函数名()"的方式调用,还可以以"库名:函数名()"的方式调用这两种方式稍有不同,下面介绍
CFuncLib.C_Func1()
CFuncLib.C_Func2()
--或
CFuncLib:C_Func1()
CFuncLib:C_Func2()
运行结果:
Lua传参给C语言函数:
传参是通过栈来传递的,每个被调用的C语言函数都有一个属于自己的栈,被调用的C语言函数能且只能访问自己的栈,如果超出限制范围,Lua环境会检测到错误并退出,栈可以通过索引来访问,从1,2......n分别表示栈底、栈底往上的第一个元素、栈底往上的第二个元素...,索引还可以是负数,-1,-2,...分别表示栈顶的元素、栈顶往下的第一个元素、栈顶往下的第二个元素...
示意图:
Lua给C语言传参时有固定顺序,如果Lua要给C语言函数传三个参数,那么再调用C语言函数之前,Lua环境会将这三个参数全部压栈,压栈顺序永远是第一个参数先压栈,第二个参数再压栈,即按参数的位置顺序入栈,所以第一个参数永远再栈底,第二个参数永远再栈底往上一个元素的那个位置。当然,也有例外的时候,当采用”库名:函数名()”的方式调用函数时,栈底的第一个元素并不是第一个参数,而是self,self是该函数的一个隐形参数,具体最后说明,先不考虑这种特殊情况。
一般值的传递(整数、布尔值、字符串等):
C语言函数返回给Lua值时,由于一个可以返回多个值,那么自然也有固定的顺序,不然分不清谁是谁了,这个顺序和Lua传参给C语言一样,第一个参数先入栈,第二个在入栈,即第一个参数永远再栈底,第二个此之,如此就可以按顺序一个个将返回值返回给Lua脚本了。
假设,要给一个C语言传一个整形参数,那么代码如下:
test_01.lua:
C_Func(0x55)
Main.c:
int C_Func(lua_State* L)
{
int arg1,temp1;
arg1 = lua_tointeger(L, 1);
temp1 = lua_tointeger(L, 1);
return 0;
}
运行结果:
lua_tointeger函数负责将栈中索引为1的那个地方(栈底)的值转换成一个整型数,由于该函数不会对栈的内容做任何修改,所以temp1的值也是0x55,和lua_tointeger类似的函数还有很多,负责将栈中指定位置的数据转换成想要的数据类型。
table类型数据的传递:
一般的数据类型这样就可以了,但对应table类型,这样却不行,因为table除了可以像数组那样按顺序索引,还可以用Key索引,C语言没有这种类型的数据,所以需要特殊处理,这里一般会用到这么几个函数:
int lua_gettable(lua_State *L, int idx);
lua_pushxxxx(lua_State *L, type idx);
lua_next(lua_State *L, int idx) ;
lua_gettable函数负责获取指定table中指定key所对应的值,L是的状态机,idx是指将栈中的第几个元素当作table,那key呢,key是栈顶中的值,所以便需要lua_pushxxxx系列的函数将key压倒栈顶,该函数有很多个,比如lua_pushstring、lua_pushinteger等,lua_gettable获取的值会不是直接返回的,因为它并不知道值的类型,它会把值放到栈顶,所以要真正获取到值,还需要调用lua_toxxx系列的函数将栈顶的值取出来,并且最后别忘了调用lua_pop函数将这个值出栈,因为这个值已经被取出来了,所以这个值不需要、也不能继续留在栈中,否则会影响下一步操作,所以获取参数table中指定位置的值的方法顺序为:
- 用lua_pushxxxx压入key,
- 调用lua_gettable获取table中的值,
- 调用lua_toxxx系列函数将值转换成对应类型的数据,
- 调用lua_pop函数将值出栈,避免对下一步操作造成影响。
例子如下:
Main.c:
int C_Func(lua_State* L)
{
const char* arg1;
lua_pushfstring(L, "name");
lua_gettable(L, 1);
arg1=lua_tostring(L, -1);
lua_pop(L, 1);
return 0;
}
Test_01.lua:
tab={["name"]="lua",["version"]="5.3.5"}
C_Func(tab)
运行结果:
如果需要遍历这个表,那就需要用到lua_next函数了,lua_next函数的功能为:从栈顶弹出一个值当作key,然后计算出下一个key值,并将其压栈,在根据取出来的key值取出表中对应的数据压到栈中,如果取出来的key为nil,则表示表中第一个数据,如果取出来的key在表中没有,则回返回0。
例子如下:
Main.c:
int C_Func(lua_State* L)
{
const char* arg1;
lua_pushnil(L);
while (lua_next(L,1))
{
printf("%srn", lua_tostring(L, -1));
lua_pop(L, 1);
}
/*到这里,栈恢复原样*/
return 0;
}
Test_01.lua
tab={["name"]="lua",["version"]="5.3.5"}
C_Func(tab)
运行结果:
.与:的区别:
通过”库名:函数名()”调用函数时,包含一个隐含参数,而通过”库名.函数名()”的方法调用时,则没有这个隐含参数。这个隐含参数如果有,则一定被存放在栈底,即参数列表中可见的参数中第一个参数并不是在栈底,而是在栈底上面一个元素。
C语言返回给Lua值:
Lua的函数可以返回多个返回值,而C语言只能有一个返回值,所以C语言函数返回给Lua值必然不是简单的return就可以了,实际上,可以被Lua调用的C语言函数return的值类型被强制设定为int型,返回值的大小代表了这个函数返回值的个数,而返回给Lua值,则是通过栈来实现的,将需要返回的数据压栈,然后通过return语句返回压入栈中数据的个数,告知Lua环境该函数有多少个返回值,然后Lua环境从栈中取值,将值赋给接收返回值的变量。既然可以有多个返回值,那么压栈的顺序肯定也得讲究一下,不然就乱套了。Lua中,压栈的顺序和外部接收返回值变量的顺序是一致的:最先入栈的数据会被第一个变量接收,第二个入栈的会被第二个变量接收...,以此类推,这点和Lua给C语言函数传参比较类似。
例子如下:
Main.c:
int C_Func(lua_State* L)
{
/*将需要返回的值压入栈中*/
lua_pushinteger(L, 523);
lua_pushstring(L,"这是一个返回值...");
/*return 2,表示这个函数有两个返回值*/
return 2;
}
Test_01.c:
value,str=C_Func()
print(value)
print(str)
运行结果: