前几章都是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函数,绕得会有些头晕,但理解上面所说的就行了。恩,罗嗦了这么多,不想再多说,就这样吧,去试试,写下调试一下,都是可以的。