(接上篇)
-------------------
5.8 执行 Lua 代码
-------------------
一个宿主程序能够执行写在文件中或在字符串中的 Lua 块,使用下面的函数:
数组
int lua_dofile (lua_State *L, const char *filename); int lua_dostring (lua_State *L, const char *string); int lua_dobuffer (lua_State *L, const char *buff, size_t size, const char *name);
这些函数返回 0 表示成功,或者失败时返回一个错误码:
> LUA_ERRRUN - 执行模块时出错。
> LUA_ERRSYNTAX - 预编译时语法错误。
> LUA_ERRMEM - 内存分配错误。对于此类错误,Lua 不会调用 _ERRORMESSAGE (参见 4.7 节)。
> LUA_ERRERR - 执行 _ERRORMESSAGE 时出错。对于此类错误,Lua 不会再次调用 _ERRORMESSAGE ,以避免死循环。
> LUA_ERRFILE - 打开文件错误(只针对 lua_dofile)。在这种状况下,你可能想要检查 errno,调用 strerror,或者调用 perror 去告诉用户哪里出了问题。
这些常量定义在 lua.h 中。
当经过 NULL 参数调用时,lua_dofile 执行标准输入流。lua_dofile 和 lua_dobuffer 均可以执行预编译的块。它们自动检测出块是文本或者二进制,并相应地加载它(参见程序 luac)。lua_dostring 只执行文本格式的源代码。
lua_dobuffer 的第三个参数是块的名字,它用在错误信息和调试信息中。若是名字是 NULL,Lua 会给块一个默认的名字。
这些函数把块最终返回的结果压栈。一个块能够返回任意数量的值;Lua 保证这些值能够存放到栈空间中,可是调用以后就须要你本身维护栈空间了。若是你须要压栈其它的元素在调用这些函数以后,而且你想安全地使用栈空间,你必需要么经过 lua_stackspace 来检查栈空间,要求从栈同移除元素(若是你不须要它们的话)。例如,下面的代码加载一个文件中的块而且丢弃全部的这个块返回的结果,把栈恢复为调用之间的状态:
安全
{ int oldtop = lua_gettop(L); lua_dofile(L, filename); lua_settop(L, oldtop); }
-------------------
5.9 操纵 Lua 中的全局变量
-------------------
可使用下面的函数读取 Lua 全局变量的值:
闭包
void lua_getglobal (lua_State *L, const char *varname);
它将会把给定变量的值压栈。在 Lua 中,这个函数可能触发一个标签方法 ``getglobal''(参见 4.8 节)。为了读取全局变量真正的值,而不唤起任何标签方法,在全局表上使用 lua_rawget(参见下面)。
保存一个值到全局变量,调用
函数
void lua_setglobal (lua_State *L, const char *varname);
它出栈须要保存到给定变量的值。在 Lua 中,这个函数可能会触发一个标签方法 ``setglobal'' (参见 4.8 节)。为了设置全局变量的真正的值,而不唤起任何标签方法,在全局表上使用 lua_rawset (参见下面)。
全部的全局变量都保存在一个普通的表中。你能够得到这个表经过调用
lua
void lua_getglobals (lua_State *L);
它把当前的全局变量的表压栈。为设置其它的表做为全局变量的表,调用
spa
void lua_setglobals (lua_State *L);
将要使用的表出栈。
-------------------
5.10 操纵 Lua 中的表
-------------------
Lua 表也能够经过 API 来操做。
为读取表中的值,表必须在栈的某个地方。在这种状况下,调用
指针
void lua_gettable (lua_State *L, int index);
这里 index 指定表。lua_gettable 出栈一个 key,而且返回(在栈上)表在 key 处的内容。在 Lua 中,这个操做可能会触发一个标签方法 ``gettable'' 。为了得到表的 key 的真正值,而不唤起任何标签方法,使用 raw 版本的:
调试
void lua_rawget (lua_State *L, int index);
为保存一个值到表中,表已经在栈的某个地方了,你压栈 key 和值(以这种顺序),而后调用
code
void lua_settable (lua_State *L, int index);
这里的 index 指定表。lua_settable 出栈 key 和 value。在 Lua 中,这个操做可能会触发一个标签方法 ``settable'' 。为了设置任意表的 index 的真正值,而不唤起任何标签方法,使用 raw 版本的:
对象
void lua_rawset (lua_State *L, int index);
最后,函数
void lua_newtable (lua_State *L);
建立一个新的空表并压栈。
-------------------
5.11 把表做为数组使用
-------------------
有 API 函数可使用 Lua 表做为数组,也就是说,表的索引都有数值:
void lua_rawgeti (lua_State *L, int index, int n); void lua_rawseti (lua_State *L, int index, int n); int lua_getn (lua_State *L, int index);
lua_rawgeti 得到在栈的 index 处表的第 n 个元素。
lua_rawseti 设置在栈的 index 处表的第 n 个元素为栈顶的值。
lua_getn 返回在栈 index 处表的元素个数。这个数值是表中字段 n 的值,若是它有一个数值型值,或者表中最大的数值型索引不是一个 nil 值。
-------------------
5.12 调用 Lua 函数
-------------------
定义在 Lua 中的函数(或者注册到 Lua 的 C 函数)能够从宿主程序调用。这采用以下协议:首先,被调用的函数压栈;而后,函数的参数以直接顺序压栈(参见 5.5 节),即,第一个参数先压栈。最后,函数被调用经过:
int lua_call (lua_State *L, int nargs, int nresults);
这个函数返回和 lua_dostring (参见 5.8 节)一样的错误码。若是你想传播这个错误,而不是返回一个错误码,使用
void lua_rawcall (lua_State *L, int nargs, int nresults);
在这两个函数中,nargs 是你压栈的参数的个数。全部的参数和函数值都从栈中弹出,函数的返回值压栈。函数的返回值被调整(参见 4.3 节)为 nresults,除非 nresults 是 LUA_MULTRET。在这种状况下,全部的函数返回值都压栈。函数的返回值以直接顺序压栈(第一个返回值最早压栈),因此调用以后最后一个返回值在栈顶。
下面的例子显示宿主程序如何作到和 Lua 代码等价的:
a,b = f("how", t.x, 4)
在 C 中:
lua_getglobal(L, "t"); /* global `t' (for later use) */ lua_getglobal(L, "f"); /* function to be called */ lua_pushstring(L, "how"); /* 1st argument */ lua_pushstring(L, "x"); /* push the string `x' */ lua_gettable(L, -4); /* push result of t.x (2nd arg) */ lua_pushnumber(L, 4); /* 3rd argument */ lua_call(L, 3, 2); /* call function with 3 arguments and 2 results */ lua_setglobal(L, "b"); /* set global variable `b' */ lua_setglobal(L, "a"); /* set global variable `a' */ lua_pop(L, 1); /* remove `t' from the stack */
注意,上面的代码是平衡的:在它结束时,栈被回退为它的原来配置。这被认为是好的编译实践。
一些特别的 Lua 函数有它们本身的 C 接口。宿主程序能够生成一个 Lua 错误经过调用函数
void lua_error (lua_State *L, const char *message);
这个函数永远不返回。若是 lua_error 被从 Lua 调用的 C 函数中调用,相应的 Lua 执行被终结,就如同 Lua 代码中发生了一个错误。不然,整个宿主程序被一个 exit (EXIT_FAILURE)调用终止。在终结执行以前,message 被传递给错误处理函数 _ERRORMESSAGE (参见 4.7 节)。若是 message 为 NULL, _ERRORMESSAGE 便不会被调用。
标签方法能够经过下面的函数改变
void lua_settagmethod (lua_State *L, int tag, const char *event);
第二个参数是标签,第三个参数是事件名字(参见 4.8 节);新的方法从栈上弹出。为得到当前的
标签方法的值,使用函数
void lua_gettagmethod (lua_State *L, int tag, const char *event);
能够拷贝全部的标签方法从一个标签到另外一个:
int lua_copytagmethods (lua_State *L, int tagto, int tagfrom);
这个函数返回 tagto。
你能够遍历一个表经过函数
int lua_next (lua_State *L, int index);
这里 index 指定被遍历的表。这个函数从栈中弹出一个 key,并从表中压栈一个 key-value 对(也就是给定 key 的下一个键值对)。若是没有元素了,函数返回 0 (不压栈)。一个典型的调用以下:
/* table is in the stack at index `t' */ lua_pushnil(L); /* first key */ while (lua_next(L, t) != 0) { /* `key' is at index -2 and `value' at index -1 */ printf("%s - %s\n", lua_typename(L, lua_type(L, -2)), lua_typename(L, lua_type(L, -1))); lua_pop(L, 1); /* removes `value'; keeps `index' for next iteration */ }
函数
void lua_concat (lua_State *L, int n);
链接栈顶的 n 个值,弹出它们,而且压栈结果;n 最小为 2 。链接操做遵照 Lua 通用的语义(参见 4.5.5 节)。
-------------------
5.13 定义 C 函数
-------------------
注册 C 函数到 Lua ,用下面的宏:
#define lua_register(L, n, f) (lua_pushcfunction(L, f), lua_setglobal(L, n)) /* const char *n; */ /* lua_CFunction f; */
它接受函数在 Lua 中的名字,和一个函数指针。这个指针的类型必须为 lua_CFunction,其定义为:
typedef int (*lua_CFunction) (lua_State *L);
也就是一个 Lua 环境参数一个整型返回值的函数指针。
为了和 Lua 正确的交互,C 函数必须遵照下面的协议,这个协议规定了参数和返回值传递的方法:C 函数在栈里获得它从 Lua 得到的参数,以直接顺序(第一个参数最早压栈)。为了返回值到 Lua, C 函数能够把返回值顺序压栈(第一个返回值最早压栈),而且返回结果的个数。就像 Lua 函数同样,一个由 Lua 调用的 C 函数也能够返回多个值。
做为一个例子,下面的函数接受一个可变个数的数值型参数而且返回它们的平均数和总数:
static int foo (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ double sum = 0; int i; for (i = 1; i <= n; i++) { if (!lua_isnumber(L, i)) lua_error(L, "incorrect argument to function `average'"); sum += lua_tonumber(L, i); } lua_pushnumber(L, sum/n); /* first result */ lua_pushnumber(L, sum); /* second result */ return 2; /* number of results */ }
函数能够做为 'average' 注册到 Lua ,经过调用
lua_register(L, "average", foo);
当新建一个 C 函数时,能够为它关联一些 upvalue (参见 4.6 节),从而新建一个 C 闭包,当函数被调用时这些值被传递给它,做为普通的参数。为了为了一个 C 函数关联 upvalue,首先这些值应该压栈。而后函数
void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);
被用来压栈 C 函数,参数 n 说明应该有多少个 upvalue 被关联到函数(这些 upvalue 从栈上弹出);事实上,宏 pushcfunction 被定义为参数 n 为 0 的 lua_pushcclosure 。而后,不管什么时候 C 函数被调用时,这些 upvalue 被插入函数做为最后的参数,在函数调用的实际参数以后。这使得程序容易得到 upvalue 而没必要知道函数接受多少个参数(回忆一下,Lua 中的函数能够接受任意数量的参数):第 i 个 upvalue 在栈里的索引为 i-(n+1),这里 n 为 upvalue 的个数。
更多的 C 函数和闭包的例子,参见官方发布版中的文件 lbaselib.c, liolib.c, lmathlib.c, 和 lstrlib.c 。
-------------------
5.14 Lua 对象的引用
-------------------
若是一个 C 代码须要在一个 C 函数的生存期以外保持一个 Lua 值的话, 它必须新建那个值的引用(reference)。下面的函数能够管理这种引用:
int lua_ref (lua_State *L, int lock); int lua_getref (lua_State *L, int ref); void lua_unref (lua_State *L, int ref);
lua_ref 从栈顶弹出一个值,新建一个它的引用,并返回这个引用。一个 nil 值的引用永远为 LUA_REFNIL。(lua.h 也定义了一个不一样于其它的有效引用的常量 LUA_NOREF 。)若是 lock 不是 0 的话,对象就被锁定:这意味着这个对象将不会被垃圾回收。没有锁定的引用可被垃圾回收。
当 C 中须要引用对象的时候,调用 lua_getref 把那个对象压栈;若是对象已经被垃圾回收了,lua_getref 返回 0 (不压栈任何东西)。
当一个引用不须要了,应该使用 lua_unref 调用来释放它。
注册
当 Lua 启动时,它在位置 LUA_REFREGISTRY 注意一个表。它能够经过宏来引用
#define lua_getregistry(L) lua_getref(L, LUA_REFREGISTRY)
这个表能够被 C 库做为一个注册机制使用。任何 C 库能够在这个表中保存数据,只要它使用的 key 不一样于其它的库。(未完待续)