爲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); }