cocos2d tolua C++对象使用lua对象的变量,函数

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 */
        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 


你可能感兴趣的:(lua,cocos2d-x)