为Lua绑定C对象

为Lua绑定C对象,一种可行的方法是为C对象添加一个壳,lua 的 userdata 中仅仅保存 C 对象指针。然后给 userdata 设置 gc 元方法,在被回收时,正确调用 C 对象的销毁函数。

对于这种方法,userdata的行为跟lua表的行为是不一致的,例如向lua中绑定了一个socket对象,应用程序在获得了socket对象之后,很可能需要设置socket对象关联一些应用数据。但因为userdata不是一个lua表,执行设置操作的时候会出现错误。一种解决方法是在Lua中对这个socket对象再做一层封装,这样封装后的对象行为就跟lua表一致了。

这个方法的缺点是需要对很多的C对象做更进一步的Lua封装,这不但增加了很多无谓的工作量也降低了对象的访问效率。

在这里介绍另一种方法,壳对象除了保存指向C对象的指针之外,还要保存一个lua表,C对象的所有绑定方法都添加到这个额外的表中,为userdata添加_index和_newindex元方法,在这两个元方法中将对字段的访问转发到lua表中。这样,对userdata执行设置操作的时候会通过_newindex元方法,把字段关联到保存的lua表中。

#define UD_METATABLE "lua_ud"

typedef struct {
    chk_luaRef table;
}lua_ud;

#define lua_checkud(L,I)    \
    (lua_ud*)luaL_checkudata(L,I,UD_METATABLE)

static int32_t lua_ud_hello(lua_State *L) {
    lua_ud *ud = lua_checkud(L,1);
    printf("lua_ud_hello\n");
    return 0;
}

static    luaL_Reg ud_methods[] = {
    {"Hello", lua_ud_hello},
    {NULL,      NULL}
};

static int32_t lua_ud_index(lua_State *L) {
    lua_ud *ud = lua_checkud(L,1);
    lua_rawgeti(ud->table.L,LUA_REGISTRYINDEX,ud->table.index);
    if(ud->table.L != L) {
        lua_xmove(ud->table.L, L, 1);
    }
    lua_rotate(L,-2,1);    
    lua_rawget(L,-2);
    return 1;
}

static int32_t lua_ud_newindex(lua_State *L) {
    lua_ud *ud = lua_checkud(L,1);
    lua_rawgeti(ud->table.L,LUA_REGISTRYINDEX,ud->table.index);
    if(ud->table.L != L) {
        lua_xmove(ud->table.L, L, 1);
    }
    lua_rotate(L,-3,2);
    lua_rotate(L,-3,2);
    lua_rawset(L,-3);    
    return 0;
}

static int32_t lua_ud_gc(lua_State *L) {
    printf("gc\n");
    lua_ud *ud = lua_checkud(L,1);
    if(ud->table.L) {
        chk_luaRef_release(&ud->table);
    }
    return 1;
}

static int32_t lua_new_ud(lua_State *L) {
    lua_ud *ud = LUA_NEWUSERDATA(L,lua_ud);
    lua_newtable(L);
    ud->table = chk_toluaRef(L,-1);

    int i = 0;
    for(; ud_methods[i].name; ++i) {
        SET_FUNCTION(L,ud_methods[i].name,ud_methods[i].func);        
    }
    lua_pop(L,1);//pop the table
    luaL_getmetatable(L, UD_METATABLE);
    lua_setmetatable(L, -2);
    return 1;
}

static void register_ud(lua_State *L) {
    luaL_Reg ud_mt[] = {
        {"__gc", lua_ud_gc},
        {"__index",lua_ud_index},
        {"__newindex",lua_ud_newindex},
        {NULL, NULL}
    };

    luaL_newmetatable(L, UD_METATABLE);
    luaL_setfuncs(L, ud_mt, 0);
    lua_setmetatable(L, -2);

    lua_newtable(L);
    SET_FUNCTION(L,"new",lua_new_ud);
}

相关推荐