我爱编程

C++集成lua

2017-10-31  本文已影响0人  小小青蛙不怕风吹雨打

tolua++

C++最常用的类对象,它的导入要比函数导入复杂许多。
本质上C++的对象是一个指针, 一般是用UserData包装,绑定元表信息。
元表用C++类结构建立起来,告诉lua如何读写C++对象。

有个自动导出C++的工具,tolua++。
步骤:

  1. 写个描述文件,和C++头文件很像
  2. 运行tolua++命令生成cpp代码,里面有tolua_module_open的注册函数
  3. 在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有不少坑点。

  1. 内存管理【这是最麻烦的地方
    lua有自动垃圾回收,C++要手动管理。
  2. 常用类型的导出
    c++的基本类型和模版类型非常多,lua通常只支持double和Lua.Table 常用的int64,vector,map的处理就那么顺心了。
  3. 继承体系
    这个反而简单了,C++的多继承体系很容易可以用lua的元表模拟起来。
  4. 递归调用、异常、协程
    难免出现这种情况,C++ -> Lua -> C++ -> Lua。中间发生异常如何处理?协程中断如何处理?一不小心就程序状态不对,内存泄漏啥的。

Lua管理C++对象

实际也就是Lua管理C++的指针和内存。
两个简单情况:

  1. Lua完全负责C++对象的生命周期
    这时需要把C++对象包装成UserData,在元表里加入__gc的方法,lua垃圾回收时就可以自动回收函数了。
    也可以在元表里导入new和delete方法,lua代码里调用。
    在tolua++里,如果导入了构造函数和析构函数,就有三个元表键值new,delete,new_local
  2. Lua运行期间C++对象一直有效
    如C++静态变量,导出到lua里时不需要负责内存回收的,这时最好使用lightuserdata,就保存个指针到lua里。

C++管理Lua对象

这是有一种复杂情况,C++和Lua里都可以创建和释放对象。
这时的核心是一方操作要通知另一方。创建和释放都不能简单的处理了。
一般是维护一个表,记录对象,释放时修改下表,来通知对方。LuaL_ref就是为此设计的。

具体实现上有一个大的分歧:

  1. C++和Lua都可以删除对象,一方删除需要通知另一方。
    1. C++删除通知Lua,这个相对好处理些,lua里可以做到一个C++对象只有一个UserData。C++可以获取到这个UserData,然后清空指针。
    2. Lua删除通知C++,这个就不好办,Lua是不可能知道C++哪儿保存了对象的指针的。最常见的方法是使用引用计数了。
  2. C++和Lua不都可以删除对象
    1. 只有一方可以删除。一般是C++删除,Lua可以判断是否被删了。
    2. 引用计数。小缺点是引用计数维护不好就惨了。

关于对象内存管理的小结

实际使用中三种情况比较常见

  1. Lua完全管理C++对象,Lua里创建删除,C++层不保存指针。
  2. C++完全管理对象,删除时会通知Lua,Lua里可以判断对象是否还有效。Lua只使用,创建对象也是通过C++层特殊处理。
  3. C++获取保存Lua的表和闭包回调。对象的生命周期是lua负责,但是C++会设置下,让Lua不能把用着的对象给回收了。
    1. 同样也可以C++层维护引用计数,lua里操作引用计数,创建UserData时加一,回收时减一。

tolua++的实现里,是Lua可以选择是否管理C++对象的生命周期,有两个典型的方法takeownershipreleaseownership。比较弱啊。
Cocos2dx-lua对tolua++有不少改动。
主要参考:

http://blog.csdn.net/wtyqm/article/details/8977975
http://blog.csdn.net/wtyqm/article/details/9106137

上一篇下一篇

猜你喜欢

热点阅读