Lua常用堆栈操作

7-30 1,092 views

要擅于去看lua文档,源码, 部分翻译来自云风, ps C#也开源了 :)

正数表示相对于栈底的位置,栈底是1 ,负数表示相对于栈顶的位置,栈顶是-1;


入栈

lua_getfield (lua_State *L, int index, const char *k);

将t[k] 的值压栈, 这里的 t 是栈索引
index 处的值, 可能触发__index

lua_getglobal (lua_State *L, const char *name)

#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, s)

把全局变量 name 里的值压栈

lua_getmetatable (lua_State *L, int index);

如果索引index处的值有元表,则将其元表压栈,返回 1 。 否则不会将任何东西入栈,返回 0

lua_gettable (lua_State *L, int index)

将t[k]值压栈, t是栈index处值, k是栈顶值,.这个函数会弹出栈顶的key值(把结果放在栈上相同位置)可能触发__index

lua_rawget (lua_State *L, int index)

同上,不会触发__index

lua_rawgeti (lua_State *L, int index, lua_Integer n)

把 t[n] 的值压栈, 这里的 t 是指给定索引 index 处的一个值, 不出发__index

lua_gettop (lua_State *L)

返回栈中元素的个数,同时也是栈顶元素的索引,0表示栈为空

lua_insert (lua_State *L, int index);

栈顶元素移动到指定的有效索引index处, 依次移动这个索引之上的元素

lua_newtable (lua_State *L)

创建空表压栈等价于 lua_createtable(L, 0, 0)

lua_pushnumber (lua_State *L, lua_Number n)

lua_pushstring (lua_State *L, const char *s)

浮点数n压栈

s指向的字符串压栈, 返回内部副本的指针, s为Null则将nil压栈返回Null

lua_pushvalue (lua_State *L, int index)

复制index位置的元素压入栈顶

lua_tostring (lua_State *L, int index)

把索引index处(字符串或数字)转换为C字符串, 否则返回NUll


出栈

lua_setfield (lua_State *L, int index, const char *k);

做 t[k] = v 的操作, t 是栈索引 index 处的值, 而 v 是栈顶的值,这个函数将把这个值弹出堆栈,可能触发__newindex

lua_setglobal (lua_State *L, const char *name)

从栈上弹出一个值作为全局变量name的新值

lua_setmetatable (lua_State *L, int index)

将表弹出栈设置为索引index处值的原表

lua_settable (lua_State *L, int index)

做t[k] = v 的操作, t是索引index处的值,v是栈顶的值, k是栈顶之下的值, 这个函数会将键值都从栈弹出, 可能会出发__newindex

lua_rawset (lua_State *L, int index)

同上, 不触发__newindex

lua_rawseti (lua_State *L, int index, lua_Integer i)

等价于 t[i] = v, 这里的 t 是指给定索引 index 处的一个值, 而 v 是栈顶的值。函数将把这个值弹出栈, 不会触发__newindex

lua_settop (lua_State *L, int index)

把栈顶设为这个索引index,如果新栈顶比原来大,超出部分填nil, index= 0时,清空栈

lua_pop (lua_State *L, int n)

从栈中弹出 n 个元素

lua_remove (lua_State *L, int index)

移除栈索引index处的值, 将此索引之上的向下移动填充


lua_call (lua_State *L, int nargs, int nresults)

nargs为参数个数, 先压入函数,再按顺序压入参数,调用时(会从栈弹出所有参数和函数),再按顺序压入返回值(最后的结果在栈顶)nresults 被设置成 LUA_MULTRET十所有返回值都被压栈

lua: 
     a = f("how", t.x, 14)

C:
     lua_getglobal(L, "f");            
     lua_pushliteral(L, "how");      /* 1st argument */
     lua_getglobal(L, "t");          
     lua_getfield(L, -1, "x");       /* push result of t.x (2nd arg) */
     lua_remove(L, -2);              /* remove 't' from the stack */
     lua_pushinteger(L, 14);         /* 3rd argument */
     lua_call(L, 3, 1);              /* call 'f' with 3 arguments and 1 result,push result to the stack*/
     lua_setglobal(L, "a");          /* set global 'a' */

int lua_pcall (lua_State *L, int nargs, int nresults, int msgh)

如果有错误发生(返回值非0), lua_pcall 会捕获它, 然后把唯一的值(错误消息)压栈,然后返回错误码。 如果 msgh 是 0 ,返回在栈顶的错误消息就和原始错误消息完全一致。 否则, msgh 就被当成是 错误处理函数在栈上的索引位置.在发生运行时错误时, 这个函数会被调用而参数就是错误消息。错误处理函数的返回值将被 lua_pcall 作为错误消息返回在堆栈上。


Tolua为例, lua中调用gameobject.transform.position = pos流程简析

//从lua到C#基本流程:lua userdata->lua metatable->C# warp->lua id->C# translator->C#对象

//注册
L.BeginClass();//UnityEngine_GameObjectWrap
L.RegVar("transform", get_transform, null);//这些方法会被注册到对应metatable中
LuaDLL.tolua_variable(L, name, fget, fset);
	
//C tolua_variable RegVar调用,注册对应C方法到lua 
LUALIB_API void tolua_variable(lua_State *L, const char *name, lua_CFunction get, lua_CFunction set)
{ 
   ...
   lua_pushstring(L, name);
   tolua_pushcfunction(L, get); //做metatable["transform"] = get操作
   lua_rawset(L, -3);
   ...ga
}
//调用
gameobject.transform //访问userdata的metatable, 找到对应c方法
UnityEngine_GameObjectWrap .get_transform // 通过MonoPInvokeCallbackAttribute标签, 使C回调回C#
int udata = LuaDLL.tolua_rawnetobj(L, stackPos);

//C tolua_rawnetobj 把lua中的userdata变成c#可以辨认的id
LUALIB_API int tolua_rawnetobj(lua_State *L, int index)
{
    int* udata = (int*)lua_touserdata(L, index);//如果index处值为int直接作为id返回
    
    if (udata != NULL) 
    {
        return *udata;
    }
    else if (lua_istable(L, index))
    {
        lua_pushvalue(L, index);        
        lua_pushlightuserdata(L, &vptr);//如果是个table, 找到userdata
        lua_rawget(L, -2);                

        if (lua_isuserdata(L, -1))//把栈顶userdate替换到table位置
        {
            lua_replace(L, index);
            udata = (int*)lua_touserdata(L, index);//转换userdata为id返回

            if (udata != NULL) 
            {
                return *udata;
            }
        }    
        else
        {
            lua_pop(L, 1);
        }               
    }    

	return -1;
}

ObjectTranslator.TryGetValue(id)  //用这个id,从ObjectTranslator中获取C#的gameobject对象
gameobject.transform //真正C#拿到transform
ObjectTranslator.AddObject //如果之前未保存过,给transform分配一个id,id这个id会在lua中用来代表这个transform,transform要保存到ObjectTranslator供未来查找
LuaDLL.tolua_pushnewudata(L, reference, index);

//C tolua_pushnewudata
LUALIB_API void tolua_pushnewudata(lua_State *L, int metaRef, int index)
{
	lua_getref(L, LUA_RIDX_UBOX);//ubox表压栈, 值弱引用,存放userdata, 下次如果同样userdata直接从这里拿
	tolua_newudata(L, index);//在lua分配一个userdata,把id存进去
	lua_getref(L, metaRef);//拿到类型的metatable压栈
	lua_setmetatable(L, -2);//userdata附上metatable,让你可以transform.position这样使用它
	lua_pushvalue(L, -1);//栈顶放上transfrom返回
	lua_rawseti(L, -3, index);//将userdata以index为id, 保存到ubox中
	lua_remove(L, -2);	
}

//赋值
UnityEngine_TransformWrap.set_position
LuaDLL.tolua_rawnetobj(L, stackPos)
ObjectTranslator.TryGetValue(id)
LuaDLL.tolua_getvec3 //得到pos
transform.position = new Vector3(x,y,z) //真正赋值




Lua性能优化和思考

往往我们进行性能优化, 首先去搜索别人成果,然后想当然去应用, 优秀点的测试一下, 然后改完收工. 这里吐槽一句, 搜索优化类文章大多直接贴结论或者罗列测...

阅读全文

欢迎留言