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()



运行结果:




lua 调用yolo lua 调用程序_lua


方法二:

利用函数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 调用yolo lua 调用程序_c语言 方程改main的值_02


Lua传参给C语言函数:

传参是通过栈来传递的,每个被调用的C语言函数都有一个属于自己的栈,被调用的C语言函数能且只能访问自己的栈,如果超出限制范围,Lua环境会检测到错误并退出,栈可以通过索引来访问,从1,2......n分别表示栈底、栈底往上的第一个元素、栈底往上的第二个元素...,索引还可以是负数,-1,-2,...分别表示栈顶的元素、栈顶往下的第一个元素、栈顶往下的第二个元素...

示意图:


lua 调用yolo lua 调用程序_lua_03


Lua给C语言传参时有固定顺序,如果Lua要给C语言函数传三个参数,那么再调用C语言函数之前,Lua环境会将这三个参数全部压栈,压栈顺序永远是第一个参数先压栈,第二个参数再压栈,即按参数的位置顺序入栈,所以第一个参数永远再栈底,第二个参数永远再栈底往上一个元素的那个位置。当然,也有例外的时候,当采用”库名:函数名()”的方式调用函数时,栈底的第一个元素并不是第一个参数,而是selfself是该函数的一个隐形参数,具体最后说明,先不考虑这种特殊情况。

一般值的传递(整数、布尔值、字符串等):

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 调用yolo lua 调用程序_c语言状态机_04


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_pushstringlua_pushinteger等,lua_gettable获取的值会不是直接返回的,因为它并不知道值的类型,它会把值放到栈顶,所以要真正获取到值,还需要调用lua_toxxx系列的函数将栈顶的值取出来,并且最后别忘了调用lua_pop函数将这个值出栈,因为这个值已经被取出来了,所以这个值不需要、也不能继续留在栈中,否则会影响下一步操作,所以获取参数table中指定位置的值的方法顺序为:

  1. lua_pushxxxx压入key,
  2. 调用lua_gettable获取table中的值,
  3. 调用lua_toxxx系列函数将值转换成对应类型的数据,
  4. 调用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 调用yolo lua 调用程序_c语言 方程改main的值_05


如果需要遍历这个表,那就需要用到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)


运行结果:


lua 调用yolo lua 调用程序_lua 调用yolo_06


.与:的区别:

通过”库名:函数名()”调用函数时,包含一个隐含参数,而通过”库名.函数名()”的方法调用时,则没有这个隐含参数。这个隐含参数如果有,则一定被存放在栈底,即参数列表中可见的参数中第一个参数并不是在栈底,而是在栈底上面一个元素。

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)


运行结果:


lua 调用yolo lua 调用程序_lua 调用yolo_07