C++集成lua

作者: 小小青蛙不怕风吹雨打 | 来源:发表于2017-10-31 21:15 被阅读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

    相关文章

      网友评论

        本文标题:C++集成lua

        本文链接:https://www.haomeiwen.com/subject/zmjipxtx.html