最近在写热更新的模块,目的是C++调用CRUL multi接口实现多线程下载功能并提供实时的状态查询接口给lua调用,采用的是生产者消费者模型。在使用tolua手动导出lua接口的时候碰到了一些问题,并在此记录。
lua与C++的交互是使用lua的registry表,C/C++代码可以自由使用,但Lua代码不能访问他.为每一个要注册到lua的数据,在registry创建一个元表.以类型名为key,元表为value.
luaL_newmetatable函数创建一个新表(将用作metatable),将新表放到栈顶并建立表和registry中类型名的联系。这个关联是双向的:使用类型名作为表的key;同时使用表作为类型名的key(这种双向的关联,使得其他的两个函数的实现效率更高).
lua调用过程实际上是lua调用C/C++在lua中注册的代理函数,代理函数调用相关的实际函数返回结果在lua栈中,lua再对返回值进行操作.
TOLUA_API void tolua_open (lua_State* L)
一些tolua的全局table的创建.
TOLUA_API void tolua_module (lua_State* L, const char* name, int hasvar)
创建一个module,如果name的module不为nil,不做处理.
TOLUA_API void tolua_beginmodule (lua_State* L, const char* name)
将一个模块或者类入栈.
TOLUA_API void tolua_beginmodule (lua_State* L, const char* name)
{
//在函数执行前 已经有一个module在lua栈上,这个module是_G
//在bool LuaStack::init(void)函数调用luaL_register(_state, "_G", global_functions)时,
//压入栈顶的.
if (name) { // ... module
//---- now module[name] is a table, get it's metatable to store keys
// get module[name]
lua_pushstring(L,name); // ... module name
lua_rawget(L,-2); // ... module module[name]
// Is module[name] a class table?
lua_pushliteral(L, ".isclass");
lua_rawget(L, -2); // stack: ... module module[name] class_flag
if (lua_isnil(L, -1)) {
lua_pop(L, 1); // stack: ... module module[name]
return; // not a class table, use origin table
}
lua_pop(L, 1); // stack: ... module class_table
// get metatable
if (lua_getmetatable(L, -1)) { // ... module class_table mt
lua_remove(L, -2); // ... module mt
}
//---- by SunLightJuly, 2014.6.5
} else {
lua_pushvalue(L,LUA_GLOBALSINDEX);
}
}
TOLUA_API void tolua_usertype (lua_State* L, const char* type)
注册用户类型,在注册表中构建_R[type] 和 _R[const type]的元表.
static void mapsuper (lua_State* L, const char* name, const char* base)
设置name继承于base,将base的所有超类方法映射到name中(包含base的方法).
static void mapsuper (lua_State* L, const char* name, const char* base)
{
/* push registry.super */
//在_R[tolua_super]中获取name的元表,如果为nil则创建一个元表.
lua_pushstring(L,"tolua_super");
lua_rawget(L,LUA_REGISTRYINDEX); /* stack: super */
luaL_getmetatable(L,name); /* stack: super mt */
lua_rawget(L,-2); /* stack: super table */
if (lua_isnil(L,-1))
{
/* create table */
lua_pop(L,1);
lua_newtable(L); /* stack: super table */
luaL_getmetatable(L,name); /* stack: super table mt */
lua_pushvalue(L,-2); /* stack: super table mt table */
lua_rawset(L,-4); /* stack: super table */
}
/* set base as super class */
//_R[tolua_super][name_mt]["base"] = true
lua_pushstring(L,base);
lua_pushboolean(L,1);
lua_rawset(L,-3); /* stack: super table */
/* set all super class of base as super class of name */
//获取_R[base_mt]
luaL_getmetatable(L,base); /* stack: super table base_mt */
lua_rawget(L,-3); /* stack: super table base_table */
//迭代base_mt,映射所有方法到name_mt
//lua_next()的工作过程是:
//1 从栈顶弹出一个key
//2 从栈指定位置的table里取下一对key-value, 先将key入栈再将value入栈
//3 如果第2步成功则返回非0值,否则返回0.并不像栈中压入任何值
if (lua_istable(L,-1))
{
/* traverse base table */
lua_pushnil(L); /* first key */
while (lua_next(L,-2) != 0)
{
/* stack: ... base_table key value */
lua_pushvalue(L,-2); /* stack: ... base_table key value key */
lua_insert(L,-2); /* stack: ... base_table key key value */
//这里栈顶的key是为了取下一对key-value使用,同上面的函数lua_pushnil的作用
lua_rawset(L,-5); /* stack: ... base_table key */
}
}
lua_pop(L,3); /* stack: */
}
static void mapinheritance (lua_State* L, const char* name, const char* base)
设置name继承base,设置 name_mt[tolua_ubox] = base_mt[tolua_ubox],基类的tolua_ubox不存在 为子类的tolua_ubox创建一张关联value的weak表.
static void mapinheritance (lua_State* L, const char* name, const char* base)
{
/* set metatable inheritance */
luaL_getmetatable(L,name);
if (base && *base)
luaL_getmetatable(L,base);
else {
if (lua_getmetatable(L, -1)) { /* already has a mt, we don't overwrite it */
lua_pop(L, 2);
return;
};
luaL_getmetatable(L,"tolua_commonclass");
};
set_ubox(L);
lua_setmetatable(L,-2);
lua_pop(L,1);
}
tolua_open(tolua_S);
//此时栈中元素stack: ... module cc (此时module是_G)在
//LuaStack::init(void)
//luaL_register(_state, "_G", global_functions) 压入栈顶.
tolua_beginmodule(tolua_S,"cc");
tolua_usertype(tolua_S,"cc.HotUpdate");
//函数有使用module(_G),所以确保module存在
tolua_cclass(tolua_S,"HotUpdate","cc.HotUpdate","cc.Ref",nullptr);
tolua_beginmodule(tolua_S,"HotUpdate");
tolua_function(tolua_S,"create", lua_cocos2dx_HotUpdate_create);
tolua_function(tolua_S,"registerScriptHandler", lua_cocos2dx_HotUpdate_registerScriptHandler);
tolua_endmodule(tolua_S);
std::string typeName = "cocos2d::HotUpdate";//typeid(so::HotUpdate).name();
g_luaType[typeName] = "cc.HotUpdate";
g_typeCast["HotUpdate"] = "cc.HotUpdate";
tolua_endmodule(tolua_S);
local hotUpdate = cc.HotUpdate:create("", "")
print("type(HotUpdate) = ", type(HotUpdate));
print("type(cc.HotUpdate) = ", type(cc.HotUpdate));
print("type(hotUpdate) = ", type(hotUpdate))