tolua++ 源码分析
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]*/
}
}