tolua++ 源碼分析
圖中的黑色表示lua table, 灰色表示userdata, 淺灰=》light userdata, 綠色=》內存
tips:
1, lua裏的userdata是一塊內存(lua_newuserdata時指定體積),有metatable,但是doesn't have any key。 所以, 每次訪問一個usrdata,都一定是到它的metatable裏找的。
2,lightuserdata就是一個指針,沒key, 沒metatable.
3, 上圖中的userdata爲什麼存的也是c++ instance的內存地址呢。
因爲tolua++就是這麼幹的,它申請了userdata,並且指定體積是一個指針的大小,然後往這個內存裏。 如此而已。
4,tolua++導出來的那些c++對象(或者我們在lua裏“new”出來的c++對象),我們在lua裏操作,是把它當做一個普通的table的(也就是上圖右側的灰色方塊)。如果你做過lua項目的話,你肯定往往這些“table”上寫過自定義的屬性, 但這也是由tolua++支持的,因爲他們是“userdata”,不是table。
-----------------------正文----------------------
介紹之前,先貼一篇不錯的blog,可以作爲讀tolua++的綱要。
今天把tolua++源碼裏的"tolua_pushusertype()"這個函數讀了一下,覺得這應該是tolua++裏最核心的一個函數了。
顧名思義,就是你給我一個c++實例(value指針所指), 我生成一個與之對應的userdata,並push之
但這個function完成的功能可不止這些,c++對象與其對應lua userdata的之所以能“對應”,基本就是在這個函數裏實現的。
把這部分源碼貼出來吧,自己添了一些註釋:
/*@type correspond to C++ class Name
*@value c++ instance pointer
*/
TOLUA_API void tolua_pushusertype (lua_State* L, void* value, const char* type)
{
if (value == NULL)
lua_pushnil(L);
else
{ /*usually, tolua++ build a correspoding lua table( say class table ) for each Class typein C++
*now, we just fetch find this table by 'type'( tolua++ already build it somewhere 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"); /*ubox means userdata box*/
lua_rawget(L,-2); /* stack: mt ubox */
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
lua_pushstring(L, "tolua_ubox");
lua_rawget(L, LUA_REGISTRYINDEX); /*fetch from global registery, i don't know why either*/
};
lua_pushlightuserdata(L,value); /* stack: mt ubox key<value> */
lua_rawget(L,-2); /* stack: mt ubox ubox[value] */
if (lua_isnil(L,-1)) /*if hasn't allocate a userdata for it*/
{
lua_pop(L,1); /* stack: mt ubox */
lua_pushlightuserdata(L,value);
*(void**)lua_newuserdata(L,sizeof(void *)) = value; /* stack: mt ubox value newud */
lua_pushvalue(L,-1); /* stack: mt ubox value newud newud */
lua_insert(L,-4); /* stack: mt newud ubox value newud */
/*ubox[ lightuserdata ] = userdata */
lua_rawset(L,-3); /* ubox[value] = newud, stack: mt newud ubox */
lua_pop(L,1); /* stack: mt newud */
lua_pushvalue(L, -2); /* stack: mt newud mt */
/*let the metatable of new userdata refer to 'Class table'*/
lua_setmetatable(L,-2); /* update mt, stack: mt newud */
#ifdef LUA_VERSION_NUM
lua_pushvalue(L, TOLUA_NOPEER);
lua_setfenv(L, -2);
#endif
}
else /*i don't care about this branch now*/
{
/* check the need of updating the metatable to a more specialized class */
lua_insert(L,-2); /* stack: mt ubox[u] ubox */
lua_pop(L,1); /* stack: mt ubox[u] */
lua_pushstring(L,"tolua_super");
lua_rawget(L,LUA_REGISTRYINDEX); /* stack: mt ubox[u] super */
lua_getmetatable(L,-2); /* stack: mt ubox[u] super mt */
lua_rawget(L,-2); /* stack: mt ubox[u] super super[mt] */
if (lua_istable(L,-1))
{
lua_pushstring(L,type); /* stack: mt ubox[u] super super[mt] type */
lua_rawget(L,-2); /* stack: mt ubox[u] super super[mt] flag */
if (lua_toboolean(L,-1) == 1) /* if true */
{
lua_pop(L,3); /* mt ubox[u]*/
lua_remove(L, -2);
return;
}
}
/* type represents a more specilized type */
/*luaL_getmetatable(L,type); // stack: mt ubox[u] super super[mt] flag mt */
lua_pushvalue(L, -5); /* stack: mt ubox[u] super super[mt] flag mt */
lua_setmetatable(L,-5); /* stack: mt ubox[u] super super[mt] flag */
lua_pop(L,3); /* stack: mt ubox[u] */
}
lua_remove(L, -2); /* stack: ubox[u]*/
}
}