lua栈对于将c函数注册的相关 api使用介绍

在lua端调用c函数,需要将c函数事先注册到lua的环境中
以下是相关push函数,下面的所有函数都是push一个data到栈中,放到栈的L-top-1的位置,并且栈的高度L->top++;

LUA_API void  (lua_pushnil) (lua_State *L);
LUA_API void  (lua_pushnumber) (lua_State *L, lua_Number n);
LUA_API void  (lua_pushinteger) (lua_State *L, lua_Integer n);
LUA_API void  (lua_pushlstring) (lua_State *L, const char *s, size_t l);
LUA_API void  (lua_pushstring) (lua_State *L, const char *s);
LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
                                                  va_list argp);
LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
LUA_API void  (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
LUA_API void  (lua_pushboolean) (lua_State *L, int b);
LUA_API void  (lua_pushlightuserdata) (lua_State *L, void *p);
LUA_API int   (lua_pushthread) (lua_State *L);


以下的函数是栈中的数据进行get和set,注意要和上面的函数配合使用

/*
** get functions (Lua -> stack)
lua从栈中获取data,然后将结果放到L->top-1位置
*/
/*
获取栈中idx位置的table在栈L->top - 1的key存储的value,
并且将结果存储到L->top - 1的位置上;
注意:调用此函数需要事先压入一个值,key

*/
LUA_API void  (lua_gettable) (lua_State *L, int idx);
/*
获取栈中idx位置的table在栈k位置存储的value
并且将结果存入L->top-1的位置
L->top++;
*/
LUA_API void  (lua_getfield) (lua_State *L, int idx, const char *k);
/*
只从当前table获取key值,避开他的metatable;
通过传入的idx获取到table,然后将此时L->top-1处的key放入到table中
将获取到的值再次存入栈顶的L->top-1位置
注意:调用此函数需要事先压入一个值,key
特别注意:这个函数不经过元表
*/
LUA_API void  (lua_rawget) (lua_State *L, int idx);
/*
获取在栈的idx的table在n这个位置上的整数value
并且将结果整数存入L->top-1位置上
L->top++;
特别注意:这个不经过元表,并且说明对于table的key的存放方式是不一样的,整型和其它,
这样做是为了提高查找效率
*/
LUA_API void  (lua_rawgeti) (lua_State *L, int idx, int n);
/*
创建一个table放在栈顶(L->top-1)位置
L->top++;
*/
LUA_API void  (lua_createtable) (lua_State *L, int narr, int nrec);
/*
创建一个userData对象放在栈顶(L->top-1)位置
L->top++;
*/
LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
/*
获取在栈的objindex的table它的元表
并且将结果放入L->top-1位置上
L->top++;
*/
LUA_API int   (lua_getmetatable) (lua_State *L, int objindex);
/*
获取指定位置的环境,并且将结果放到L->top-1的位置上
L->top++;
*/
LUA_API void  (lua_getfenv) (lua_State *L, int idx);


/*
** set functions (stack -> Lua)
lua对栈中的一些元素进行设置
*/
/*
L->top-1:value
L->top-2:key;
设置栈中idx处表的table[key]为value
栈移除之前压入的两个值
注意:调用此函数需要事先压入两个值,key,value
L->top -= 2
*/
LUA_API void  (lua_settable) (lua_State *L, int idx);
/*
L->top-1:value
key:k;
设置栈中idx处表的table[k]为value
栈移除之前压入的1个值
注意:调用此函数需要事先压入一个值,value
L->top--;
*/
LUA_API void  (lua_setfield) (lua_State *L, int idx, const char *k);
/*
L->top-1:value
L->top-2:key;
设置栈中idx处表的table[key]为value
栈移除之前压入的两个值
特别注意:此处的表的设置,不会经过元表
注意:调用此函数需要事先压入两个值,key,value
L->top -= 2;
*/
LUA_API void  (lua_rawset) (lua_State *L, int idx);
/*
L->top-1:value
key:n;(为整型)
设置栈中idx处表的table[k]为value
栈移除之前压入的1个值
特别注意:此处的表的设置,不会经过元表
注意:调用此函数需要事先压入一个值,value
 L->top--;
*/
LUA_API void  (lua_rawseti) (lua_State *L, int idx, int n);
/*
设置原表 将栈顶top-1处的元素设置为栈中objinde位置元素的原表
注意调用此函数时需要事先压入一个值,value
L->top--;
*/
LUA_API int   (lua_setmetatable) (lua_State *L, int objindex);
/*
设置一个函数的环境
(1)当第一个参数为一个函数时,表示设置该函数的环境
(2)当第一个参数为一个数字时,为1代表当前函数,2代表调用自己的函数,3代表调用自己的函数的函数,以此类推
 所谓函数的环境,其实一个环境就是一个表,该函数被限定为只能访问该表中的域,或在函数体内自己定义的变量
 注意:调用此函数需要事先压入一个值,value
 L->top--;
*/
LUA_API int   (lua_setfenv) (lua_State *L, int idx);

总结:针对将C函数注册到lua的环境中,lua对于set一个值,一般要push一个key或者key和value,然后L->top-1或者L->top-2,lua对于get一个值一般要push 零个key或者一个key,相应的l->top++或者栈的高度不变

1:对于从栈中set数据

lua_settable:(经过原表)预先push两个值(key,value)进去,处理完栈pop两个元素
lua_setfield:(经过原表)预先push一个值(value)进去+加上自己传参一个(非整形key),处理完栈pop一个元素
lua_rawseti:(不经过原表)预先push一个值(value)进去+加上自己传参一个(整形key),处理完栈pop一个元素
lua_rawset:(不经过原表)预先push两个值(key,value)进去,处理完栈pop两个元素

2:对于从栈中get数据:数据放在L->top-1位置

lua_gettable:(经过原表)预先push一个值(key)进去,处理完栈大小不变
lua_getfield:(经过原表)预先push零个值(value)进去+加上自己传参一个(非整形key),处理完栈大小+1
lua_rawgeti:(不经过原表)预先push零个值(value)进去+加上自己传参一个(整形key),处理完栈大小+1
lua_rawget:(不经过原表)预先push一个值(key)进去,处理完栈大小不变

3:对于index2adr()函数的认识

lua操作数据是模仿栈的,L->top指向一个空闲的单位,L->top-1指向一个实际存在元素的单位,L->base指向栈底,lua在获取栈中元素的时候使用的是索引,且务必调用一个接口index2adr(L,idx),这个接口当传入的参数是正值索引则从栈底往上+,如果是负数且大于最小的伪索引则从栈顶往下-,伪索引指的是(-10000注册表,-10001环境表,-10002全局表)

4:对于三个伪索引的认识

这三个伪索引指的的值,并不存在于栈中,只是借助栈的索引来获取,获取函数是index2adr(L,idx),注册表(-10000)存放在global_state中且在整个lua虚拟机中是唯一的,环境表(-10001)存放在当前闭包中,_G全局表(-10002)存放在当前的lua_state中,在lua5.1关于三者的关系是,在L->top[注册表][_LOADED][_G],_G在注册表中保留一份地址,可以通过注册表找到_G,_G是在lua中可以访问到的,而注册表是不可以访问到的,我们自己写的库一般都存在全局表中,暴漏给lua调用

5:lua_setmetatable (lua_State *L, int objindex)设置原表

预先push一个元表table进入栈的L->top-1的位置,然后通过索引找到要关联元表的table,
最后设置 table.metatable = 元表table,根据代码可以看出:只有table和userData 可以有原表

6:(lua_getmetatable) (lua_State *L, int objindex)获取原表

通过索引获取到table,然后找他的table.metatable,最后将结果放到L->top-1的位置上,栈高度+1
根据代码可以看出:只有table和userData 可以有原表

7:void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val)

在当前table寻找key,如果找不到就去找他的元表,如果找不到就去元表的元表去找,以此类推,实现了继承

/*
获取t[key]值,并且将它的值赋给val
*/
void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) {
  int loop;
  for (loop = 0; loop < MAXTAGLOOP; loop++) {
    const TValue *tm;
    if (ttistable(t)) {  /* `t' is a table? */
      Table *h = hvalue(t);
      const TValue *res = luaH_get(h, key); /* do a primitive get */
      if (!ttisnil(res) ||  /* result is no nil? */
          (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */
        setobj2s(L, val, res);
        return;
      }
      /* else will try the tag method */
    }
    else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))
      luaG_typeerror(L, t, "index");
    if (ttisfunction(tm)) {
      callTMres(L, val, tm, t, key);
      return;
    }
	/*
	重点:更换table,用元表来找对应的key
	此处表达了lua中的继承概念,可以通过设置元表来实现继承
	*/
    t = tm;  /* else repeat with `tm' */ 
  }
  luaG_runerror(L, "loop in gettable");
}

8: lua_pop(L,n)

当n>0的时候,就是将栈顶位置下移n个元素,相当于从栈中pop这n个元素
当n<0的时候,就是将栈顶位置上移n个元素,上移的每个位置补充为nil

#define lua_pop(L,n)		lua_settop(L, -(n)-1)

LUA_API void lua_settop (lua_State *L, int idx) {
  lua_lock(L);
  if (idx >= 0) {
    api_check(L, idx <= L->stack_last - L->base);
	//将栈多余位置的元素设为nil
    while (L->top < L->base + idx)
      setnilvalue(L->top++);
    L->top = L->base + idx;
  }
  else {
    api_check(L, -(idx+1) <= (L->top - L->base));
    L->top += idx+1;  /* `subtract' index (index is negative) */
  }
  lua_unlock(L);
}

9:lua_rawgeti (lua_State *L, int idx, int n)

找出在栈中指定索引idx的元素table,将table的指定位置的索引n的值压如栈中
栈的大小加一

额外的发现,我们往table中放入(key,value)时,可以将key设置为整数,这样方便快速查找

LUA_API void lua_rawgeti (lua_State *L, int idx, int n) {
  StkId o;
  lua_lock(L);
  o = index2adr(L, idx);
  api_check(L, ttistable(o));
  setobj2s(L, L->top, luaH_getnum(hvalue(o), n));
  api_incr_top(L);
  lua_unlock(L);
}

10:lua_rawget (lua_State *L, int idx)

取出栈中指定索引idx的table,把栈顶元素作为它的key,取出table[L->top-1]处的value,并且将value放到栈顶,
栈的大小不变,不加特别说明,栈顶指的都是L->top-1

LUA_API void lua_rawget (lua_State *L, int idx) {
  StkId t;
  lua_lock(L);
  //获取栈中指定位置元素的地址
  t = index2adr(L, idx);
  api_check(L, ttistable(t));
  setobj2s(L, L->top - 1, luaH_get(hvalue(t), L->top - 1));
  lua_unlock(L);
}

11:lua_rawset (lua_State *L, int idx)

取出栈中指定索引idx处的table,将栈的L->top-2处的元素设为这个table的key,
然后将L->top-1作为value赋值给table[L->top-2],栈的大小减2

table[L->top-2] =L->top-1;

LUA_API void lua_rawset (lua_State *L, int idx) {
  StkId t;
  lua_lock(L);
  api_checknelems(L, 2);
  t = index2adr(L, idx);
  api_check(L, ttistable(t));
  setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1);
  luaC_barriert(L, hvalue(t), L->top-1);
  L->top -= 2;
  lua_unlock(L);
}

12:lua_pushvalue (lua_State *L, int idx)

将栈中指定位置idx的元素压入到栈顶(L->top),栈的大小加一

LUA_API void lua_pushvalue (lua_State *L, int idx) {
  lua_lock(L);

  setobj2s(L, L->top, index2adr(L, idx));
  api_incr_top(L);

  lua_unlock(L);
}

13:lua_next (lua_State *L, int idx)

查找栈中指定位置的table中的value,并且将其放入栈顶位置
栈的高度+1

LUA_API int lua_next (lua_State *L, int idx) {
  StkId t;
  int more;
  lua_lock(L);
  t = index2adr(L, idx);
  api_check(L, ttistable(t));
  more = luaH_next(L, hvalue(t), L->top - 1);
  /*
  L->top value
  L->top-1 下一次要找的key
  */
  if (more) {
	/*
	   L->top   null
	   L->top-1 value
       L->top-2 下一次要找的key
	*/
    api_incr_top(L);
  }
  else  /* no more elements */
    L->top -= 1;  /* remove key */
  lua_unlock(L);
  return more;
}

13:luaH_next (lua_State *L, Table *t, StkId key)

寻找table中的key(L->top-1)
如果传入的key为nil,说明要从根元素开始找
默认优先从从数组中查找
如果找不到 则从链表中开始查找

int luaH_next (lua_State *L, Table *t, StkId key) {
  int i = findindex(L, t, key);  /* find original element */
  for (i++; i < t->sizearray; i++) {  /* try first array part */
    if (!ttisnil(&t->array[i])) {  /* a non-nil value? */
	  //找到key为i的位置不为空
	  //那么key的下一个位置就是i+1
      setnvalue(key, cast_num(i+1));
	  //此处相当于把value放到了L->top上
	  //因为一般情况此处的key指的是L->top-1
      setobj2s(L, key+1, &t->array[i]);
      return 1;
    }
  }
  for (i -= t->sizearray; i < sizenode(t); i++) {  /* then hash part */
    if (!ttisnil(gval(gnode(t, i)))) {  /* a non-nil value? */
	  //设置下一次的查找key
      setobj2s(L, key, key2tval(gnode(t, i)));
	  //此处相当于把value放到了L->top上
	  //因为一般情况此处的key指的是L->top-1
      setobj2s(L, key+1, gval(gnode(t, i)));
      return 1;
    }
  }
  return 0;  /* no more elements */
}

14:lua_pcall (lua_State *L, int nargs, int nresults, int errfunc)

在c领域,想要调用lua栈中的函数,可以使用这个函数
栈中函数的位置是从上往下,所有参数+1处就是要执行函数的位置,这符合一个设计
就是执行函数时。虚拟机会先把函数首地址率先入栈,然后再把函数的参数依次入栈,最后执行函数

LUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) {
  struct CallS c;
  int status;
  ptrdiff_t func;
  lua_lock(L);
  api_checknelems(L, nargs+1);
  checkresults(L, nargs, nresults);
  if (errfunc == 0)
    func = 0;
  else {
    StkId o = index2adr(L, errfunc);
    api_checkvalidindex(L, o);
    func = savestack(L, o);
  }
  c.func = L->top - (nargs+1);  /* function to be called */
  c.nresults = nresults;
  status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
  adjustresults(L, nresults);
  lua_unlock(L);
  return status;
}

15:LUALIB_API void (luaL_register) (lua_State *L, const char *libname, const luaL_Reg *l)

将c函数注册到全局环境中给lua调用的注册接口

//默认情况下,lua在启动的时候会为我们注册一些必要c函数供我们在lua中调用
/*
L->top[LUA_REGISTRYINDEX][_LOADED][_G]
L->top[LUA_REGISTRYINDEX][_LOADED][coroutine]
L->top[LUA_REGISTRYINDEX][_LOADED][package]
L->top[LUA_REGISTRYINDEX][_LOADED][table]
L->top[LUA_REGISTRYINDEX][_LOADED][io]
L->top[LUA_REGISTRYINDEX][_LOADED][os]
L->top[LUA_REGISTRYINDEX][_LOADED][string]
L->top[LUA_REGISTRYINDEX][_LOADED][math]
L->top[LUA_REGISTRYINDEX][_LOADED][debug]
*/
LUALIB_API void (luaL_register) (lua_State *L, const char *libname,
                                const luaL_Reg *l) {
  luaI_openlib(L, libname, l, 0);
}
LUALIB_API void luaI_openlib (lua_State *L, const char *libname,
                              const luaL_Reg *l, int nup) {
  if (libname) {
    int size = libsize(l);
    /* check whether lib already exists */
    luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1);//此时L->top[LUA_REGISTRYINDEX][_LOADED]存储的table放到了L->top-1的位置上
    lua_getfield(L, -1, libname);  /* get _LOADED[libname] */
    if (!lua_istable(L, -1)) {  /* not found? */
      lua_pop(L, 1);  /* remove previous result */
      /* try global variable (and create one if it does not exist) */
      if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL)//此时L->top[LUA_GLOBALSINDEX][libname]存储的table放到了L->top-1的位置上
        luaL_error(L, "name conflict for module " LUA_QS, libname);
      lua_pushvalue(L, -1);//L->top-1,L->top-2存储的值都是L->top[LUA_GLOBALSINDEX][libname],起到的作用,即保证注册表的table必须在-3的位置
      lua_setfield(L, -3, libname);  /* _LOADED[libname] = new table */
	  //最后
	  //L->top[LUA_REGISTRYINDEX][_LOADED][libname] = L->top[LUA_GLOBALSINDEX][libname];
    }
    lua_remove(L, -2);  /* remove _LOADED table */
    lua_insert(L, -(nup+1));  /* move library table to below upvalues */
  }
  for (; l->name; l++) {
    int i;
    for (i=0; i<nup; i++)  /* copy upvalues to the top */
      lua_pushvalue(L, -nup);
    lua_pushcclosure(L, l->func, nup);
    lua_setfield(L, -(nup+2), l->name);
  }
  lua_pop(L, nup);  /* remove upvalues */
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值