LUA中绑定C对象的3种方法

Posted bywayboy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LUA中绑定C对象的3种方法相关的知识,希望对你有一定的参考价值。

对于C 和LUA之间的对象绑定, 大致可以分为以下几种.

  1. 全集C对象指针.
  2. 生存周期完全由C控制的指针.
  3. 生存周期完全由LUA控制的指针.

针对以上3种场景, 我们可以用下面的方法进行对象绑定.

 1. 在整个软件生存周期都有效的对象指针


这种类型的C对象, 不用关心C对象的生存周期. 可以使用 lua upvalue 来进行绑定.

static int lua_function_method1(lua_State * L)

    you_c_ptr * obj = (you_c_ptr *)lua_touserdata(L, lua_upvalueindex(1)); //通过 upvalue 获取c对象

luaL_Reg lua_functions[] = 
    "method1", lua_function_method1,
    NULL, NULL
;

int luaopen_cobject(lua_State * L)

    ## 直接返回一个方法表.即可
    lua_newlibtable(L, lua_functions);
    lua_pushlightuserdata(L, c_object_ptr);
    luaL_setfuncs(L, lua_functions, 1);
    return 1;


 2. 不知道何时会失效的C对象.


这类对象的生存周期不确定, 比如一个网络连接, 在你的程序运行的任何时间它都有可能失效(比如:连接断开了).  可以给C对象增加一个引用计数, 同时使用匿名元表的方式来绑定到lua对象.
 

/*
 绑定对象的工具函数
    Lua 虚拟机指针, 方法表, 方法数目, upvalue数, __index 原方法(没有可以提供 NULL)
*/

int lua_bind_metatable(lua_State * L, int i, luaL_Reg * methods, int reg_sz, int nups, lua_CFunction _index)

    int top = lua_gettop(L);
    lua_createtable(L, 0, reg_sz);
    top = lua_gettop(L);
    lua_rotate(L, -(1 + nups), 1);
    luaL_setfuncs(L, methods, nups);
    top = lua_gettop(L);
    lua_pushliteral(L, "__index");
    if(_index)
        lua_pushcfunction(L, _index);
    else
        lua_pushvalue(L, -2);
    
    lua_rawset(L, -3);

    lua_pushliteral(L, "__metatable");
    lua_pushliteral(L, "not allow access metatable");
    lua_rawset(L, -3);

    top = lua_gettop(L);
    printf("set metatable to type: stack:%d, type:%d\\n", i + nups - 1 ,  lua_type(L,i + nups - 1));
    lua_setmetatable(L, i + nups -1);
    top = lua_gettop(L);
    return 0;



static int lua_function_method1(lua_State * L)

    //通过 upvalue 获取c对象
    you_c_ptr * obj = (you_c_ptr *)lua_touserdata(L, lua_upvalueindex(1)); 
    // .... you code is here.


/*减少引用计数*/
void c_obj_release(you_c_ptr * ptr)
    ptr->refCount--;
    if(0 == ptr->refCount)
        free(ptr);
    


void c_obj_add_ref(you_c_ptr * ptr)
    ptr->refCount++;


static int lua_function___gc(lua_State * L)

    // 通过 upvalue 获取c对象
    you_c_ptr * obj = (you_c_ptr *)lua_touserdata(L, lua_upvalueindex(1)); 
    // 其它释放资源的方法.
    // ...

    // 减少引用计数
    c_obj_release(obj);

luaL_Reg lua_functions[] = 
    "method1", lua_function_method1,
    "__gc", lua_function___gc,
    NULL, NULL
;


/*获取 LUA 对象*/
int luaopen_cobject(lua_State * L)

    ## 由于需要用到gc原方法, 这里只能返回一个元表了.
    ## lua种所有 lightuserdata 是共用一个元表的, 用此 这里需要一个空table作为lua对象的this
    lua_newtable(L, 0, 0);
    lua_pushlightuserdata(L, you_c_obj_ptr);
    c_obj_add_ref(you_c_obj_ptr);
    lua_bind_metatable(L, lua_functions, sizeof(lua_functions[0]) / sizeof(lua_functions), 1, NULL);
    return 1;


3. 生存周期都由LUA 控制的C对象


这类C对象 是通过 lua_newuserdata(sizeof(cobject_t)) 来创建的. 具有以下两种情形

  1. 有析构函数 则可以采用第2种方法来返回LUA对象. 不同的是它不需要引用计数. 
  2. 无析构函数 则可以使用第1种方法来返回LUA对象. 


通过以上方法得到的lua对象 在访问对象方法的时候都可以使用 “.” 而非 “:”.  原因是他们的this指针都在方法的 upvalue里面.

 

4. 涉及到协程调度.

LUA 的垃圾回收器并不会关心协程是否处于挂起状态还是dead状态, 只要没有变量引用到它, 则会对其进行回收.

好在 LUA_REGISTRYINDEX 是全局的, 我们可以再yeild之前 使用 lua_pushthread 将其压入堆栈然后用luaL_ref 引用到注册表中, 以确保当前线程不会被垃圾回收器强行回收掉.

因此, 当您再库中挂起一个协程并等待唤醒的时候, 需要对其进行引用.

 

以上是关于LUA中绑定C对象的3种方法的主要内容,如果未能解决你的问题,请参考以下文章

LUA中绑定C对象的3种方法

LUA中绑定C对象的3种方法

slua中,绑定lua文件到Monobehavior的一种方法

第十六章 C++与Lua的相互绑定之ELuna

Lua 面向对象

UE4 Unlua源码解析6 - UUnLuaManager内重要方法逐行解释