lua与C/C++程序的整合

luaC/C++交互,主要靠lua API和向lua注册好的函数。如下图
20130816205205703
LuaGlue就是让Lua脚本中可调用的C++函数的接口。即那些注册的函数。
这里起关键作用的要数lua_State的结构,这就得讲一下Lua环境和Lua Stack

Lua环境

Lua环境由所有可操作的数据构成,如编译好的函数、变量以及其他运行时内存。这些数据保存在一个称做lua_State的结构中。所有Lua应用程序都要求至少有一个lua_State,如果需要还可以有多个(如需要为两个不同的系统保存不同的数据时)。

Lua Stack

对于我们来说,Lua环境是用来发送和接收数据的地方,它利用栈(Lua Stack)来达到该目的。所有的数据交换,无论是LuaC/C++语言或C/C++语言到Lua都通过这个栈来完成。Lua栈不同于系统栈,它只能通过LuaAPI函数访问。

注册到Lua中的函数

所有注册到Lua中的函数都具有相同的原型:

1
typedef int (*lua_Function)(lua_State *L);

这个函数仅有一个参数,即Lua的状态。它返回一个整数,表示其压入栈中的返回值数量。因此函数无须在压入结果前清空栈。在它返回后,Lua会自动删除栈中结果之下的内容。

Lua使用这个函数前,必须注册这个函数。

1
void lua_pushcfunction (lua_State *L, lua_CFunction f);

将一个C函数压入堆栈。 这个函数接收一个C函数指针,并将一个类型为functionLua值压入堆栈。当这个栈顶的值被调用时,将触发对应的C函数。
这种方法需要重新编译Lua的执行程序,才能在Lua程序中使用这个新函数。但用下面的方法会更好,直接将C函数链接到lua

Lua调用C函数

Lua调用C函数时,并不依赖于函数名、包的位置或可见性规则,而只依赖于注册时传入的函数地址。当用C函数扩展Lua时,最好将代码设计为一个C模块。因为现在只注册一个函数,但说之后可能会需要更多的函数。辅助库为这项工作提供了一个函数luaL_register,这个函数接收一些C函数及其名称,并将这些函数注册到一个与模块同名的table中。例如,假设创建一个模块,其中包含了这个luaglue函数。首先,必须定义这个模块函数:

1
2
3
static int luaglue(lua_state *L)
{
}

然后,声明一个数组,其中包含模块中所有函数及名称。这个数组元素的类型为luaL_Reg结构,该结构有两个字段,一个字符串和一个函数指针:

1
2
3
4
static const struct luaL_Reg mylib[] = {
{"dir",l_dir},
{NULL,NULL}//结尾
};

最后,声明一个主函数,其中用到了luaL_register:

1
2
3
4
5
int luaopen_mylib(lua_State *L)
{
luaL_register(L,"mylib",mylib);
return 1;
}

其中luaL_register原型为:

1
void luaL_register (lua_State *L,const char *libname,const luaL_Reg *l);

luaL_register根据给定的名称(“mylib”)创建(或复用)一个table,并用数组mylib中的信息填充这个table。在luaL_Register返回时,会将这个table留在栈中。最后,luaopen_mylib函数返回1,表示将这个table返回给Lua

打开一个库,当libname为null时,该函数注册所有在luaL_Reg上的函数,不为null时,该函数会创建一个table,根据libname注册不与libname关联的函数。

当写完c模块后,必须将其链接到解释器。如果Lua解释器支持动态链接的话,那么最简便的方法是使用动态链接机制。在这种情况中,必须将c代码编译成动态链接库,并将这个库放入C路径(LUA_CPATH)中。然后,便可以用requireLua中加载这个模块:

1
require "mylib"

这名调用会将动态库mylib链接到Lua,并会寻找luaopen_mylib函数,将其注册为一个Lua函数,然后调用它以打开模块。
如果解释器不支持动态链接,那么就必须用新的模块来重新编译Lua。此外,还需要以某种方式来告诉解释器,它应在打开一个新状态的同时打开这个模块。最简单的做法是,将luaopen_mylib加到luaL_openlibs会打开的标准库列表中,这个列表在文件linit.c中。

C++程序员的观点来看,Lua像一个“黑盒子”,为一些服务处理命令和调用。Lua通常作为最上层接口直接和程序使用者和游戏玩家打交道,在核心程序处理之前接受并响应输入。

lua_tinker

如果嫌麻烦,不想手动写这些代码的话,我推荐lua_tinker。也就只有两个文件,lua_tinker.hlua_tinker.cpp
环境配置,首先你需要搭建一个可以用lua的开发环境,这里网上有很多,我就不多说了。然后再将lua_tinkerlua_tinker.hlua_tinker.cpp添加到你的工程中就可以了。

1
2
3
4
5
6
7
lua_tinker::def(L, "cpp_func", cpp_func); //在L栈中注册C++函数
lua_tinker::dofile(L, "sample1.lua"); //加载lua文件
int result = lua_tinker::call<int>(L, "lua_func", 3, 4); //调用lua中的function lua_func

lua_tinker::class_add(L,"classA");//在lua中注册类
lua_tinker::class_con(L,lua_tinker::constructor<classA>);//在lua中注册构造函数
lua_tinker::class_mem(L,"memName",&classA::memName);

lua_tinker::call是要注意,参数不支持智能指针。


Hay tres tipos de personas: los que saben contar y los que no.