主页 > 文章分享 >

Lua 5.3 参考手册 4 – 编程接口

发布时间:2019-04-01 编辑:admin 标签: 热度:802

这个部分描述了 Lua 的 C API , 也就是宿主程序跟 Lua 通讯用的一组 C 函数。 所有的 API 函数按相关的类型以及常量都声明在头文件 lua.h 中。 虽然我们说的是“函数”, 但一部分简单的 API 是以宏的形式提供的。 除非另有说明, 所有的这些宏都只使用它们的参数一次 (除了第一个参数,那一定是 Lua 状态), 因此你不需担心这些宏的展开会引起一些副作用。 C 库中所有的 Lua API 函数都不去检查参数是否相容及有效。 然而,你可以在编译 Lua 时加上打开一个宏

4.1 – 栈

Lua 使用一个 虚拟栈 来和 C 互传值。 栈上的的每个元素都是一个 Lua 值 (nil,数字,字符串,等等)。

无论何时 Lua 调用 C,被调用的函数都得到一个新的栈, 这个栈独立于 C 函数本身的栈,也独立于之前的 Lua 栈。 它里面包含了 Lua 传递给 C 函数的所有参数, 而 C 函数则把要返回的结果放入这个栈以返回给调用者 (参见 lua_CFunction)。

方便起见, 所有针对栈的 API 查询操作都不严格遵循栈的操作规则。 而是可以用一个 索引 来指向栈上的任何元素: 正的索引指的是栈上的绝对位置(从1开始); 负的索引则指从栈顶开始的偏移量。 展开来说,如果堆栈有 n 个元素, 那么索引 1 表示第一个元素 (也就是最先被压栈的元素) 而索引 n 则指最后一个元素; 索引 -1 也是指最后一个元素 (即栈顶的元素), 索引 -n 是指第一个元素。


4.2 – 栈大小

当你使用 Lua API 时, 就有责任保证做恰当的调用。 特别需要注意的是, 你有责任控制不要堆栈溢出。 你可以使用 lua_checkstack 这个函数来扩大可用堆栈的尺寸。

无论何时 Lua 调用 C , 它都只保证至少有 LUA_MINSTACK 这么多的堆栈空间可以使用。 LUA_MINSTACK 一般被定义为 20 , 因此,只要你不是不断的把数据压栈, 通常你不用关心堆栈大小。

当你调用一个 Lua 函数却没有指定要接收多少个返回值时 (参见 lua_call), Lua 可以保证栈一定有足够的空间来接收所有的返回值, 但不保证此外留有额外的空间。 因此,在做了一次这样的调用后,如果你需要继续压栈, 则需要使用 lua_checkstack。


4.3 – 有效索引与可接受索引

API 中的函数若需要传入栈索引,这个索引必须是 有效索引 或是 可接受索引。


有效索引 指引用栈内真实位置的索引; 即在 1 到栈顶之间的位置 (1 ≤ abs(index) ≤ top)。 通常,一个可能修改该位置的值的函数需要传入有效索引。

除非另有说明, 任何可以接受有效索引的函数同时也接受 伪索引。 伪索引指代一些可以被 C code 访问得到 Lua 值,而它们又不在栈内。 这用于访问注册表以及 C 函数的上值(参见 §4.4)。

对于那些只是需要栈中的值(例如查询函数) 而不需要指定一个栈位置的函数, 可以用一个可接受的索引去调用它们。 可接受索引 不仅可以是任何包括伪索引在内的有效索引, 还可以是任何超过栈顶但落在为栈分配出来的空间内的正索引。 (注意 0 永远都不是一个可接受索引。) 除非另有说明,API 里的函数都接受可接受索引。

允许可接受索引是为了避免对栈顶以外的查询时做额外的检查。 例如,C 函数可以直接查询传给它的第三个参数, 而不用先检查是不是有第三个参数, 即不需要检查 3 是不是一个有效索引。

对于那些以可接受索引调用的函数, 无效索引被看作包含了一个虚拟类型 LUA_TNONE 的值, 这个值的行为和 nil 一致。


4.4 – C 闭包

当 C 函数被创建出来, 我们有可能会把一些值关联在一起, 也就是创建一个 C 闭包 (参见 lua_pushcclosure); 这些被关联起来的值被叫做 上值 , 它们可以在函数被调用的时候访问的到。

无论何时去调用 C 函数, 函数的上值都可以用伪索引定位。 我们可以用 lua_upvalueindex 这个宏来生成这些伪索引。 第一个关联到函数的值放在 lua_upvalueindex(1) 位置处,依此类推。 使用 lua_upvalueindex(n) 时, 若 n 大于当前函数的总上值个数 (但不可以大于 256)会产生一个可接受的但无效的索引。


4.5 – 注册表

Lua 提供了一个 注册表, 这是一个预定义出来的表, 可以用来保存任何 C 代码想保存的 Lua 值。 这个表可以用有效伪索引 LUA_REGISTRYINDEX 来定位。 任何 C 库都可以在这张表里保存数据, 为了防止冲突,你需要特别小心的选择键名。 一般的用法是,你可以用一个包含你的库名的字符串做为键名, 或者取你自己 C 对象的地址,以轻量用户数据的形式做键, 还可以用你的代码创建出来的任意 Lua 对象做键。 关于变量名,字符串键名中以下划线加大写字母的名字被 Lua 保留。

注册表中的整数键用于引用机制 (参见 luaL_ref), 以及一些预定义的值。 因此,整数键不要用于别的目的。

当你创建了一个新的 Lua 状态机, 其中的注册表内就预定义好了几个值。 这些预定义值可以用整数索引到, 这些整数以常数形式定义在 lua.h 中。 有下列常数:


LUA_RIDX_MAINTHREAD: 注册表中这个索引下是状态机的主线程。 (主线程和状态机同时被创建出来。)

LUA_RIDX_GLOBALS: 注册表的这个索引下是全局环境。

4.6 – C 中的错误处理

在内部实现中,Lua 使用了 C 的 longjmp 机制来处理错误。 (如果你使用 C++ 编译,Lua 将换成异常; 细节请在源代码中搜索 LUAI_THROW。) 当 Lua 碰到任何错误 (比如内存分配错误、类型错误、语法错误、还有运行时错误) 它都会 抛出一个错误出去; 也就是调用一次长跳转。 在 保护环境 下, Lua 使用 setjmp 来设置一个恢复点; 任何发生的错误都会跳转到最近的一个恢复点。


如果错误发生在保护环境之外, Lua 会先调用 panic 函数 (参见 lua_atpanic) 然后调用 abort 来退出宿主程序。 你的 panic 函数只要不返回 (例如:长跳转到你在 Lua 外你自己设置的恢复点) 就可以不退出程序。


panic 函数以错误消息处理器(参见 §2.3)的方式运行; 错误消息在栈顶。 不同的是,它不保证栈空间。 做任何压栈操作前,panic 函数都必须先检查是否有足够的空间 (参见§4.2)。


大多数 API 函数都有可能抛出错误, 例如在内存分配错误时就会抛出。 每个函数的文档都会注明它是否可能抛出错误。


在 C 函数内部,你可以通过调用 lua_error 来抛出错误。

4.7 – C 中的让出处理

Lua 内部使用 C 的 longjmp 机制让出一个协程。 因此,如果一个 C 函数 foo 调用了一个 API 函数, 而这个 API 函数让出了(直接或间接调用了让出函数)。 由于 longjmp 会移除 C 栈的栈帧, Lua 就无法返回到 foo 里了。


为了回避这类问题, 碰到 API 调用中调用让出时,除了那些抛出错误的 API 外,还提供了三个函数: lua_yieldk, lua_callk,和 lua_pcallk 。 它们在让出发生时,可以从传入的 延续函数 (名为 k 的参数)继续运行。


我们需要预设一些术语来解释延续点。 对于从 Lua 中调用的 C 函数,我们称之为 原函数。 从这个原函数中调用的上面所述的三个 C API 函数我们称之为 被调函数。 被调函数可以使当前线程让出。 (让出发生在被调函数是 lua_yieldk, 或传入 lua_callk 或 lua_pcallk 的函数调用了让出时。)


假设正在运行的线程在执行被调函数时让出。 当再次延续这条线程,它希望继续被调函数的运行。 然而,被调函数不可能返回到原函数中。 这是因为之前的让出操作破坏了 C 栈的栈帧。 作为替代品,Lua 调用那个作为被调函数参数给出的 延续函数 。 正如其名,延续函数将延续原函数的任务。


下面的函数会做一个说明:


     int original_function (lua_State *L) {

       ...     /* code 1 */

       status = lua_pcall(L, n, m, h);  /* calls Lua */

       ...     /* code 2 */

     }

现在我们想允许被 lua_pcall 运行的 Lua 代码让出。 首先,我们把函数改写成这个样子:


     int k (lua_State *L, int status, lua_KContext ctx) {

       ...  /* code 2 */

     }

     

     int original_function (lua_State *L) {

       ...     /* code 1 */

       return k(L, lua_pcall(L, n, m, h), ctx);

     }

上面的代码中,新函数 k 就是一个 延续函数 (函数类型为 lua_KFunction)。 它的工作就是原函数中调用 lua_pcall 之后做的那些事情。 现在我们必须通知 Lua 说,你必须在被 lua_pcall 执行的 Lua 代码发生过中断(错误或让出)后, 还得继续调用 k 。 所以我们还得继续改写这段代码,把 lua_pcall 替换成 lua_pcallk:


     int original_function (lua_State *L) {

       ...     /* code 1 */

       return k(L, lua_pcallk(L, n, m, h, ctx2, k), ctx1);

     }

注意这里那个额外的显式的对延续函数的调用: Lua 仅在需要时,这可能是由错误导致的也可能是发生了让出而需要继续运行,才会调用延续函数。 如果没有发生过任何让出,调用的函数正常返回, 那么 lua_pcallk (以及 lua_callk)也会正常返回。 (当然,这个例子中你也可以不在之后调用延续函数, 而是在原函数的调用后直接写上需要做的工作。)


除了 Lua 状态,延续函数还有两个参数: 一个是调用最后的状态码,另一个一开始由 lua_pcallk 传入的上下文 (ctx)。 (Lua 本身不使用这个值;它仅仅从原函数转发这个值给延续函数。) 对于 lua_pcallk 而言, 状态码和 lua_pcallk 本应返回值相同,区别仅在于发生过让出后才执行完时,状态码为 LUA_YIELD(而不是 LUA_OK)。 对于lua_yieldk 和 lua_callk 而言, 调用延续函数传入的状态码一定是 LUA_YIELD。 (对这两个函数,Lua 不会因任何错误而调用延续函数。 因为它们并不处理错误。) 同样,当你使用 lua_callk 时, 你应该用 LUA_OK 作为状态码来调用延续函数。 (对于 lua_yieldk, 几乎没有什么地方需要直接调用延续函数, 因为 lua_yieldk 本身并不会返回。)


Lua 会把延续函数看作原函数。 延续函数将接收到和原函数相同的 Lua 栈,其接收到的 lua 状态也和 被调函数若返回后应该有的状态一致。 (例如, lua_callk 调用之后, 栈中之前压入的函数和调用参数都被调用产生的返回值所替代。) 这时也有相同的上值。 等到它返回的时候,Lua 会将其看待成原函数的返回去操作。


4.8 – 函数和类型

这里按字母次序列出了所有 C API 中的函数和类型。 每个函数都有一个这样的提示:[-o, +p, x]


对于第一个域,o, 指的是该函数会从栈上弹出多少个元素。 第二个域,p, 指该函数会将多少个元素压栈。 (所有函数都会在弹出参数后再把结果压栈。) x|y 这种形式的域表示该函数根据具体情况可能压入(或弹出) x 或 y 个元素; 问号 '?' 表示 我们无法仅通过参数来了解该函数会弹出/压入多少元素 (比如,数量取决于栈上有些什么)。 第三个域,x, 解释了该函数是否会抛出错误: '-' 表示该函数绝对不会抛出错误; 'e' 表示该函数可能抛出错误; 'v' 表示该函数可能抛出有意义的错误。


lua_absindex


[-0, +0, –]


int lua_absindex (lua_State *L, int idx);

将一个可接受的索引 idx 转换为绝对索引 (即,一个不依赖栈顶在哪的值)。


lua_Alloc


typedef void * (*lua_Alloc) (void *ud,

                             void *ptr,

                             size_t osize,

                             size_t nsize);

Lua 状态机中使用的内存分配器函数的类型。 内存分配函数必须提供一个功能类似于 realloc 但又不完全相同的函数。 它的参数有 ud ,一个由 lua_newstate 传给它的指针;ptr ,一个指向已分配出来/将被重新分配/要释放的内存块指针; osize ,内存块原来的尺寸或是关于什么将被分配出来的代码; nsize ,新内存块的尺寸。


如果 ptr 不是 NULL, osize 是 ptr 指向的内存块的尺寸, 即这个内存块当初被分配或重分配的尺寸。


如果 ptr 是 NULL, osize 是 Lua 即将分配对象类型的编码。 当(且仅当)Lua 创建一个对应类型的新对象时, osize 是LUA_TSTRING,LUA_TTABLE,LUA_TFUNCTION, LUA_TUSERDATA,或 LUA_TTHREAD 中的一个。 若 osize 是其它类型,Lua 将为其它东西分配内存。


Lua 假定分配器函数会遵循以下行为:


当 nsize 是零时, 分配器必须和 free 行为类似并返回 NULL。


当 nsize 不是零时, 分配器必须和 realloc 行为类似。 如果分配器无法完成请求,返回 NULL。 Lua 假定在 osize >= nsize 成立的条件下, 分配器绝不会失败。


这里有一个简单的分配器函数的实现。 这个实现被放在补充库中,供 luaL_newstate 使用。


     static void *l_alloc (void *ud, void *ptr, size_t osize,

                                                size_t nsize) {

       (void)ud;  (void)osize;  /* not used */

       if (nsize == 0) {

         free(ptr);

         return NULL;

       }

       else

         return realloc(ptr, nsize);

     }

注意,标准 C 能确保 free(NULL) 没有副作用, 且 realloc(NULL,size) 等价于 malloc(size)。 这段代码假定 realloc 在缩小块长度时不会失败。 (虽然标准 C 没有对此行为做出保证,但这看起来是一个安全的假定。)


lua_arith


[-(2|1), +1, e]


void lua_arith (lua_State *L, int op);

对栈顶的两个值(或者一个,比如取反)做一次数学或位操作。 其中,栈顶的那个值是第二个操作数。 它会弹出压入的值,并把结果放在栈顶。 这个函数遵循 Lua 对应的操作符运算规则 (即有可能触发元方法)。


op 的值必须是下列常量中的一个:


LUA_OPADD: 加法 (+)

LUA_OPSUB: 减法 (-)

LUA_OPMUL: 乘法 (*)

LUA_OPDIV: 浮点除法 (/)

LUA_OPIDIV: 向下取整的除法 (//)

LUA_OPMOD: 取模 (%)

LUA_OPPOW: 乘方 (^)

LUA_OPUNM: 取负 (一元 -)

LUA_OPBNOT: 按位取反 (~)

LUA_OPBAND: 按位与 (&)

LUA_OPBOR: 按位或 (|)

LUA_OPBXOR: 按位异或 (~)

LUA_OPSHL: 左移 (<<)

LUA_OPSHR: 右移 (>>)

lua_atpanic


[-0, +0, –]


lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf);

设置一个新的 panic 函数,并返回之前设置的那个。 (参见 §4.6)。


lua_call


[-(nargs+1), +nresults, e]


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

调用一个函数。


要调用一个函数请遵循以下协议: 首先,要调用的函数应该被压入栈; 接着,把需要传递给这个函数的参数按正序压栈; 这是指第一个参数首先压栈。 最后调用一下 lua_call;nargs 是你压入栈的参数个数。 当函数调用完毕后,所有的参数以及函数本身都会出栈。 而函数的返回值这时则被压栈。 返回值的个数将被调整为 nresults 个, 除非 nresults被设置成 LUA_MULTRET。 在这种情况下,所有的返回值都被压入堆栈中。 Lua 会保证返回值都放入栈空间中。 函数返回值将按正序压栈(第一个返回值首先压栈), 因此在调用结束后,最后一个返回值将被放在栈顶。


被调用函数内发生的错误将(通过 longjmp )一直上抛。


下面的例子中,这行 Lua 代码等价于在宿主程序中用 C 代码做一些工作:


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

这里是 C 里的代码:


     lua_getglobal(L, "f");                  /* function to be called */

     lua_pushliteral(L, "how");                       /* 1st argument */

     lua_getglobal(L, "t");                    /* table to be indexed */

     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 */

     lua_setglobal(L, "a");                         /* set global 'a' */

注意上面这段代码是 平衡 的: 到了最后,堆栈恢复成原有的配置。 这是一种良好的编程习惯。


lua_callk


[-(nargs + 1), +nresults, e]


void lua_callk (lua_State *L,

                int nargs,

                int nresults,

                lua_KContext ctx,

                lua_KFunction k);

这个函数的行为和 lua_call 完全一致,只不过它还允许被调用的函数让出 (参见 §4.7)。


lua_CFunction


typedef int (*lua_CFunction) (lua_State *L);

C 函数的类型。


为了正确的和 Lua 通讯, C 函数必须使用下列协议。 这个协议定义了参数以及返回值传递方法: C 函数通过 Lua 中的栈来接受参数, 参数以正序入栈(第一个参数首先入栈)。 因此,当函数开始的时候, lua_gettop(L) 可以返回函数收到的参数个数。 第一个参数(如果有的话)在索引 1 的地方, 而最后一个参数在索引 lua_gettop(L) 处。 当需要向 Lua 返回值的时候, C 函数只需要把它们以正序压到堆栈上(第一个返回值最先压入), 然后返回这些返回值的个数。 在这些返回值之下的,堆栈上的东西都会被 Lua 丢掉。 和 Lua 函数一样,从 Lua 中调用 C 函数也可以有很多返回值。


下面这个例子中的函数将接收若干数字参数,并返回它们的平均数与和:


     static int foo (lua_State *L) {

       int n = lua_gettop(L);    /* 参数的个数 */

       lua_Number sum = 0.0;

       int i;

       for (i = 1; i <= n; i++) {

         if (!lua_isnumber(L, i)) {

           lua_pushliteral(L, "incorrect argument");

           lua_error(L);

         }

         sum += lua_tonumber(L, i);

       }

       lua_pushnumber(L, sum/n);        /* 第一个返回值 */

       lua_pushnumber(L, sum);         /* 第二个返回值 */

       return 2;                   /* 返回值的个数 */

     }

lua_checkstack


[-0, +0, –]


int lua_checkstack (lua_State *L, int n);

确保堆栈上至少有 n 个额外空位。 如果不能把堆栈扩展到相应的尺寸,函数返回假。 失败的原因包括将把栈扩展到比固定最大尺寸还大 (至少是几千个元素)或分配内存失败。 这个函数永远不会缩小堆栈; 如果堆栈已经比需要的大了,那么就保持原样。


lua_close


[-0, +0, –]


void lua_close (lua_State *L);

销毁指定 Lua 状态机中的所有对象 (如果有垃圾收集相关的元方法的话,会调用它们), 并且释放状态机中使用的所有动态内存。 在一些平台上,你可以不必调用这个函数, 因为当宿主程序结束的时候,所有的资源就自然被释放掉了。 另一方面,长期运行的程序,比如一个后台程序或是一个网站服务器, 会创建出多个 Lua 状态机。那么就应该在不需要时赶紧关闭它们。


lua_compare


[-0, +0, e]


int lua_compare (lua_State *L, int index1, int index2, int op);

比较两个 Lua 值。 当索引 index1 处的值通过 op 和索引 index2 处的值做比较后条件满足,函数返回 1 。 这个函数遵循 Lua 对应的操作规则(即有可能触发元方法)。 反之,函数返回 0。 当任何一个索引无效时,函数也会返回 0 。


op 值必须是下列常量中的一个:


LUA_OPEQ: 相等比较 (==)

LUA_OPLT: 小于比较 (<)

LUA_OPLE: 小于等于比较 (<=)

lua_concat


[-n, +1, e]


void lua_concat (lua_State *L, int n);

连接栈顶的 n 个值, 然后将这些值出栈,并把结果放在栈顶。 如果 n 为 1 ,结果就是那个值放在栈上(即,函数什么都不做); 如果 n 为 0 ,结果是一个空串。 连接依照 Lua 中通常语义完成(参见 §3.4.6 )。


lua_copy


[-0, +0, –]


void lua_copy (lua_State *L, int fromidx, int toidx);

从索引 fromidx 处复制一个值到一个有效索引 toidx 处,覆盖那里的原有值。 不会影响其它位置的值。


lua_createtable


[-0, +1, e]


void lua_createtable (lua_State *L, int narr, int nrec);

创建一张新的空表压栈。 参数 narr 建议了这张表作为序列使用时会有多少个元素; 参数 nrec 建议了这张表可能拥有多少序列之外的元素。 Lua 会使用这些建议来预分配这张新表。 如果你知道这张表用途的更多信息,预分配可以提高性能。 否则,你可以使用函数 lua_newtable 。


lua_dump


[-0, +0, e]


int lua_dump (lua_State *L,

                        lua_Writer writer,

                        void *data,

                        int strip);

把函数导出成二进制代码块 。 函数接收栈顶的 Lua 函数做参数, 然后生成它的二进制代码块。 若被导出的东西被再次加载, 加载的结果就相当于原来的函数。 当它在产生代码块的时候, lua_dump 通过调用函数 writer (参见 lua_Writer ) 来写入数据,后面的 data 参数会被传入 writer 。


如果 strip 为真, 二进制代码块将不包含该函数的调试信息。


最后一次由 writer 的返回值将作为这个函数的返回值返回; 0 表示没有错误。


该函数不会把 Lua 函数弹出堆栈。


lua_error


[-1, +0, v]


int lua_error (lua_State *L);

以栈顶的值作为错误对象,抛出一个 Lua 错误。 这个函数将做一次长跳转,所以一定不会返回 (参见 luaL_error)。


lua_gc


[-0, +0, e]


int lua_gc (lua_State *L, int what, int data);

控制垃圾收集器。


这个函数根据其参数 what 发起几种不同的任务:


LUA_GCSTOP: 停止垃圾收集器。

LUA_GCRESTART: 重启垃圾收集器。

LUA_GCCOLLECT: 发起一次完整的垃圾收集循环。

LUA_GCCOUNT: 返回 Lua 使用的内存总量(以 K 字节为单位)。

LUA_GCCOUNTB: 返回当前内存使用量除以 1024 的余数。

LUA_GCSTEP: 发起一步增量垃圾收集。

LUA_GCSETPAUSE: 把 data 设为 垃圾收集器间歇率 (参见 §2.5),并返回之前设置的值。

LUA_GCSETSTEPMUL: 把 data 设为 垃圾收集器步进倍率 (参见 §2.5),并返回之前设置的值。

LUA_GCISRUNNING: 返回收集器是否在运行(即没有停止)。

关于这些选项的细节,参见 collectgarbage 。


lua_getallocf


[-0, +0, –]


lua_Alloc lua_getallocf (lua_State *L, void **ud);

返回给定状态机的内存分配器函数。 如果 ud 不是 NULL , Lua 把设置内存分配函数时设置的那个指针置入 *ud 。


lua_getfield


[-0, +1, e]


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

把 t[k] 的值压栈, 这里的 t 是索引指向的值。 在 Lua 中,这个函数可能触发对应 "index" 事件对应的元方法 (参见 §2.4 )。


函数将返回压入值的类型。


lua_getextraspace


[-0, +0, –]


void *lua_getextraspace (lua_State *L);

返回一个 Lua 状态机中关联的内存块指针。 程序可以把这块内存用于任何用途;而 Lua 不会使用它。


每一个新线程都会携带一块内存, 初始化为主线程的这块内存的副本。


默认配置下,这块内存的大小为空指针的大小。 不过你可以重新编译 Lua 设定这块内存不同的大小。 (参见 luaconf.h 中的 LUA_EXTRASPACE。)


lua_getglobal


[-0, +1, e]


int lua_getglobal (lua_State *L, const char *name);

把全局变量 name 里的值压栈,返回该值的类型。


lua_geti


[-0, +1, e]


int lua_geti (lua_State *L, int index, lua_Integer i);

把 t[i] 的值压栈, 这里的 t 指给定的索引指代的值。 和在 Lua 里一样,这个函数可能会触发 "index" 事件的元方法 (参见 §2.4)。


返回压入值的类型。


lua_getmetatable


[-0, +(0|1), –]


int lua_getmetatable (lua_State *L, int index);

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


lua_gettable


[-1, +1, e]


int lua_gettable (lua_State *L, int index);

把 t[k] 的值压栈, 这里的 t 是指索引指向的值, 而 k 则是栈顶放的值。


这个函数会弹出堆栈上的键,把结果放在栈上相同位置。 和在 Lua 中一样, 这个函数可能触发对应 "index" 事件的元方法 (参见 §2.4 )。


返回压入值的类型。


lua_gettop


[-0, +0, –]


int lua_gettop (lua_State *L);

返回栈顶元素的索引。 因为索引是从 1 开始编号的, 所以这个结果等于栈上的元素个数; 特别指出,0 表示栈为空。


lua_getuservalue


[-0, +1, –]


int lua_getuservalue (lua_State *L, int index);

将给定索引处的用户数据所关联的 Lua 值压栈。


返回压入值的类型。


lua_insert


[-1, +1, –]


void lua_insert (lua_State *L, int index);

把栈顶元素移动到指定的有效索引处, 依次移动这个索引之上的元素。 不要用伪索引来调用这个函数, 因为伪索引没有真正指向栈上的位置。


lua_Integer


typedef ... lua_Integer;

Lua 中的整数类型。


缺省时,这个就是 long long, (通常是一个 64 位以二为补码的整数), 也可以修改它为 long 或 int (通常是一个 32 位以二为补码的整数)。 (参见 luaconf.h 中的LUA_INT 。)


Lua 定义了两个常量: LUA_MININTEGER 和 LUA_MAXINTEGER 来表示这个类型可以表示的最小和最大值。


lua_isboolean


[-0, +0, –]


int lua_isboolean (lua_State *L, int index);

当给定索引的值是一个布尔量时,返回 1 ,否则返回 0 。


lua_iscfunction


[-0, +0, –]


int lua_iscfunction (lua_State *L, int index);

当给定索引的值是一个 C 函数时,返回 1 ,否则返回 0 。


lua_isfunction


[-0, +0, –]


int lua_isfunction (lua_State *L, int index);

当给定索引的值是一个函数( C 或 Lua 函数均可)时,返回 1 ,否则返回 0 。


lua_isinteger


[-0, +0, –]


int lua_isinteger (lua_State *L, int index);

当给定索引的值是一个整数 (其值是一个数字,且内部以整数储存), 时,返回 1 ,否则返回 0 。


lua_islightuserdata


[-0, +0, –]


int lua_islightuserdata (lua_State *L, int index);

当给定索引的值是一个轻量用户数据时,返回 1 ,否则返回 0 。


lua_isnil


[-0, +0, –]


int lua_isnil (lua_State *L, int index);

当给定索引的值是 nil 时,返回 1 ,否则返回 0 。


lua_isnone


[-0, +0, –]


int lua_isnone (lua_State *L, int index);

当给定索引无效时,返回 1 ,否则返回 0 。


lua_isnoneornil


[-0, +0, –]


int lua_isnoneornil (lua_State *L, int index);

当给定索引无效或其值是 nil 时, 返回 1 ,否则返回 0 。


lua_isnumber


[-0, +0, –]


int lua_isnumber (lua_State *L, int index);

当给定索引的值是一个数字,或是一个可转换为数字的字符串时,返回 1 ,否则返回 0 。


lua_isstring


[-0, +0, –]


int lua_isstring (lua_State *L, int index);

当给定索引的值是一个字符串或是一个数字 (数字总能转换成字符串)时,返回 1 ,否则返回 0 。


lua_istable


[-0, +0, –]


int lua_istable (lua_State *L, int index);

当给定索引的值是一张表时,返回 1 ,否则返回 0 。


lua_isthread


[-0, +0, –]


int lua_isthread (lua_State *L, int index);

当给定索引的值是一条线程时,返回 1 ,否则返回 0 。


lua_isuserdata


[-0, +0, –]


int lua_isuserdata (lua_State *L, int index);

当给定索引的值是一个用户数据(无论是完全的还是轻量的)时, 返回 1 ,否则返回 0 。


lua_isyieldable


[-0, +0, –]


int lua_isyieldable (lua_State *L);

如果给定的协程可以让出,返回 1 ,否则返回 0 。


lua_KContext


typedef ... lua_KContext;

延续函数上下文参数的类型。 这一定是一个数字类型。 当有 intptr_t 时,被定义为 intptr_t , 因此它也可以保存指针。 否则,它被定义为 ptrdiff_t。


lua_KFunction


typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx);

延续函数的类型(参见 §4.7 )。


lua_len


[-0, +1, e]


void lua_len (lua_State *L, int index);

返回给定索引的值的长度。 它等价于 Lua 中的 '#' 操作符 (参见 §3.4.7)。 它有可能触发 "length" 事件对应的元方法 (参见 §2.4 )。 结果压栈。


lua_load


[-0, +1, –]


int lua_load (lua_State *L,

              lua_Reader reader,

              void *data,

              const char *chunkname,

              const char *mode);

加载一段 Lua 代码块,但不运行它。 如果没有错误, lua_load 把一个编译好的代码块作为一个 Lua 函数压到栈顶。 否则,压入错误消息。


lua_load 的返回值可以是:


LUA_OK: 没有错误;

LUA_ERRSYNTAX: 在预编译时碰到语法错误;

LUA_ERRMEM: 内存分配错误;

LUA_ERRGCMM: 在运行 __gc 元方法时出错了。 (这个错误和代码块加载过程无关,它是由垃圾收集器引发的。)

lua_load 函数使用一个用户提供的 reader 函数来读取代码块(参见 lua_Reader )。 data 参数会被传入 reader 函数。


chunkname 这个参数可以赋予代码块一个名字, 这个名字被用于出错信息和调试信息(参见 §4.9)。


lua_load 会自动检测代码块是文本的还是二进制的, 然后做对应的加载操作(参见程序 luac )。 字符串 mode 的作用和函数 load 一致。 它还可以是 NULL 等价于字符串 "bt"。


lua_load 的内部会使用栈, 因此 reader 函数必须永远在每次返回时保留栈的原样。


如果返回的函数有上值, 第一个上值会被设置为 保存在注册表(参见 §4.5) LUA_RIDX_GLOBALS 索引处的全局环境。 在加载主代码块时,这个上值是 _ENV 变量(参见§2.2)。 其它上值均被初始化为 nil。


lua_newstate


[-0, +0, –]


lua_State *lua_newstate (lua_Alloc f, void *ud);

创建一个运行在新的独立的状态机中的线程。 如果无法创建线程或状态机(由于内存有限)则返回 NULL。 参数 f 是一个分配器函数; Lua 将通过这个函数做状态机内所有的内存分配操作。 第二个参数 ud ,这个指针将在每次调用分配器时被转入。


lua_newtable


[-0, +1, e]


void lua_newtable (lua_State *L);

创建一张空表,并将其压栈。 它等价于 lua_createtable(L, 0, 0) 。


lua_newthread


[-0, +1, e]


lua_State *lua_newthread (lua_State *L);

创建一条新线程,并将其压栈, 并返回维护这个线程的 lua_State 指针。 这个函数返回的新线程共享原线程的全局环境, 但是它有独立的运行栈。


没有显式的函数可以用来关闭或销毁掉一个线程。 线程跟其它 Lua 对象一样是垃圾收集的条目之一。


lua_newuserdata


[-0, +1, e]


void *lua_newuserdata (lua_State *L, size_t size);

这个函数分配一块指定大小的内存块, 把内存块地址作为一个完全用户数据压栈, 并返回这个地址。 宿主程序可以随意使用这块内存。


lua_next


[-1, +(2|0), e]


int lua_next (lua_State *L, int index);

从栈顶弹出一个键, 然后把索引指定的表中的一个键值对压栈 (弹出的键之后的 “下一” 对)。 如果表中以无更多元素, 那么 lua_next 将返回 0 (什么也不压栈)。


典型的遍历方法是这样的:


     /*  表放在索引 't' 处 */

     lua_pushnil(L);  /* 第一个键 */

     while (lua_next(L, t) != 0) {

       /* 使用 '键' (在索引 -2 处) 和 '值' (在索引 -1 处)*/

       printf("%s - %s\n",

              lua_typename(L, lua_type(L, -2)),

              lua_typename(L, lua_type(L, -1)));

       /* 移除 '值' ;保留 '键' 做下一次迭代 */

       lua_pop(L, 1);

     }

在遍历一张表的时候, 不要直接对键调用 lua_tolstring , 除非你知道这个键一定是一个字符串。 调用 lua_tolstring 有可能改变给定索引位置的值; 这会对下一次调用lua_next 造成影响。


关于迭代过程中修改被迭代的表的注意事项参见 next 函数。


lua_Number


typedef double lua_Number;

Lua 中浮点数的类型。


Lua 中数字的类型。缺省是 double ,但是你可以改成 float 。 (参见 luaconf.h 中的 LUA_REAL 。)


lua_numbertointeger


int lua_numbertointeger (lua_Number n, lua_Integer *p);

将一个 Lua 浮点数转换为一个 Lua 整数。 这个宏假设 n 有对应的整数值。 如果该值在 Lua 整数可表示范围内, 就将其转换为一个整数赋给 *p。 宏的结果是一个布尔量,表示转换是否成功。 (注意、由于圆整关系,这个范围测试不用此宏很难做对。)


该宏有可能对其参数做多次取值。


lua_pcall


[-(nargs + 1), +(nresults|1), –]


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

以保护模式调用一个函数。


nargs 和 nresults 的含义与 lua_call 中的相同。 如果在调用过程中没有发生错误, lua_pcall 的行为和 lua_call 完全一致。 但是,如果有错误发生的话, lua_pcall 会捕获它, 然后把唯一的值(错误消息)压栈,然后返回错误码。 同 lua_call 一样, lua_pcall 总是把函数本身和它的参数从栈上移除。


如果 msgh 是 0 , 返回在栈顶的错误消息就和原始错误消息完全一致。 否则, msgh 就被当成是 错误处理函数 在栈上的索引位置。 (在当前的实现里,这个索引不能是伪索引。) 在发生运行时错误时, 这个函数会被调用而参数就是错误消息。 错误处理函数的返回值将被 lua_pcall 作为错误消息返回在堆栈上。


典型的用法中,错误处理函数被用来给错误消息加上更多的调试信息, 比如栈跟踪信息。 这些信息在 lua_pcall 返回后, 由于栈已经展开,所以收集不到了。


lua_pcall 函数会返回下列常数 (定义在 lua.h 内)中的一个:


LUA_OK (0): 成功。

LUA_ERRRUN: 运行时错误。

LUA_ERRMEM: 内存分配错误。对于这种错,Lua 不会调用错误处理函数。

LUA_ERRERR: 在运行错误处理函数时发生的错误。

LUA_ERRGCMM: 在运行 __gc 元方法时发生的错误。 (这个错误和被调用的函数无关。)

lua_pcallk


[-(nargs + 1), +(nresults|1), –]


int lua_pcallk (lua_State *L,

                int nargs,

                int nresults,

                int msgh,

                lua_KContext ctx,

                lua_KFunction k);

这个函数的行为和 lua_pcall 完全一致,只不过它还允许被调用的函数让出 (参见 §4.7)。


lua_pop


[-n, +0, –]


void lua_pop (lua_State *L, int n);

从栈中弹出 n 个元素。


lua_pushboolean


[-0, +1, –]


void lua_pushboolean (lua_State *L, int b);

把 b 作为一个布尔量压栈。


lua_pushcclosure


[-n, +1, e]


void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);

把一个新的 C 闭包压栈。


当创建了一个 C 函数后, 你可以给它关联一些值, 这就是在创建一个 C 闭包(参见 §4.4); 接下来无论函数何时被调用,这些值都可以被这个函数访问到。 为了将一些值关联到一个 C 函数上, 首先这些值需要先被压入堆栈(如果有多个值,第一个先压)。 接下来调用 lua_pushcclosure 来创建出闭包并把这个 C 函数压到栈上。 参数 n 告之函数有多少个值需要关联到函数上。 lua_pushcclosure 也会把这些值从栈上弹出。


n 的最大值是 255 。


当 n 为零时, 这个函数将创建出一个 轻量 C 函数, 它就是一个指向 C 函数的指针。 这种情况下,不可能抛出内存错误。


lua_pushcfunction


[-0, +1, –]


void lua_pushcfunction (lua_State *L, lua_CFunction f);

将一个 C 函数压栈。 这个函数接收一个 C 函数指针, 并将一个类型为 function 的 Lua 值压栈。 当这个栈顶的值被调用时,将触发对应的 C 函数。


注册到 Lua 中的任何函数都必须遵循正确的协议来接收参数和返回值 (参见 lua_CFunction )。


lua_pushcfunction 是作为一个宏定义出现的:


     #define lua_pushcfunction(L,f)  lua_pushcclosure(L,f,0)

lua_pushfstring


[-0, +1, e]


const char *lua_pushfstring (lua_State *L, const char *fmt, ...);

把一个格式化过的字符串压栈, 然后返回这个字符串的指针。 它和 C 函数 sprintf 比较像, 不过有一些重要的区别:


你不需要为结果分配空间: 其结果是一个 Lua 字符串,由 Lua 来关心其内存分配 (同时通过垃圾收集来释放内存)。

这个转换非常的受限。 不支持符号、宽度、精度。 转换符只支持 '%%' (插入一个字符 '%'), '%s' (插入一个带零终止符的字符串,没有长度限制), '%f' (插入一个lua_Number), '%L' (插入一个 lua_Integer), '%p' (插入一个指针或是一个十六进制数), '%d' (插入一个 int), '%c' (插入一个用 int 表示的单字节字符),以及 '%U' (插入一个用 long int 表示的 UTF-8 字)。

lua_pushglobaltable


[-0, +1, –]


void lua_pushglobaltable (lua_State *L);

将全局环境压栈。


lua_pushinteger


[-0, +1, –]


void lua_pushinteger (lua_State *L, lua_Integer n);

把值为 n 的整数压栈。


lua_pushlightuserdata


[-0, +1, –]


void lua_pushlightuserdata (lua_State *L, void *p);

把一个轻量用户数据压栈。


用户数据是保留在 Lua 中的 C 值。 轻量用户数据 表示一个指针 void*。 它是一个像数字一样的值: 你不需要专门创建它,它也没有独立的元表,而且也不会被收集(因为从来不需要创建)。 只要表示的 C 地址相同,两个轻量用户数据就相等。


lua_pushliteral


[-0, +1, e]


const char *lua_pushliteral (lua_State *L, const char *s);

这个宏等价于 lua_pushlstring, 区别仅在于只能在 s 是一个字面量时才能用它。 它会自动给出字符串的长度。


lua_pushlstring


[-0, +1, e]


const char *lua_pushlstring (lua_State *L, const char *s, size_t len);

把指针 s 指向的长度为 len 的字符串压栈。 Lua 对这个字符串做一个内部副本(或是复用一个副本), 因此 s 处的内存在函数返回后,可以释放掉或是立刻重用于其它用途。 字符串内可以是任意二进制数据,包括零字符。


返回内部副本的指针。


lua_pushnil


[-0, +1, –]


void lua_pushnil (lua_State *L);

将空值压栈。


lua_pushnumber


[-0, +1, –]


void lua_pushnumber (lua_State *L, lua_Number n);

把一个值为 n 的浮点数压栈。


lua_pushstring


[-0, +1, e]


const char *lua_pushstring (lua_State *L, const char *s);

将指针 s 指向的零结尾的字符串压栈。 因此 s 处的内存在函数返回后,可以释放掉或是立刻重用于其它用途。


返回内部副本的指针。


如果 s 为 NULL,将 nil 压栈并返回 NULL。


lua_pushthread


[-0, +1, –]


int lua_pushthread (lua_State *L);

把 L 表示的线程压栈。 如果这个线程是当前状态机的主线程的话,返回 1 。


lua_pushvalue


[-0, +1, –]


void lua_pushvalue (lua_State *L, int index);

把栈上给定索引处的元素作一个副本压栈。


lua_pushvfstring


[-0, +1, e]


const char *lua_pushvfstring (lua_State *L,

                              const char *fmt,

                              va_list argp);

等价于 lua_pushfstring , 不过是用 va_list 接收参数,而不是用可变数量的实际参数。


lua_rawequal


[-0, +0, –]


int lua_rawequal (lua_State *L, int index1, int index2);

如果索引 index1 与索引 index2 处的值 本身相等(即不调用元方法),返回 1 。 否则返回 0 。 当任何一个索引无效时,也返回 0 。


lua_rawget


[-1, +1, –]


int lua_rawget (lua_State *L, int index);

类似于 lua_gettable , 但是作一次直接访问(不触发元方法)。


lua_rawgeti


[-0, +1, –]


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

把 t[n] 的值压栈, 这里的 t 是指给定索引处的表。 这是一次直接访问;就是说,它不会触发元方法。


返回入栈值的类型。


lua_rawgetp


[-0, +1, –]


int lua_rawgetp (lua_State *L, int index, const void *p);

把 t[k] 的值压栈, 这里的 t 是指给定索引处的表, k 是指针 p 对应的轻量用户数据。 这是一次直接访问;就是说,它不会触发元方法。


返回入栈值的类型。


lua_rawlen


[-0, +0, –]


size_t lua_rawlen (lua_State *L, int index);

返回给定索引处值的固有“长度”: 对于字符串,它指字符串的长度; 对于表;它指不触发元方法的情况下取长度操作('#')应得到的值; 对于用户数据,它指为该用户数据分配的内存块的大小; 对于其它值,它为 0 。


lua_rawset


[-2, +0, e]


void lua_rawset (lua_State *L, int index);

类似于 lua_settable , 但是是做一次直接赋值(不触发元方法)。


lua_rawseti


[-1, +0, e]


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

等价于 t[i] = v , 这里的 t 是指给定索引处的表, 而 v 是栈顶的值。


这个函数会将值弹出栈。 赋值是直接的;即不会触发元方法。


lua_rawsetp


[-1, +0, e]


void lua_rawsetp (lua_State *L, int index, const void *p);

等价于 t[k] = v , 这里的 t 是指给定索引处的表, k 是指针 p 对应的轻量用户数据。 而 v 是栈顶的值。


这个函数会将值弹出栈。 赋值是直接的;即不会触发元方法。


lua_Reader


typedef const char * (*lua_Reader) (lua_State *L,

                                    void *data,

                                    size_t *size);

lua_load 用到的读取器函数, 每次它需要一块新的代码块的时候, lua_load 就调用读取器, 每次都会传入一个参数 data 。 读取器需要返回含有新的代码块的一块内存的指针, 并把 size 设为这块内存的大小。 内存块必须在下一次函数被调用之前一直存在。 读取器可以通过返回 NULL 或设 size 为 0 来指示代码块结束。 读取器可能返回多个块,每个块可以有任意的大于零的尺寸。


lua_register


[-0, +0, e]


void lua_register (lua_State *L, const char *name, lua_CFunction f);

把 C 函数 f 设到全局变量 name 中。 它通过一个宏定义:


     #define lua_register(L,n,f) \

            (lua_pushcfunction(L, f), lua_setglobal(L, n))

lua_remove


[-1, +0, –]


void lua_remove (lua_State *L, int index);

从给定有效索引处移除一个元素, 把这个索引之上的所有元素移下来填补上这个空隙。 不能用伪索引来调用这个函数,因为伪索引并不指向真实的栈上的位置。


lua_replace


[-1, +0, –]


void lua_replace (lua_State *L, int index);

把栈顶元素放置到给定位置而不移动其它元素 (因此覆盖了那个位置处的值),然后将栈顶元素弹出。


lua_resume


[-?, +?, –]


int lua_resume (lua_State *L, lua_State *from, int nargs);

在给定线程中启动或延续一条协程 。


要启动一个协程的话, 你需要把主函数以及它需要的参数压入线程栈; 然后调用 lua_resume , 把 nargs 设为参数的个数。 这次调用会在协程挂起时或是结束运行后返回。 当函数返回时,堆栈中会有传给 lua_yield 的所有值, 或是主函数的所有返回值。 当协程让出, lua_resume 返回 LUA_YIELD , 若协程结束运行且没有任何错误时,返回 0 。 如果有错则返回错误代码(参见 lua_pcall )。


在发生错误的情况下, 堆栈没有展开, 因此你可以使用调试 API 来处理它。 错误消息放在栈顶在。


要延续一个协程, 你需要清除上次 lua_yield 遗留下的所有结果, 你把需要传给 yield 作结果的值压栈, 然后调用 lua_resume 。


参数 from 表示协程从哪个协程中来延续 L 的。 如果不存在这样一个协程,这个参数可以是 NULL 。


lua_rotate


[-0, +0, –]


void lua_rotate (lua_State *L, int idx, int n);

把从 idx 开始到栈顶的元素轮转 n 个位置。 对于 n 为正数时,轮转方向是向栈顶的; 当 n 为负数时,向栈底方向轮转 -n 个位置。 n 的绝对值不可以比参于轮转的切片长度大。


lua_setallocf


[-0, +0, –]


void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);

把指定状态机的分配器函数换成带上用户数据 ud 的 f 。


lua_setfield


[-1, +0, e]


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

做一个等价于 t[k] = v 的操作, 这里 t 是给出的索引处的值, 而 v 是栈顶的那个值。


这个函数将把这个值弹出栈。 跟在 Lua 中一样,这个函数可能触发一个 "newindex" 事件的元方法 (参见 §2.4)。


lua_setglobal


[-1, +0, e]


void lua_setglobal (lua_State *L, const char *name);

从堆栈上弹出一个值,并将其设为全局变量 name 的新值。


lua_seti


[-1, +0, e]


void lua_seti (lua_State *L, int index, lua_Integer n);

做一个等价于 t[n] = v 的操作, 这里 t 是给出的索引处的值, 而 v 是栈顶的那个值。


这个函数将把这个值弹出栈。 跟在 Lua 中一样,这个函数可能触发一个 "newindex" 事件的元方法 (参见 §2.4)。


lua_setmetatable


[-1, +0, –]


void lua_setmetatable (lua_State *L, int index);

把一张表弹出栈,并将其设为给定索引处的值的元表。


lua_settable


[-2, +0, e]


void lua_settable (lua_State *L, int index);

做一个等价于 t[k] = v 的操作, 这里 t 是给出的索引处的值, v 是栈顶的那个值, k 是栈顶之下的值。


这个函数会将键和值都