Lua API 小记2(lua值, 栈解释)

这些东西是平时遇到的, 觉得有一定的价值, 所以记录下来, 以后遇到类似的问题可以查阅, 同时分享出来也能方便需要的人, 转载请注明来自RingOfTheC[ring.of.the.c@gmail.com]

 

打算记录一些lua_api, 可能会觉得lua文档中已经说的很清楚了, 但是我将用自己的方式, 记录下我认为重要的东西, 先约定一下api说明的格式

编号. api作用简述

api函数原型

api操作说明

返回值说明

对栈的影响

注意事项

 

这次主要记录lua函数调用的几个相关的api, 需要说明的是, 对于这几个api, lua手册上写的相当详细, 相当好, 可以直接看手册

 

10. 调用一个lua函数

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

lua c api的特点就是"不是一个人在战斗" [我想表达的意思是, lua中的一句话, 在c api实现起来就是n句, 可能有人疑惑那为什么不直接用lua多好, c api这么麻烦, 答案是有的事只能用c api才能实现], 所以, 调用它之前, 需要布局一下栈, 第一, 要把要call的函数压入栈; 第二, call要用的参数正序压入栈中; 然后才能调用lua_call, 调用完了, 自己去取返回值, 它都给你压栈上了.

操作:

      argn = Stack.pop()

      ... // 一共压入nargs个参数

      arg2 = Stack.pop()

      arg3 = Stack.pop()

       func = Stack.pop() // 函数本身也弹出

       res1, res2, ..., resj = func(arg1, arg2, ..., argn)

       Stack.push(res1)

       Stack.push(res2)

       … // 压入nresults个返回值

       Stack.push(resj)

 

 

无返回值

调用结束后, 栈高度增加 nresults – (1 + nargs), 如果将nresults参数设置为LUA_MULTRET, 那么lua返回几个值, 栈上就压入几个值, 否者强制压入nresults个值, 不足的是空值, 多余的抛弃掉

注意, 这个函数是有危险的, 如果在其中发生了错误, 会直接退出程序

这个函数的用途: 尚未发现, 除非你能接受出错立马退出, 反正我是做游戏的, 我受不起, 呵呵, 顺便一说, lauxlib.h中的luaL_check*一族函数也是这样的, 不符合预期的话, 直接退出, 这些函数都要小心, 有类似于断言的效果.

 

 

11. 保护下调用一个lua函数

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

参数, 行为和lua_call都一样, 如果在调用中没有发生任何错误, lua_pcall == lua_call; 但是如果有错误发生时, lua_pcall会捕获它

errfunc指出了Stack上的一个元素, 这个元素应该是一个函数, 当发生错误的时候

    ef = Stack[errfunc]

    value = ef(errmsg)

    Stack.push(value)

也就是说, 在错误的时候, errfunc指定的错误处理函数会被调用, 该处理函数的返回值被压到栈上.

 

默认情况下, 可以给errfunc传值0, 实际的效果是指定了这样一个函数做出错处理 function defaulterr(errmsg) return errmsg end.

本函数有返回值 LUA_ERRRUN运行时错误  LUA_ERRMEM内存分配错误[注意, 这种错会导致lua调用不了错误处理函数]  LUA_ERRERR运行错误处理函数时出错了, 写程序的时候必须检查返回值:)

强烈推荐该函数, 不过事实上大家也都用的这个函数:)

 

 

12. 保护下调用一个c函数

int lua_cpcall (lua_State *L, lua_CFunction func, void *ud)

以保护模式调用c函数, func中可以且只能从堆栈上拿到一个参数, 就是ud, 当有错误时, 和lua_pcall返回相同的错误代码, 并在堆栈顶部留下errmsg字符串, 调用成功的话它返回零, 并且不会修改堆栈, 所有从func中返回的值都被扔掉.

这里注意的问题是:

1. "当有错误时", 这个错误的意思是lua的错误, 而不是c/c++的错误. 在func中使用lua_call和lua_check*族函数, 并不会导致程序退出了, 而是表现的像lua_pcall那样.

2. 调用成功的时候func中的返回值都被扔掉了.

 

 

------------------------------------------------ 华丽的分割线 ------------------------------------------------------------

 

1. 理解lua的栈到底是什么?

    lua的栈类似于以下的定义, 它是在创建lua_State的时候创建的:

             TValue stack[max_stack_len]  // 欲知内情可以查 lstate.c 的stack_init函数

    存入栈的数据类型包括数值, 字符串, 指针, talbe, 闭包等, 下面是一个栈的例子:

          lua栈

   执行下面的代码就可以让你的lua栈上呈现图中的情况

    lua_pushcclosure(L, func, 0) // 创建并压入一个闭包

    lua_createtable(L, 0, 0)        // 新建并压入一个表

    lua_pushnumber(L, 343)      // 压入一个数字

    lua_pushstring(L, “mystr”)   // 压入一个字符串

 

    这里要说明的是, 你压入的类型有数值, 字符串, 表和闭包[在c中看来是不同类型的值], 但是最后都是统一用TValue这种数据结构来保存的:), 下面用图简单的说明一下这种数据结构:

    lua

 

    TValue结构对应于lua中的所有数据类型, 是一个{值, 类型} 结构, 这就lua中动态类型的实现, 它把值和类型绑在一起, 用tt记录value的类型, value是一个联合结构, 由Value定义, 可以看到这个联合有四个域, 先说明简单的

        p -- 可以存一个指针, 实际上是lua中的light userdata结构

        n -- 所有的数值存在这里, 不过是int , 还是float

        b -- Boolean值存在这里, 注意, lua_pushinteger不是存在这里, 而是存在n中, b只存布尔

        gc -- 其他诸如table, thread, closure, string需要内存管理垃圾回收的类型都存在这里

        gc是一个指针, 它可以指向的类型由联合体GCObject定义, 从图中可以看出, 有string, userdata, closure, table, proto, upvalue, thread

    从下面的图可以的得出如下结论:

        1. lua中, number, boolean, nil, light userdata四种类型的值是直接存在栈上元素里的, 和垃圾回收无关.

        2. lua中, string, table, closure, userdata, thread存在栈上元素里的只是指针, 他们都会在生命周期结束后被垃圾回收.

 

 

2. lua和c通信的约定

    lua和c通信时有这样的约定: 所有的lua中的值由lua来管理, c++中产生的值lua不知道, 类似表达了这样一种意思: "如果你(c/c++)想要什么, 你告诉我(lua), 我来产生, 然后放到栈上, 你只能通过api来操作这个值, 我只管我的世界", 这个很重要, 因为:

         "如果你想要什么, 你告诉我, 我来产生"就可以保证, 凡是lua中的变量, lua要负责这些变量的生命周期和垃圾回收, 所以, 必须由lua来创建这些值(在创建时就加入了生命周期管理要用到的簿记信息)

         "然后放到栈上, 你只能通过api来操作这个值", lua api给c提供了一套完备的操作界面, 这个就相当于约定的通信协议, 如果lua客户使用这个操作界面, 那么lua本身不会出现任何"意料之外"的错误.

         "我只管我的世界"这句话体现了lua和c/c++作为两个不同系统的分界, c/c++中的值, lua是不知道的, lua只负责它的世界

 

 

3. lua value 和 c value的对应关系

             c          lua
         nil           无    {value=0, tt = t_nil}
      boolean       int  非0, 0    {value=非0/0, tt = t_boolean}
      number       int/float等   1.5    {value=1.5, tt = t_number}
   lightuserdata    void*, int*, 各种*  point    {value=point, tt = t_lightuserdata}
      string          char  str[]    {value=gco, tt = t_string}   gco=TString obj
      table            无    {value=gco, tt = t_table}  gco=Table obj
      userdata            无    {value=gco, tt = t_udata} gco=Udata obj
      closure            无    {value=gco, tt = t_function} gco=Closure obj

 

可以看出来, lua中提供的一些类型和c中是对应的, 也提供一些c中没有的类型. 其中有一些药特别的说明一下:

        nil值, c中没有对应, 但是可以通过lua_pushnil向lua中压入一个nil值

        注意: lua_push*族函数都有"创建一个类型的值并压入"的语义, 因为lua中所有的变量都是lua中创建并保存的, 对于那些和c中有对应关系的lua类型, lua会通过api传来的附加参数, 创建出对应类型的lua变量放在栈顶, 对于c中没有对应类型的lua类型, lua直接创建出对应变量放在栈顶.

       例如:    lua_pushstring(L, “string”) lua根据"string"创建一个 TString obj, 绑定到新分配的栈顶元素上

                  lua_pushcclosure(L,func, 0) lua根据func创建一个 Closure obj, 绑定到新分配的栈顶元素上

                  lua_pushnumber(L,5) lua直接修改新分配的栈顶元素, 将5赋值到对应的域

                  lua_createtable(L,0, 0)lua创建一个Tabke obj, 绑定到新分配的栈顶元素上

 

       总之, 这是一个 c value –> lua value的流向, 不管是想把一个简单的5放入lua的世界, 还是创建一个table, 都会导致

                  1. 栈顶新分配元素    2. 绑定或赋值

                还是为了重复一句话, 一个c value入栈就是进入了lua的世界, lua会生成一个对应的结构并管理起来, 从此就不再依赖这个c value

        lua value –> c value时, 是通过 lua_to* 族api实现, 很简单, 取出对应的c中的域的值就行了, 只能转化那些c中有对应值的lua value, 比如table就不能to c value, 所以api中夜没有提供 lua_totable这样的接口.

 

鉴于再写过长, 今天就先记录到这里:)

posted @ 2010-11-04 16:21  ROTC  阅读(7952)  评论(2编辑  收藏  举报