lua与C/C++交互概要
转 http://blog.csdn.net/wildfireli/article/details/22307635
Lua生来就是为了和C交互的,因此使用C扩展Lua或者将Lua嵌入到C当中都是非常流行的做法。要想理解C和Lua的交互方式,首先要回顾一下C语言是如何处理函数参数的。
C函数和参数
大家知道C语言是用汇编实现的,在汇编语言中可没有函数的概念,与函数对应的是叫做子过程的东西,子过程就是一段指令,一个子过程与它调用的子过程之间通
过栈来进行参数的传递交互。在一个子过程在调用别的子过程之前,会按照约定的格式将要调用的子过程需要的参数入栈,在被调用的子过程中,可以按照约定的规
则将参数从栈中取出。同理,对于返回值的传递也同样是通过堆栈进行的。C语言约定的参数放入栈中的格式,就是“调用惯例”。C语言的函数原型则决定了压入
栈中的参数的数量和类型。
Lua的虚拟堆栈
Lua和C之间的交互巧妙的模拟了C语言的堆栈,Lua和C语言之间的相互调用和访问都通过堆栈来进行,巧妙的解决了不同类型之间变量相互访问的问题。具体的,我们想象如下一个图
+-------+ +-------+ | | | | | | +-------+ | | | C | <==> | | <==> | Lua | | Space | |Virtual| | Space | | | | Stack | | | | | | | | | +-------+ +-------+ +-------+
由于C和Lua是不同层次的语言,因此C语言的变量和Lua中的变量以及函数不能直接的交互,我们假定C语言和Lua都有自己的“空间(C Space和Lua Space)”。而这两个空间之间的交互就通过上图中的这个虚拟堆栈来解决。为何采用虚拟堆栈的方式来进行交互呢?其目的是在提供强大的灵活性的同时避免 交互时两种语言变量类型的组合爆炸。
C语言读写Lua全局变量(基本类型)
C语言读取Lua的全局变量是一种最简单的操作。通过上图我们可以猜测到,如果通过C语言读取Lua中的全局变量需要两步:1、将全局变量从Lua
Space压入虚拟堆栈;2、从堆栈将全局变量读取到C语言Space中。在Lua和C的交互中,Lua无法看到和操作虚拟堆栈,仅在C语言中有操作堆栈
的权利,因此前面说到的两步全都是在C语言中完成的。我们看一个简单的例子
Lua代码:
global_var1 = 5; print("Print global varb from lua", global_var1);
C代码:
void get_global(lua_State *L) { int global_var1; lua_getglobal(L, "global_var1"); /* 从lua的变量空间中将全局变量global_var1读取出来放入虚拟堆栈中 */ global_var1 = lua_tonumber(L, -1); /* 从虚拟堆栈中读取刚才压入堆栈的变量,-1表示读取堆栈最顶端的元素 */ printf("Read global var from C: %d\n", global_var1); }
执行结果:
Lua中对堆栈的操作都是通过索引来进行的,索引为1表示从栈底数第一个元素,索引为2表示从栈底数第二个元素;同样也可以使用负数从栈顶开始计算,-1
表示从栈顶数第一个元素,-2表示从栈顶数第二个元素。更多堆栈的操作函数请参考lua的官方手册http://www.lua.org/manual
/5.2/manual.html。
同样从堆栈中获取元素,除了我们使用的lua_tonumber之外,还有lua_tolstring,lua_toboolean等。
通常情况下在读取变量之前还需要对堆栈中元素的实际类型做出检查:
void get_global(lua_State *L) { int global_var1; lua_getglobal(L, "global_var1"); /* 从lua的变量空间中将全局变量global_var1读取出来放入虚拟堆栈中 */ if (!lua_isnumber(L, -1)) /* 检查堆栈中栈顶第一个元素是否是数字 */ error(L, "Is not number."); global_var1 = lua_tonumber(L, -1); /* 从虚拟堆栈中读取刚才压入堆栈的变量,-1表示读取堆栈最顶端的元素 */ }
写入全局变量也一样简单: 首先将数据压入堆栈,然后再将堆栈中的数据存入全局变量。
C代码:
void set_global(lua_State *L)
{
lua_pushinteger(L, 9);
lua_setglobal(L, "global_var1");
printf("set global var from C:9\n");
}
cococs C++--------------------------------------------------------------------------------与Lua
虽然终极方法是用bindings-generator脚本来注册C++类进Cocos2d-x的Lua环境,但理解toLua++本身的用法还是狠有必要的,只有知道了toLua++原本的用法,才能更好地理解Cocos2d-x是怎么把自己的C++类都注册进Lua环境的,这不仅能让编程时的思路更加 清晰,也能为日后在源码中寻找各种接口文档的过程中不至于看不懂那一大堆tolua_beginmodule、tolua_function是什么 意思。影响程序员学习提高的一大障碍就是忽略那些一知半解的代码,不去刨根究底地搞明白。
使用toLua++的标准做法是:
1、准备好自己的C++类,该怎么写就怎么写
2、仿造这个类的.h文件,改一个.pkg文件出来,具体格式要按照toLua++的规定,比如移除所有的private成员等
3、建一个专门用来桥接C++和Lua之间的C++类,使用特殊的函数签名来写它的.h文件,.cpp文件不写,等着toLua++来生成
4、给这个桥接的C++类写一个.pkg文件,按照toLua++的特殊格式来写,目的是把真正做事的C++类给定义进去
5、在命令行下用toLua++生成桥接类的.cpp文件
6、程序入口引用这个桥接类,执行生成的桥接函数,Lua环境中就可以使用真正做事的C++类了
编译---------------------------------------
在LUA的官网下载LUA源码后会有SRC文件夹
其中 lua.c 和 luac.c 都有 int main 函数,各源文件从根节点互相引用,可以将其编译为库 或 .exe使用,或将源码添加到项目里(要放到根目录)。
lua.c的main是编译运行时用的
luac.c的main是编译一个将lua代码编译成二进制代码的软件用的
luaconf.h 有个 LUA_BUILD_AS_DLL 用于编译windows动态链接库.dll文件
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步