爲Lua綁定C對象

爲Lua綁定C對象,一種可行的方法是爲C對象添加一個殼,lua 的 userdata 中僅僅保存 C 對象指針。而後給 userdata 設置 gc 元方法,在被回收時,正確調用 C 對象的銷燬函數。socket

對於這種方法,userdata的行爲跟lua表的行爲是不一致的,例如向lua中綁定了一個socket對象,應用程序在得到了socket對象以後,極可能須要設置socket對象關聯一些應用數據。但由於userdata不是一個lua表,執行設置操做的時候會出現錯誤。一種解決方法是在Lua中對這個socket對象再作一層封裝,這樣封裝後的對象行爲就跟lua表一致了。函數

這個方法的缺點是須要對不少的C對象作更進一步的Lua封裝,這不但增長了不少無謂的工做量也下降了對象的訪問效率。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);
}
相關文章
相關標籤/搜索