lua
与C/C++
交互,主要靠lua API
和向lua
注册好的函数。如下图LuaGlue
就是让Lua
脚本中可调用的C++
函数的接口。即那些注册的函数。
这里起关键作用的要数lua_State
的结构,这就得讲一下Lua
环境和Lua Stack
。
Lua环境
Lua
环境由所有可操作的数据构成,如编译好的函数、变量以及其他运行时内存。这些数据保存在一个称做lua_State
的结构中。所有Lua
应用程序都要求至少有一个lua_State
,如果需要还可以有多个(如需要为两个不同的系统保存不同的数据时)。
Lua Stack
对于我们来说,Lua
环境是用来发送和接收数据的地方,它利用栈(Lua Stack)
来达到该目的。所有的数据交换,无论是Lua
到C/C++
语言或C/C++
语言到Lua
都通过这个栈来完成。Lua
栈不同于系统栈,它只能通过Lua
的API
函数访问。
注册到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
函数指针,并将一个类型为function
的Lua
值压入堆栈。当这个栈顶的值被调用时,将触发对应的C
函数。
这种方法需要重新编译Lua
的执行程序,才能在Lua
程序中使用这个新函数。但用下面的方法会更好,直接将C
函数链接到lua
。
Lua调用C函数
Lua
调用C
函数时,并不依赖于函数名、包的位置或可见性规则,而只依赖于注册时传入的函数地址。当用C
函数扩展Lua
时,最好将代码设计为一个C
模块。因为现在只注册一个函数,但说之后可能会需要更多的函数。辅助库为这项工作提供了一个函数luaL_register
,这个函数接收一些C
函数及其名称,并将这些函数注册到一个与模块同名的table
中。例如,假设创建一个模块,其中包含了这个luaglue
函数。首先,必须定义这个模块函数:
1 | static int luaglue(lua_state *L) |
然后,声明一个数组,其中包含模块中所有函数及名称。这个数组元素的类型为luaL_Reg
结构,该结构有两个字段,一个字符串和一个函数指针:
1 | static const struct luaL_Reg mylib[] = { |
最后,声明一个主函数,其中用到了luaL_register
:
1 | int luaopen_mylib(lua_State *L) |
其中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)
中。然后,便可以用require
从Lua
中加载这个模块:
1 | require "mylib" |
这名调用会将动态库mylib
链接到Lua
,并会寻找luaopen_mylib
函数,将其注册为一个Lua
函数,然后调用它以打开模块。
如果解释器不支持动态链接,那么就必须用新的模块来重新编译Lua
。此外,还需要以某种方式来告诉解释器,它应在打开一个新状态的同时打开这个模块。最简单的做法是,将luaopen_mylib
加到luaL_openlibs
会打开的标准库列表中,这个列表在文件linit.c
中。
从C++
程序员的观点来看,Lua
像一个“黑盒子”,为一些服务处理命令和调用。Lua
通常作为最上层接口直接和程序使用者和游戏玩家打交道,在核心程序处理之前接受并响应输入。
lua_tinker
如果嫌麻烦,不想手动写这些代码的话,我推荐lua_tinker
。也就只有两个文件,lua_tinker.h
,lua_tinker.cpp
。
环境配置,首先你需要搭建一个可以用lua
的开发环境,这里网上有很多,我就不多说了。然后再将lua_tinker
的lua_tinker.h
和lua_tinker.cpp
添加到你的工程中就可以了。
1 | lua_tinker::def(L, "cpp_func", cpp_func); //在L栈中注册C++函数 |
用lua_tinker::call
是要注意,参数不支持智能指针。
Hay tres tipos de personas: los que saben contar y los que no.