cocos2dx v3.x lua绑定分析

打算新项目转到cocos2dx v3上了,下载代码浏览过后发现改动真是非常大,结构性调整很多。

比如tolua绑定这一块,就几乎全翻新了。

胶水代码的生成,改成了全自动式的,通过clang来分析c++代码,可以准确的知道每一个类、函数、参数的信息,再也不用手动写pkg文件了。

运行期对象管理这块,似乎也有了不少改动,至少我原来的一些扩展代码运行不了了,还没来得及细看,待看完再一一录下。

先记录一下目前已看清楚的【类名表、类元表、对象实例】之间的关系:

1、类元表:最核心的表,在lua代码里是不可见的。这是在注册每个类的第一步时建立的:

tolua_usertype(tolua_S,"cc.Application");

但其实类元表也就是一个普通的table,只不过它挂在lua registry上,所以说一般的逻辑代码是不会用到它的。类的所有函数、与父类的关系,也都记在这个核心的元表里。

2、类名表:这就是lua代码里要使用该类时所用的名字,也就是:

local app=cc.Application:getInstance()

里cc.Application这个变量对应的表。它是注册类的第二步中建立的:

    tolua_cclass(tolua_S,"Application","cc.Application","",nullptr);

【类名表】的元表就是【类元表】。

3、对象实例:每个c++ object被push到lua里,是以一个userdata表示。它的元表被设成【类元表】,所对它调用的各种方法都会索引到相应的c++函数上。它的生成是在以下函数中完成:

void tolua_pushusertype_internal (lua_State* L, void* value, const char* type, int addToRoot)

 

其实在这里我不是太清楚每个类为什么要有两个表来表示,从功能上说,完全可以合为一个,也就是【类元表】上的所有功能都可以放在【类名表】里实现。也许是因为作者担心类名表存在于普通变量空间里,可能会被无意中修改覆写吧!但分开之后,也明显导致了一些后续处理上的麻烦:比如在给【模块】注册函数时,就要判断当前【模块】是一个【普通模块】(对应于c++里的名字空间)还是一个【类名表】,如果是后者,那函数不能直接挂在它上面,而是要转挂到【类元表】上去,这是在下面函数中处理的:

/* Begin module
    * It pushes the module (or class) table on the stack
*/
TOLUA_API void tolua_beginmodule (lua_State* L, const char* name)
{
    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);
    }
}

 

同时这个修改也导致了我之前的一些代码运行失效。因为我会给一些类添加扩展函数,如:

rawset(ccui.Widget,"set_enable", function(self, v)
  self:setTouchEnabled(v)
  self:setBright(v)
end

按上述逻辑,实例对象的元表直接指向类元表,也就是说完全绕过了类名表(类名表实际只在创建对象时起个提供类变量的引子作用),而rawset在类名表上的扩展函数自然也被忽略了。

修改办法也很简单,去掉rawset,直接往类名表上写就行了,这会导致其通过元表上的class_newindex_event函数,把数据改记到类元表上,从而符合了实例对象的属性访问流程。

至于之前为什么用一个rawset多此一举?那也是无奈之法,因为上一版本的cocos2dx在lua绑定实现上就是有点问题,不用rawset的话直接就挂了。所以说它现在又改好了,也算是回归自然吧。

 

posted on 2015-02-18 21:49  冷欺花  阅读(921)  评论(0编辑  收藏  举报

导航