A闪的 BLOG 技术与人文
云风几年前译的Lua 5.1文档,这里直接转载过来,方便记录。原地址
这个部分描述了 Lua 的 C API , 也就是宿主程序跟 Lua 通讯用的一组 C 函数。 所有的 API 函数按相关的类型以及常量都声明在头文件 lua.h 中。
虽然我们说的是“函数”,但一部分简单的 API 是以宏的形式提供的。 所有的这些宏都只使用它们的参数一次 (除了第一个参数,也就是 lua 状态机), 因此你不需担心这些宏的展开会引起一些副作用。
在所有的 C 库中,Lua API 函数都不去检查参数的有效性和坚固性。 然而,你可以在编译 Lua 时加上打开一个宏开关来 开启 luaconf.h 文件中的宏 luai_apicheck 以改变这个行为。
Lua 使用一个虚拟栈来和 C 传递值。 栈上的的每个元素都是一个 Lua 值 (nil,数字,字符串,等等)。
无论何时 Lua 调用 C,被调用的函数都得到一个新的栈, 这个栈独立于 C 函数本身的堆栈,也独立于以前的栈。 (译注:在 C 函数里,用 Lua API 不能访问到 Lua 状态机中本次调用之外的堆栈中的数据) 它里面包含了 Lua 传递给 C 函数的所有参数, 而 C 函数则把要返回的结果也放入堆栈以返回给调用者 (参见 lua_CFunction)。
方便起见,所有针对栈的 API 查询操作都不严格遵循栈的操作规则。 而是可以用一个索引来指向栈上的任何元素: 正的索引指的是栈上的绝对位置(从一开始); 负的索引则指从栈顶开始的偏移量。 更详细的说明一下,如果堆栈有 n 个元素, 那么索引 1 表示第一个元素(也就是最先被压入堆栈的元素) 而索引 n 则指最后一个元素; 索引 -1 也是指最后一个元素(即栈顶的元素), 索引 -n 是指第一个元素。 如果索引在 1 到栈顶之间(也就是,1 ≤ abs(index) ≤ top) 我们就说这是个有效的索引。
当你使用 Lua API 时,就有责任保证其坚固性。 特别需要注意的是,你有责任控制不要堆栈溢出。 你可以使用 lua_checkstack 这个函数来扩大可用堆栈的尺寸。
无论何时 Lua 调用 C , 它都只保证 LUA_MINSTACK 这么多的堆栈空间可以使用。 LUA_MINSTACK 一般被定义为 20 , 因此,只要你不是不断的把数据压栈,通常你不用关心堆栈大小。
所有的查询函数都可以接收一个索引,只要这个索引是任何栈提供的空间中的值。 栈能提供的最大空间是通过 lua_checkstack 来设置的。 这些索引被称作可接受的索引,通常我们把它定义为:
(index < 0 && abs(index) <= top) ||
(index > 0 && index <= stackspace)
注意,0 永远都不是一个可接受的索引。(译注:下文中凡提到的索引,没有特别注明的话,都指可接受的索引。)
除了特别声明外,任何一个函数都可以接受另一种有效的索引,它们被称作“伪索引”。 这个可以帮助 C 代码访问一些并不在栈上的 Lua 值。 伪索引被用来访问线程的环境,函数的环境,注册表,还有 C 函数的 upvalue (参见 §3.4)。
线程的环境(也就是全局变量放的地方)通常在伪索引 LUA_GLOBALSINDEX 处。 正在运行的 C 函数的环境则放在伪索引 LUA_ENVIRONINDEX 之处。
你可以用常规的 table 操作来访问和改变全局变量的值,只需要指定环境表的位置。 举例而言,要访问全局变量的值,这样做:
lua_getfield(L, LUA_GLOBALSINDEX, varname);
当 C 函数被创建出来,我们有可能会把一些值关联在一起, 也就是创建一个 C closure ; 这些被关联起来的值被叫做 upvalue , 它们可以在函数被调用的时候访问的到。 (参见 lua_pushcclosure)。
无论何时去调用 C 函数,函数的 upvalue 都被放在指定的伪索引处。 我们可以用 lua_upvalueindex 这个宏来生成这些伪索引。 第一个关联到函数的值放在 lua_upvalueindex(1) 位置处,依次类推。 任何情况下都可以用 lua_upvalueindex(n) 产生一个 upvalue 的索引, 即使 n 大于实际的 upvalue 数量也可以。它都可以产生一个可接受但不一定有效的索引。
Lua 提供了一个注册表,这是一个预定义出来的表,可以用来保存任何 C 代码想保存的 Lua 值。 这个表可以用伪索引 LUA_REGISTRYINDEX 来定位。 任何 C 库都可以在这张表里保存数据,为了防止冲突,你需要特别小心的选择键名。 一般的用法是,你可以用一个包含你的库名的字符串做为键名,或者可以取你自己 C 代码 中的一个地址,以 light userdata 的形式做键。
注册表里的整数健被用于补充库中实现的引用系统的工作,一般说来不要把它们用于别的用途。
在内部实现中,Lua 使用了 C 的 longjmp 机制来处理错误。 (如果你使用 C++ 的话,也可以选择换用异常;参见 luaconf.h 文件。) 当 Lua 碰到任何错误(比如内存分配错误、类型错误、语法错误、还有一些运行时错误) 它都会产生一个错误出去; 也就是调用一个 long jump 。 在保护环境下,Lua 使用 setjmp 来设置一个恢复点; 任何发生的错误都会激活最近的一个恢复点。
几乎所有的 API 函数都可能产生错误,例如内存分配错误。 但下面的一些函数运行在保护环境中(也就是说它们创建了一个保护环境再在其中运行), 因此它们不会产生错误出来: lua_newstate, lua_close, lua_load, lua_pcall, and lua_cpcall。
在 C 函数里,你也可以通过调用 lua_error 产生一个错误。