C++对象绑定到lua通常是创建一个userdata用来存放cpp对象。在tolua库中维护了一张表:tolua_ubox表,它是个弱表,用来缓存所有cppobj的ptr->userdata映射。
userdata对象的生成过程和cpp对象到userdata的映射的生成。
void tolua_pushusertype_internal (lua_State* L,void* value,const char* type,int addToRoot) { if (value == NULL) lua_pushnil(L); else { luaL_getmetatable(L,type); /* stack: mt */ if (lua_isnil(L,-1)) { /* NOT FOUND metatable */ lua_pop(L,1); return; } lua_pushstring(L,"tolua_ubox"); lua_rawget(L,-2); /* stack: mt ubox */
//现在每个metatable都维护了一份tolua_ubox表,在类的生成过程,就会为metatable生成这个tolua_ubox表,具体请看mapinheritance和set_ubox if (lua_isnil(L,-1)) { lua_pop(L,1); lua_pushstring(L,"tolua_ubox"); lua_rawget(L,LUA_REGISTRYINDEX); 在创建tolua环境就会创建注册表中的tolua_ubox表 }; lua_pushlightuserdata(L,value); /* stack: mt ubox key<value> */ lua_rawget(L,-2); /* stack: mt ubox ubox[value] */ //查看在mt中的ubox表中是否存在value(C++对象),如果不存在,创建一个,并设置值为userdata,值为value。 if (lua_isnil(L,-1)) { lua_pop(L,1); /* stack: mt ubox */ lua_pushlightuserdata(L,value); *(void**)lua_newuserdata(L,sizeof(void *)) = value; /* stack: mt ubox value newud */
//newud就是userdata lua_pushvalue(L,-1); /* stack: mt ubox value newud newud */ lua_insert(L,-4); /* stack: mt newud ubox value newud */ lua_rawset(L,-3); /* ubox[value] = newud,stack: mt newud ubox */ 映射 lua_pop(L,1); /* stack: mt newud */ /*luaL_getmetatable(L,type);*/ lua_pushvalue(L,-2); /* stack: mt newud mt */ lua_setmetatable(L,-2); /* update mt,stack: mt newud */ 设置userdata的metatable为cpp的metatable。 #ifdef LUA_VERSION_NUM lua_pushvalue(L,TOLUA_NOPEER); /* stack: mt newud peer */ lua_setfenv(L,-2); /* stack: mt newud */ 设置userdata的环境为TOLUA_NOPEER #endif } else { //..... } }
那这和C++对象使用lua对象的函数,变量有什么关系呢?
其实一直在纠结lua对象继承C++对象的这个问题,到今天才看到比较满意的解决办法。
function cls.extend(target,...) //target是个C++对象来的 -- 先继承C++对象 local t = tolua.getpeer(target) if not t then t = {} tolua.setpeer(target,t) end setmetatable(t,cls) cls.ctor(target,...) return target end
在网上其实存在很多这个例子。
知其然不知其然怎么行呢?
tolua_getpeer和tolua_setpeer是什么鬼:
static int tolua_bnd_getpeer(lua_State* L) { /* stack: userdata */ lua_getfenv(L,-1); if (lua_rawequal(L,-1,TOLUA_NOPEER)) { 在上面的描述中,userdata的环境就是TOLUA_NOPEER,所以这边会返回空 lua_pop(L,1); lua_pushnil(L); }; return 1; };
static int tolua_bnd_setpeer(lua_State* L) { /* stack: userdata,table */ if (!lua_isuserdata(L,-2)) { lua_pushstring(L,"Invalid argument #1 to setpeer: userdata expected."); lua_error(L); }; if (lua_isnil(L,-1)) { lua_pop(L,1); lua_pushvalue(L,TOLUA_NOPEER); }; lua_setfenv(L,-2); 为userdata设置新的环境 return 0; };
所以例子中的
local t = tolua.getpeer(target) if not t then t = {} tolua.setpeer(target,t) end
这段是为了给这个C++对象(userdata)设置一个新的环境。这个环境现在什么都没有
setmetatable(t,cls) 现在这个环境中有了cls,这个cls就是Lua table。可以看做是我们的lua对象,lua class
然后返回C++对象。这样就可以通过C++对象使用到lua对象中的所有内容。其实在lua文件中可以创建这样的C++对象,既可以使用这个对象原有的东西还可以使用我们的lua内容,其实换个思路想想,如果把返回的对象看做是lua对象,这也就是lua对象继承C++对象嘛。
范例:
local tt = cc.Node:create() local env = {} env.abc = 4 env.func = function() print("环境函数") //输出环境函数 end local peer = tolua.getpeer(tt) if not peer then tolua.setpeer(tt,env) print(tt.abc) //输出4 tt.func() end (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|