Lua与C/C++的交互6:Lua调用C/C++函数

前几章都是C/C++调用Lua函数,即用Lua扩展C/C++,此时C/C++是应用程序代码,拥有主控制权。这次我想从Lua中调用C/C++,即把C/C++当作库程序,此时Lua拥有主控制权。扩展Lua的一项基本含义就是,应用程序将新的C/C++函数注册到Lua中。Lua调用C/C++函数时,也是通过一个stack来进行的。C/C++函数从这个stack中获取Lua中传过来的函数参数,同时把结果压入这个栈中,最后返回一个其压入stack中的结果数量(为了在栈中将函数结果与其他值区分开来)。很重要的一点,要记住,当注册的C/C++函数被Lua调用时,这个C/C++函数享有一个独有的stack,再简单点说就是,当进入到这个被注册的C/C++函数体时,里面所有的stack操作都是基于它的这个独有栈进行的。这个stack在进入函数体时生成,它里面从索引1到n,分别存放着从Lua程序那边传过来的第1到第n个函数参数。这些C/C++函数只能看到自己的私有stack,其他stack跟它无关。
以下是完整源代码:

#include "stdafx.h"
#include "stdio.h"
extern "C"
{
	#include "lua.h"
	#include "lualib.h"
	#include "lauxlib.h"
};

#pragma comment(lib,"lua5.1.lib")

//依次打印stack中元素(从栈底 -> 栈顶)
void stackDump(lua_State *L)
{
	int i;
	int top = lua_gettop(L);
	printf("the size of stack is:%d\n",top);
	for ( i = 1;i <= top;i++ )
	{
		int type = lua_type(L, i);
		switch(type)
		{
		case LUA_TSTRING:
			{
				printf("%s",lua_tostring(L, i));
				break;
			}

		case LUA_TBOOLEAN:
			{
				printf(lua_toboolean(L, i)?"true":"false");
				break;
			}
		case LUA_TNUMBER:
			{
				printf("%g",lua_tonumber(L, i));
				break;
			}
		case LUA_TTABLE:
			{
				printf("this is a table!");
				break;
			}
		default:
			{
				printf("%s",lua_typename(L ,i));
				break;
			}
		}
		printf(" ");
	}
	printf("\n");
}

//Call Lua function again
double fireAgain(lua_State *L, double x, double y)
{
	printf("Eneter C function fireAgain\n");
	/* From index 1 to n,stack value is:4.0  9.0  36.0 */
	stackDump(L);
	double res;
	/*压入函数跟参数*/
	lua_getglobal(L, "fireAgain");//待调用的Lua函数
	lua_pushnumber(L, x);	//压入第一个参数
	lua_pushnumber(L, y);	//压入第二个参数

	/* From index 1 to n,stack value is:4.0  9.0  36.0  function  16.0  81.0 */
	stackDump(L);
	/*完成调用(2个参数,一个返回结果)*/
	if ( lua_pcall(L, 2, 1, 0) != 0 )
		printf("run function 'f' error:%s",lua_tostring(L, -1) );

	/* From index 1 to n,stack value is:4.0  9.0  36.0  1296.0  */
	stackDump(L);
	/*检索结果*/
	if ( !lua_isnumber(L, -1) )
		printf("function 'fire' must return a number");

	res = lua_tonumber(L, -1);
	lua_pop(L, 1);//弹出结果
	/* From index 1 to n,stack value is:4.0  9.0  36.0 */
	stackDump(L);
	return res;
}

//C funtion need to be registered into Lua
static int plus(lua_State *L)
{
	printf("Enter C function plus\n");
	/* From index 1 to n,the stack value is:4.0  9.0  */
	stackDump(L);
	double x = 0.0;
	double y = 0.0;

	/* Get parameters from stack(this stack is private stack belong to fucntion plus ) */
	if ( lua_isnumber(L, 1) )
		x = lua_tonumber(L, 1);
	if ( lua_isnumber(L, 2) )
		y = lua_tonumber(L, 2);

	double result = x * y;
	lua_pushnumber(L, result);		//Push result to stack
	fireAgain(L, x * x, y * y);	//Call Lua function
	return 1;
}

//Registered C module
static const struct luaL_Reg myLib[] = 
{
	{"plus", plus},
	{NULL, NULL}
};

//Call Lua function fire 
double fire(lua_State *L, double x, double y)
{
	double res;
	/*压入函数跟参数*/
	lua_getglobal(L, "fire");//待调用的Lua函数
	lua_pushnumber(L, x);	//压入第一个参数
	lua_pushnumber(L, y);	//压入第二个参数

	/* From index 1 to n,stack value is: function 2.0  3.0  */
	stackDump(L);
	/*完成调用(2个参数,一个返回结果)*/
	if ( lua_pcall(L, 2, 1, 0) != 0 )
		printf("run function 'f' error:%s",lua_tostring(L, -1) );

	/* From index 1 to n,stack value is:36.0 */
	stackDump(L);
	/*检索结果*/
	if ( !lua_isnumber(L, -1) )
		printf("function 'fire' must return a number");

	res = lua_tonumber(L, -1);
	lua_pop(L, 1);//弹出结果
	/* Now the stack is empty */
	stackDump(L);
	return res;

}

//注册C函数到表中(C -> stack)
int luaopen_mylib(lua_State *L)
{
	luaL_register(L, "mylib", myLib);
	lua_setglobal(L, "mylib");
	return 1;
}

//Lua call C function
void luaCallCFunction(lua_State *L, const char *fname)
{
	if ( luaL_loadfile(L, fname) || lua_pcall(L, 0, 0 ,0) )
		printf("error,can't run config file:%s:",lua_tostring(L, -1));
	luaopen_mylib(L);
	stackDump(L);
	/* 调用lua函数 */
	double result = fire(L, 2.0, 3.0);
	printf("the result is %f:",result);

}

int _tmain(int argc, _TCHAR* argv[])
{
	lua_State *L;
	L = luaL_newstate();
	luaL_openlibs(L);	
	luaCallCFunction(L, "luaCallCFunction.lua");
	return 0;
}

打印结果如下:

the size of stack is:0

the size of stack is:3
boolean 2 3
>>>>>>>>Enter Lua function:fire
Enter C function plus
the size of stack is:2
4 9
Eneter C function fireAgain
the size of stack is:3
4 9 36
the size of stack is:6
4 9 36 string 16 81
>>>>>>>>>>>Enter Lua function fireAgain
the size of stack is:4
4 9 36 1296
the size of stack is:3
4 9 36
the size of stack is:1
36
the size of stack is:0

the result is:36.000000

其中luaCallCFunction.lua中内容如下:

function fire(x, y)
	print(">>>>>>>>Enter Lua function:fire")
	x = x * 2
	y = y * 3
	local result = mylib.plus(x, y)
	return result
end

function fireAgain(x, y)
	print(">>>>>>>>>>>Enter Lua function fireAgain")
	local result = x * y
	return result
end

详细讲解下C/C++函数的注册过程。要注册C/C++函数到Lua中,我们需要用到这个函数:void luaL_register (lua_State *L, const char *libname, const luaL_Reg *l),它首先创建一个表,然后将list中(luaL_Reg)的所有函数都注册到这个表中,并设置它为全局变量libname,最后在栈顶留下这个表。在Lua文件中,我们就可以通过libname这个全局变量(可看作一个包)来获取注册过的C函数或是其他C中内容。其实Lua模块是一个程序块(chunk),其中定义了一些Lua函数,这些函数通常存储为table的条目。利用这种思想,我们将那些为Lua编写的C函数组装到一个C模块中,然后注册这个模块到Lua中,这样Lua中就可以用table的形式调用这些C函数了。Lua通过上面的注册过程记录下C函数,然后使用这些函数地址直接调用它。所以,Lua调用C函数时,并不依赖函数名,包的位置,或可见性规则,而只依赖于注册时传入的函数地址。
这里,我们要记住以下4条:1.注册的C模块需要有一个主函数,这个主函数形式为typedef int (*lua_CFunction) (lua_State *L);它注册模块中所有的C函数,并将它们存储到一个适当的地方。我在程序中写的是 int luaopen_mylib(lua_State *L),它将mylib注册到Lua中,并设置它为全局变量,这样以后栈中会留有一个全局的table(mylib),最后函数返回1,表示将这个table返回给Lua。 2.请注意必须按规定写好struct luaL_Reg 这个结构体,其中的元素{"plus", plus},第一个plus是留给Lua调用的函数名称,第二个是被注册的C函数本身。 3.注册的C函数形式也是 typedef int (*lua_CFunction) (lua_State *L),它最后也必须返回一个整数,即结果的数量。  4.C模块中只有一个外部(公共)函数,也就是上面的主函数,其他所有的注册函数都是私有的,在C语言中声明为static。最后说下,虽然这里我们是说Lua调用C函数,但实际上我们还是从C中来启动程序的。其实我们也可以从Lua文件中启动程序,Lua有自己的解释器。这样做的话,我们可以把注册的C函数当作一个动态连接库,然后通过require “mylib”加载这个模块。而实际运用中,还是以从C/C++启动为主。
上面的程序中经历了C调用Lua函数,Lua调用C函数,绕得会有些头晕,但理解上面所说的就行了。恩,罗嗦了这么多,不想再多说,就这样吧,去试试,写下调试一下,都是可以的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值