• Luna


            LUA并不支持直接访问C++的类,但是通过使用LUA的C API和C++ templates,可以在LUA注册C++类,并访问其成员,这种方法叫做Luna。

           LunaWrapper是一个简单的Luna实现,它的具体做法可概括为如下:

    1.初始化LunaWrapper时调用其注册函数Register,在Register中通过LUA C API把一个C函数定义为一个全局LUA函数,函数名是LUA中要访问的C++ class类名,以便能在lua中这样创建C++对象:foo = Foo()。这个C函数其实主要是new一个Foo对象出来,关于这个C函数具体实现见下文。在Register中还创建了一个关联C++类名的元表,并且把这个元表的__gc方法设置为一个C函数gc_obj,当LUA中没有谁在引用foo,垃圾回收foo时会调用gc_obj,后面可以看见 gc_obj其实是delete了这个Foo对象。代码见下:

      static void Register(lua_State *L) {
          lua_pushcfunction(L, &Luna<T>::constructor);
          lua_setglobal(L, T::className);
    
          luaL_newmetatable(L, T::className);
          lua_pushstring(L, "__gc");
          lua_pushcfunction(L, &Luna<T>::gc_obj);
          lua_settable(L, -3);
        }


    C函数在C++中当然可以使用静态成员函数代替了,至于模板的使用,则方便了导出各种不同的C++类类型。总的来说Register函数是在lua加载LunaWrapper时初始化时调用的函数。


    2.构造函数。构造函数当然是要创建出一个这样的C++对象,也当然还要做些事情能让LUA访问到它的成员。这里用到了LUA的userdata和metatable,userdata保存C++对象的指针,userdata自己本身则被保存在一个新建的表中,这个表后面可以看见它是构造函数的返回值,LUA中都是直接对它进行操作,也就是说我们需要在内部把LUA对它的访问转掉到C++对象的成员上来,让在LUA中看来是直接在访问C++对象。最后,代码遍历C++类的到处成员函数表,把每个函数名做为key,转调函数Luna<T>::thunk做为值添加进返回表中中。

    static int constructor(lua_State *L) {
          T* obj = new T(L);
    
          lua_newtable(L);
          lua_pushnumber(L, 0);
          T** a = (T**)lua_newuserdata(L, sizeof(T*));
          *a = obj;
          luaL_getmetatable(L, T::className);
          lua_setmetatable(L, -2);
          lua_settable(L, -3); // table[0] = obj;
    
          for (int i = 0; T::Register[i].name; i++) {
            lua_pushstring(L, T::Register[i].name);
            lua_pushnumber(L, i);
            lua_pushcclosure(L, &Luna<T>::thunk, 1);
            lua_settable(L, -3);
          }
          return 1;
        }


    3.转调函数。它所做的事很简单,转掉C++类的相应成元函数。这里用到了LUA的闭包C API, 把相应的成员函数在数组中的索引值与转掉函数关联起来,目的是能调用到正确的成员函数。当然对象指针也能通过table[0]轻易获得。

      static int thunk(lua_State *L) {
          int i = (int)lua_tonumber(L, lua_upvalueindex(1));
          lua_pushnumber(L, 0);
          lua_gettable(L, 1);
    
          T** obj = static_cast<T**>(luaL_checkudata(L, -1, T::className));
          lua_remove(L, -1);
          return ((*obj)->*(T::Register[i].mfunc))(L);
        }

    4.析构函数。Luna使用了lua的userdata保存了对象的指针,并且在没有谁引用userdata时,delete它保存的指针。

    static int gc_obj(lua_State *L) {
          T** obj = static_cast<T**>(luaL_checkudata(L, -1, T::className)); //检查在栈中指定位置的对象是否为带有给定名字的metatable的usertatum
          delete (*obj);
          return 0;
        }

    因为在构造时这个userdata被放在table[0]位置,而在lua中table对应C++对象,当lua中没有在引用这个table时,table会被gc,其key和value也会gc,而userdata gc时会释放C++对象,这样就做到了生命周期的一致。

    LunaWrapper完整代码和测试例子在:https://github.com/persistentsnail/luna-test

    参考资料:LunaWrapper


  • 相关阅读:
    常见问题汇总
    python的正则表达式
    Python 程序读取外部文件、网页的编码与JSON格式的转化
    第三方仓库PyPI
    文件名称一定不要设置为某些模块名称,会引起冲突!
    迟来的博客
    FENLIQI
    fenye
    Notif
    phpv6_css
  • 原文地址:https://www.cnblogs.com/persistentsnail/p/3294852.html
Copyright © 2020-2023  润新知