为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);
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。