Lua 栈操作

参考: 1. lua交互为何用堆栈? 2. lua堆栈操作

一、为什么使用堆栈

Lua和C有两点差异:动态类型和垃圾回收。

因为Lua是动态类型,因此在Lua中"a[k]=v"这条语句中的变量a、k、v可以有好几种不同的类型。 如果我们用C语言提供这个功能的操作,那么需要几十个不同的函数完成这一操作。 这是不现实的。

一个解决方案是在C中声明union类型的结构lua_Value来解决这个问题,它能够描述所有类型的Lua值。 这样上面的函数就可以声明为:
void lua_settable(lua_Vaule a, lua_Value b, lua_Value c);

这个解决方案有两个缺点:

  1. 将如此复杂的类型映射到其他语言可能很困难,Lua不仅设计成与C/C++易于交互,Java等类似语言应该也一样;
  2. Lua负责垃圾回收,如果将Lua的值保存在C变量中,Lua引擎没有办法了解这种用法;它可能错误的认为某个值为垃圾并回收;

Lua的C API没有定义任何类似Lua_Value的类型。它用一个抽象的栈在Lua和C之间交换值。 栈中的每条记录都可以保存任何Lua值。

无论何时想要从Lua中请求一个值,只需:

  1. Lua解释器将这个值压入栈;
  2. 调用Lua C API把值弹出;

这样仍然需要不同的函数将每种C类型的值压入栈和一个从不同函数从栈上取值(并不弹出)。 但这种方法避免了组合爆炸(combinatorial explosion)。 另外栈是由Lua管理的,垃圾回收器知道哪个值在被C使用。 几乎所有的API函数都用到了栈。

二、 栈操作

Lua栈以一个严格的LIFO规则来操作栈。 当调用Lua时,它只会改变栈顶部分。 而C的API却有更多的自由,它可以查询栈上的任何元素,甚至是在任何一个位置插入和删除元素。 栈中的元素通过索引值进行定位,其中栈顶是-1,栈底是1。

基本栈操作:

int lua_checkstack(L, int n); 检查栈上是否有能插入n个元素的空间;没有返回0
int lua_gettop(L); 返回top索引值,即返回栈中元素的个数。
void lua_settop(L, int i); 把top索引值设成i,即修改栈中元素数量;多退栈少补nil
void lua_pushvalue(L, int i); 把stack[i]元素拷贝一份插入栈顶
void lua_insert(L, int i); 把stack[top]插入到stack[i],其他元素相应调整位置
void lua_replace(L, int i); 把stack[top]替换掉stack[i],并弹出stack[top]
void lua_remove(L, int i); 把stack[i]删除,其他元素相应调整
void lua_xmove(LS *a, LS *b, int n) 把a的栈顶n个元素弹出压入到b的栈

C出栈函数:( stack -> C )

int lua_is *(L, int i); 返回1,如果*是(number, string, cfunction, userdata)类型
int lua_type(L, int i); 返回stack[i]的类型,CC lua_typename(L, int t);再转换成可读类型
lua_to *(L, int i); 返回stack[i]相应类型(boolean, number, integer, string(0结束), lstring, function, userdata, thread, pointer)
size_t lua_objlen(L, int i) 返回指定的索引处的值的长度。对于 string ,那就是字符串的长度;对于 table ,是取长度操作符 ('#') 的结果;对于 userdata ,就是为其分配的内存块的尺寸;对于其它值,为 0 。
int lua_equal(L, int i, int j); 当lua中stack[i]==stack[j]时返回true(可能调用__eq元方法)
int rawequal(L, int i, int j); 同上,不调用元方法
int lua_lessthan(L, int i, ing j); 当lua中stack[i]

C入栈函数:( C -> stack)

void lua_push * (L, * ); 把相应的元素压入栈顶(nil, boolean, number, integer, string, lstring, fstring, vfstring, cclosure, thread, lightuserdata)

封装的宏

#define lua_pop(L,n) lua_settop(L, -(n)-1) 弹出n个元素抛弃掉
#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) 压入c函数
#define lua_strlen(L,i) lua_objlen(L, (i)) 返回字符串长度
#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) 一系列类型判断
#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL)
#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN)
#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD)
#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE)
#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0)
#define lua_pushliteral(L, s) lua_pushlstring(L, "" s,(sizeof(s)/sizeof(char))-1) 压入literal
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)

三、 lua_State中的栈指针

lua_State表示了Lua VM上下文的一种状态。 CallInfo代表了当前函数的调用信息。

/*
** informations about a call
*/

typedef TValue *StkId;  /* index to stack elements */

typedef struct CallInfo {
  StkId base;  /* base for this function */
  StkId func;  /* function index in the stack */
  StkId	top;  /* top for this function */
  const Instruction *savedpc;
  int nresults;  /* expected number of results from this function */
  int tailcalls;  /* number of tail calls lost under this entry */
} CallInfo;

struct lua_State {
   ...
  StkId top;  /* first free slot in the stack */
  StkId base;  /* base of current function */
  StkId stack_last;  /* last free slot in the stack */
  StkId stack;  /* stack base */
  int stacksize;
  ...
  CallInfo *ci;  /* call info for current function */
  CallInfo *end_ci;  /* points after end of ci array*/
  CallInfo *base_ci;  /* array of CallInfo's */
  ...
};

发表评论