因为需求,最近又重新回忆了下tolua++的相关流程, 以 3.9的 lua stack 的初始化相关代码开始解析:
_state = lua_open(); luaL_openlibs(_state); toluafix_open(_state); // Register our version of the global "print" function const luaL_reg global_functions [] = { {"print",lua_print},{"release_print",lua_release_print},{nullptr,nullptr} }; luaL_register(_state,"_G",global_functions); g_luaType.clear(); register_all_cocos2dx(_state);
register_all_cocos2dx 如下:
<pre name="code" class="cpp">TOLUA_API int register_all_cocos2dx(lua_State* tolua_S) { tolua_open(tolua_S); tolua_module(tolua_S,"cc",0); tolua_beginmodule(tolua_S,"cc"); lua_register_cocos2dx_Ref(tolua_S);
<span style="white-space:pre"> </span>...
<span style="white-space:pre"> </span> tolua_endmodule(tolua_S);
<span style="white-space:pre"> return 1;</span>
}
TOLUA_API void tolua_open (lua_State* L) { int top = lua_gettop(L); lua_pushstring(L,"tolua_opened"); lua_rawget(L,LUA_REGISTRYINDEX); if (!lua_isboolean(L,-1)) { lua_pushstring(L,"tolua_opened"); lua_pushboolean(L,1); lua_rawset(L,LUA_REGISTRYINDEX); // create value root table lua_pushstring(L,TOLUA_VALUE_ROOT); lua_newtable(L); lua_rawset(L,LUA_REGISTRYINDEX); /* create object ptr -> udata mapping table */ lua_pushstring(L,"tolua_uBox"); lua_newtable(L); /* make weak value Metatable for uBox table to allow userdata to be garbage-collected */ lua_newtable(L); lua_pushliteral(L,"__mode"); lua_pushliteral(L,"v"); lua_rawset(L,-3); /* stack: string uBox mt */ lua_setMetatable(L,-2); /* stack: string uBox */ lua_rawset(L,LUA_REGISTRYINDEX); lua_pushstring(L,"tolua_super"); lua_newtable(L); lua_rawset(L,LUA_REGISTRYINDEX); lua_pushstring(L,"tolua_gc"); lua_newtable(L); lua_rawset(L,LUA_REGISTRYINDEX); /* create gc_event closure */ lua_pushstring(L,"tolua_gc_event"); lua_pushstring(L,"tolua_gc"); lua_rawget(L,"tolua_super"); lua_rawget(L,LUA_REGISTRYINDEX); lua_pushcclosure(L,class_gc_event,2); lua_rawset(L,LUA_REGISTRYINDEX); tolua_newMetatable(L,"tolua_commonclass"); tolua_module(L,NULL,0); tolua_beginmodule(L,NULL); tolua_module(L,"tolua","tolua"); tolua_function(L,"type",tolua_bnd_type); tolua_function(L,"takeownership",tolua_bnd_takeownership); tolua_function(L,"releaSEOwnership",tolua_bnd_releaSEOwnership); tolua_function(L,"cast",tolua_bnd_cast); tolua_function(L,"isnull",tolua_bnd_isnulluserdata); tolua_function(L,"inherit",tolua_bnd_inherit); #ifdef LUA_VERSION_NUM /* lua 5.1 */ tolua_function(L,"setpeer",tolua_bnd_setpeer); tolua_function(L,"getpeer",tolua_bnd_getpeer); #endif tolua_function(L,"getcfunction",tolua_bnd_getcfunction); tolua_function(L,"iskindof",tolua_bnd_iskindof); tolua_endmodule(L); tolua_endmodule(L); } lua_settop(L,top); }
对 tolua_open 解析如下:
tolua_open: LUA_REGISTRYINDEX["tolua_opened"] = true LUA_REGISTRYINDEX[TOLUA_VALUE_ROOT] = {} LUA_REGISTRYINDEX["tolua_uBox"] = setMetatable({},{__mode = v}) LUA_REGISTRYINDEX["tolua_super"] = {} LUA_REGISTRYINDEX["tolua_gc"] = {} LUA_REGISTRYINDEX["tolua_gc_event"] = class_gc_event [closure: LUA_REGISTRYINDEX["tolua_gc"] LUA_REGISTRYINDEX["tolua_super"]] tolua_newMetatable(L,"tolua_commonclass") mt = {} LUA_REGISTRYINDEX["tolua_commonclass"] = mt LUA_REGISTRYINDEX[mt] = "tolua_commonclass" tolua_classevents(mt) tolua_module(L,0); 确保拥有该模块, 该木块没有 moduleevent __index,__newindex tolua_beginmodule(L,NULL); push _G (not .isclass,a normal table not mt) tolua_module(L,0); _G["tolua"] = {} tolua_beginmodule(L,"tolua"); push _G["tolua"] tolua_function(L,tolua_bnd_type); _G["tolua"]["type"] = tolua_bnd_type ... tolua_endmodule(L); pop tolua_endmodule(L); pop restore stack需要留意的:
1.其中TOLUA_VALUE_ROOT 用于额外存放push到lua中的userdata 如果push的时候放入了这个table中,那么析构的时候需要从中移除
2.当push的 userdata 类的 mt 中没有 tolua_uBox 则这个userdata 会放入到这个 全局 tolua_uBox 中
3.tolua_super 存储注册到lua中类的继承关系,例如 c_derived 继承 c_base 那么 tolua_super[mt_c_derived] = {cbaseName = true},cbaseName为c_base 在注册表的 class name
4.注册的lua类的基类都会索引 tolua_commonclass,他的 __index,__newindex,__gc 值得注意下,
其他流程用伪代码解析如下:
主流程调用: register_all_cocos2dx: //栈顶已经有一个 _G 了 tolua_open(tolua_S); //见核心一 tolua_module(tolua_S,0); //确保拥有 _G["cc"] tolua_beginmodule(tolua_S,"cc"); //push _G["cc"] lua_register_cocos2dx_Ref(tolua_S); ... tolua_endmodule(tolua_S); //pop 注册详细信息: int lua_register_cocos2dx_Ref(lua_State* tolua_S) { tolua_usertype(tolua_S,"cc.Ref");: tolua_newMetatable("cc.Ref") tolua_newMetatable("const cc.Ref") mapsuper(L,type,ctype);: mapsuper (lua_State* L,const char* name,const char* base) mt = LUA_REGISTRYINDEX[name] LUA_REGISTRYINDEX["tolua_super"][mt] = {$base = true} mtbase = LUA_REGISTRYINDEX[base] merge(LUA_REGISTRYINDEX["tolua_super"][mt],LUA_REGISTRYINDEX["tolua_super"][mtbase]) 右侧内容 merge 到左侧 tolua_cclass(tolua_S,"Ref","cc.Ref","",nullptr);: tolua_cclass (lua_State* L,const char* lname,const char* base,lua_CFunction col): mapinheritance(L,name,base); mapinheritance(L,cname,name); mapinheritance (lua_State* L,const char* base) push mt_name as mt push mt_base or LUA_REGISTRYINDEX["tolua_commonclass"] as base_mt set_uBox(L);: mt["tolua_uBox"] = base_mt["tolua_uBox"] or setMetatable({},{__mode = v}) setMetatable(mt,base_mt) mapsuper(L,cbase); mapsuper(L,base); LUA_REGISTRYINDEX[name][".collector"] = col LUA_REGISTRYINDEX[cname][".collector"] = col module[lname] = setMetatable({[".isclass"] = true},mt_name) // stack: module tolua_beginmodule(tolua_S,"Ref"); tolua_function(tolua_S,"release",lua_cocos2dx_Ref_release); ... tolua_endmodule(tolua_S); std::string typeName = typeid(cocos2d::Ref).name(); g_luaType[typeName] = "cc.Ref"; g_typeCast["Ref"] = "cc.Ref"; return 1; }
至于 cocos2dx 新增的 tolua_fix代码就不做笔记了,大概就是额外记录了下push 进lua的userdata,以及 function 相关的事情, 确保当析构的时候 userdata 置 nil, 让注册表functions 与对应索引id之类的事情。
OK,完毕, 知道了这些, 在 lua中 扩充、派生 那些注册进lua中的类就已经不是问题了。
干吧爹~,争取这几周内把框架搭建出来。