tolua++
C++最常用的类对象,它的导入要比函数导入复杂许多。
本质上C++的对象是一个指针, 一般是用UserData包装,绑定元表信息。
元表用C++类结构建立起来,告诉lua如何读写C++对象。
有个自动导出C++的工具,tolua++。
步骤:
- 写个描述文件,和C++头文件很像
- 运行tolua++命令生成cpp代码,里面有tolua_module_open的注册函数
- 在Lua引擎初始化后,调用tolua_module_open注入。
$#include"hello.h"
namespace hello{
void show();
class MyClass
{
public:
MyClass();
~MyClass();
void DoSomething();
};
}
/* Open function */
TOLUA_API int tolua_hello_open (lua_State* tolua_S)
{
tolua_open(tolua_S);
tolua_reg_types(tolua_S);
tolua_module(tolua_S,NULL,0);
tolua_beginmodule(tolua_S,NULL);
tolua_module(tolua_S,"hello",0);
tolua_beginmodule(tolua_S,"hello");
tolua_function(tolua_S,"show",tolua_hello_hello_show00);
#ifdef __cplusplus
tolua_cclass(tolua_S,"MyClass","hello::MyClass","",tolua_collect_hello__MyClass);
#else
tolua_cclass(tolua_S,"MyClass","hello::MyClass","",NULL);
#endif
tolua_beginmodule(tolua_S,"MyClass");
tolua_function(tolua_S,"new",tolua_hello_hello_MyClass_new00);
tolua_function(tolua_S,"new_local",tolua_hello_hello_MyClass_new00_local);
tolua_function(tolua_S,".call",tolua_hello_hello_MyClass_new00_local);
tolua_function(tolua_S,"delete",tolua_hello_hello_MyClass_delete00);
tolua_function(tolua_S,"DoSomething",tolua_hello_hello_MyClass_DoSomething00);
tolua_endmodule(tolua_S);
tolua_endmodule(tolua_S);
tolua_endmodule(tolua_S);
return 1;
}
C++集成Lua的麻烦
C++集成lua有不少坑点。
- 内存管理【这是最麻烦的地方】
lua有自动垃圾回收,C++要手动管理。 - 常用类型的导出
c++的基本类型和模版类型非常多,lua通常只支持double和Lua.Table 常用的int64,vector,map的处理就那么顺心了。 - 继承体系
这个反而简单了,C++的多继承体系很容易可以用lua的元表模拟起来。 - 递归调用、异常、协程
难免出现这种情况,C++ -> Lua -> C++ -> Lua。中间发生异常如何处理?协程中断如何处理?一不小心就程序状态不对,内存泄漏啥的。
Lua管理C++对象
实际也就是Lua管理C++的指针和内存。
两个简单情况:
- Lua完全负责C++对象的生命周期
这时需要把C++对象包装成UserData,在元表里加入__gc的方法,lua垃圾回收时就可以自动回收函数了。
也可以在元表里导入new和delete方法,lua代码里调用。
在tolua++里,如果导入了构造函数和析构函数,就有三个元表键值new,delete,new_local
。 - Lua运行期间C++对象一直有效
如C++静态变量,导出到lua里时不需要负责内存回收的,这时最好使用lightuserdata,就保存个指针到lua里。
C++管理Lua对象
这是有一种复杂情况,C++和Lua里都可以创建和释放对象。
这时的核心是一方操作要通知另一方。创建和释放都不能简单的处理了。
一般是维护一个表,记录对象,释放时修改下表,来通知对方。LuaL_ref就是为此设计的。
具体实现上有一个大的分歧:
- C++和Lua都可以删除对象,一方删除需要通知另一方。
- C++删除通知Lua,这个相对好处理些,lua里可以做到一个C++对象只有一个UserData。C++可以获取到这个UserData,然后清空指针。
- Lua删除通知C++,这个就不好办,Lua是不可能知道C++哪儿保存了对象的指针的。最常见的方法是使用引用计数了。
- C++和Lua不都可以删除对象
- 只有一方可以删除。一般是C++删除,Lua可以判断是否被删了。
- 引用计数。小缺点是引用计数维护不好就惨了。
关于对象内存管理的小结
实际使用中三种情况比较常见
- Lua完全管理C++对象,Lua里创建删除,C++层不保存指针。
- C++完全管理对象,删除时会通知Lua,Lua里可以判断对象是否还有效。Lua只使用,创建对象也是通过C++层特殊处理。
- C++获取保存Lua的表和闭包回调。对象的生命周期是lua负责,但是C++会设置下,让Lua不能把用着的对象给回收了。
- 同样也可以C++层维护引用计数,lua里操作引用计数,创建UserData时加一,回收时减一。
tolua++的实现里,是Lua可以选择是否管理C++对象的生命周期,有两个典型的方法takeownership
和releaseownership
。比较弱啊。
Cocos2dx-lua对tolua++有不少改动。
主要参考:
http://blog.csdn.net/wtyqm/article/details/8977975
http://blog.csdn.net/wtyqm/article/details/9106137