(接上篇)
--------------------------------------
5 API
--------------------------------------
这节主要描述 Lua 的 API, 也就是宿主程序和库交互的一组 C 函数。API 函数可以分为以下几类:
1. 执行 Lua 代码;
2. 在 Lua 和 C 之间进行值的转化;
3. 操作(读写)Lua 对象;
4. 调用 Lua 函数;
5. 由 Lua 调用的 C 函数;
6. Lua 对象的引用。
所有的 API 都在文件 lua.h 中声明。
-------------------
5.1 执行 Lua 代码
-------------------
一个宿主程序可以执行写在文件中或在字符串中的 Lua 模块,使用下面的函数:
int lua_dofile (char *filename);
int lua_dostring (char *string);
两个函数都返回一个错误码:0 表示成功,非 0 表示失败。函数 lua_dofile,如果调用的时候参数为 NULL(0),将把标准输入作为“文件”执行。lua_dofile 也能执行预编译的模块。它能检查出文件是否是文本,并相应有加载它 (参见程序 luac)。
-------------------
5.2 在 Lua 和 C 之间进行值的转化
-------------------
因为 Lua 没有静态的类型系统,所有的在 Lua 和 C 之间传递的值的类型为 lua_Object,它像是 C 中的可保存任何 Lua 值的一个抽象的类型。 lua_Object 类型的在 Lua 之外是没有意义的,例如,比较两个 lua_Object 就没有意义。
Lua 有自动内存管理和垃圾回收。因此,一个 lua_Object 有一个受限的作用域,它只在新建它的块(block)中有效。一个从 Lua 调用的 C 函数是一个 block,它的参数只在它结束之前是有效的。一个好的编程实践是在这些值(Lua 对象)可用的时候把它转化为 C 语言的值,并且永远不要把 lua_Object 保存在 C 全局变量中。
当一个 C 代码频繁的调用 Lua 代码的时候,比如在一个循环中,这些返回的对象一直累积,可能会带来内存问题。为了避免这种情况,嵌套的块可以在下面的函数中定义:
void lua_beginblock (void);
void lua_endblock (void);
块结束之后,所有在它之中新建的 lua_Object 都会被释放。
可以使用下面的函数来检查一个 lua_Obejct 的类型:
int lua_type (lua_Object object);
还有下面的这些宏和函数:
int lua_isnil (lua_Object object);
int lua_isnumber (lua_Object object);
int lua_isstring (lua_Object object);
int lua_istable (lua_Object object);
int lua_isfunction (lua_Object object);
int lua_iscfunction (lua_Object object);
int lua_isuserdata (lua_Object object);
所有宏返回 1 如果对象和给定类型兼容的话,否则返回 0。函数 lua_isnumber 可以接受数值和可转化为数值的字符串,lua_isstring 接受字符串和数值(参见 4.2 节),lua_isfunction 接受 Lua 和 C 函数。
lua_type 函数可以用来区分不同类型的用户数据,见下文。
可以使用下面的函数把一个 lua_Object 转化为 C 类型:
double lua_getnumber (lua_Object object);
char *lua_getstring (lua_Object object);
char *lua_copystring (lua_Object object);
lua_CFunction lua_getcfunction (lua_Object object);
void *lua_getuserdata (lua_Object object);
lua_getnumber 可以把一个 lua_Obejct 转化为一个浮点数。这个 lua_Object 必须是一个数字或者一个可以转化为数字的字符串(见 4.2 节);否则,该函数返回 0。
lua_getstring 把 lua_Object 转化为一个 string(char *)。这个 lua_Object 必须是一个字符串或者一个数字;否则,该函数返回 0 (空指针)。该函数不会创建一个新的字符串,它只是返回一个指向 Lua 环境中的字符串的指针。因为 Lua 有垃圾回收,没有什么保证这个指针在 block 结束之后依然有效。
lua_getcfunction 把 lua_Object 转化为一个 C 函数。这个 lua_Obejct 类型必须为 Cfunction; 否则返回 0 (空指针)。类型 lua_CFunction 在 5.5 节中解释。
lua_getuserdata 把 lua_Object 转化为一个 void *。这个 lua_Obejct 类型必须为 userdata; 否则返回 0 (空指针)。
相反,把一个 C 类型转化为 lua_Obejct 类型用以下的函数:
void lua_pushnumber (double n);
void lua_pushstring (char *s);
void lua_pushcfunction (lua_CFunction f);
void lua_pushusertag (void *u, int tag);
还有一个宏:
void lua_pushuserdata (void *u);
这些函数都接受一个 C 值,把它转化为相应的 lua_Object,并把结果保存在 Lua 栈顶,在那里它可以被赋值给一个 Lua 变量,做为参数传递给一个 Lua 函数,等(见下文)。
用户数据(user data)可以有不同的标签(tag),它们的语义由宿主程序定义。任何正整数可以做为一个用户数据的标签。当一个用户数据返回的时候,可以使用 lua_type 来返回它的标签。
为了完成设置,nil 或者 lua_Object 也可以压栈,用下面的函数:
void lua_pushnil (void);
void lua_pushobject (lua_Object object);
-------------------
5.3 操作Lua 对象
-------------------
可以使用以下的函数读取 Lua 全局变量的值:
lua_Object lua_getglobal (char *varname);
在 Lua 中,如果全局变量的值为 nil, 回退函数 “getglobal" 会被调用。
把前先压到栈顶的一个值保存到一个全局变量,用下面的函数:
void lua_storeglobal (char *varname);
表也可以通过 API 来操作,函数:
lua_Object lua_getsubscript (void);
期望栈上是一个表和一个索引,并且返回表在索引处的内容。在 Lua 中,如果第一个对象不是一个表,或者索引在表中不存在,相应的回退函数会被调用。
为了保存一个值到给定的索引,程序必须把表,索引,值压栈,之后调用函数:
void lua_storesubscript (void);
相应的回退函数会被调用,如果需要的话。
最后,函数
lua_Object lua_createtable (void);
新建一个表。
请注意:大部分 Lua 库中的函数从栈上接受参数。因为其它的函数也使用这个相同的栈,所以在调用函数前参数压栈时请务必保证不调用 Lua 库中的函数。例如:如果用户想获得 a[i] 的值,一个单纯的解决方案是:
/* Warning: WRONG CODE */
lua_Object result;
lua_pushobject(lua_getglobal("a")); /*push table*/
lua_pushobject(lua_getglobal("i")); /*push index*/
result = luat_getsubscript();
然而,lua_getglobal("i") 调用改变了栈,使先前压栈的值无效了。一个正确的解决方案是:
lua_Object result;
lua_Object index = lua_getglobal("i");
lua_pushobject(lua_getglobal("a")); /*push table*/
lua_pushobject(index); /*push index*/
result = luat_getsubscript();
-------------------
5.4 调用 Lua 函数
-------------------
在宿主程序中可以调用由模块执行 dofile 或者 dostring 定义的函数。这采用如下的协议:首先,函数参数压到 Lua 栈上(参见 5.2 节),压栈的顺序和参数一致,也就是第一个参数首先压栈。再次强调,在压栈过程中,不要调用其它的 Lua 函数。
然后,函数调用可以用:
int lua_call (char *functionname);
或者
int lua_callfunction (lua_Object function);
两个函数都返回一个错误码:0 表示成功,非 0 表示失败。最后,返回值(Lua 函数可以返回多个值)可以用下面的宏取得:
lua_Object lua_getresult (int number);
number 就是返回的结果的顺序,从 1 开始。当用一个大于实际返回结果数的数值调用时,这个函数返回 LUA_NOOBJECT.
两个特殊的 Lua 函数有特殊的作用:error 和 setfallback。一个 C 函数可以产生一个 Lua 错误通过调用函数:
void lua_error (char *message);
这个函数不返回。如果在 Lua 里调用这个 C 函数,相应在的 Lua 执行就被终结了,如同在 Lua 代码中发生一个错误。否则,整个程序结束。
回退函数可以通过下面的函数修改:
lua_Object lua_setfallback(char *name, lua_CFunction fallback);
第一个参数是回退的名字,第二个是一个将做为新的回退函数的 CFunction。这个函数返回老的回退函数 lua_Object,或者失败时返回 nil(无效的回退函数名字)。这个老的回退函数值可用来串连(chaining)回退函数。
8.10 节有一个 C 代码调用 Lua 函数的例子。
-------------------
5.5 C 函数
-------------------
注册 C 函数到 Lua ,用下面的宏:
#define lua_register(n,f) (lua_pushcfunction(f), lua_storeglobal(n))
/* char *n; */
/* lua_CFunction f; */
它接受函数在 Lua 中的名字,和一个函数指针。这个指针的类型必须为 lua_CFunction,其定义为:
typedef void (*lua_CFunction) (void);
也就是一个无参无返回值的函数指针。
为了和 Lua 正确的交互,C 函数必须遵守一个协议,这个协议规定了参数和返回值传递的方法。
为了得到它的参数,C 函数调用 :
lua_Object lua_getparam (int number);
number 从 1 开始返回第一个参数。当用一个大于参数实际个数的值来调用时,该函数返回 LUA_NOOBJECT。用这种方法,就可以写可变参数个数的函数。
为了从 C 返回值到 Lua, C 函数可以把返回值顺序压栈;见 5.2 节。就像 Lua 函数一样,一个由 Lua 调用的 C 函数也可以返回多个值。
8.9 节展示了一个 Cfunction 的例子。
-------------------
5.6 Lua 对象的引用
-------------------
如前所述,lua_Object 是易变的(volatile)。如果一个 C 代码需要在块作用域以外保存一个 lua_Object 的话, 它必须新建一个对象的引用。下面的函数可以管理这种引用:
int lua_ref (int lock);
lua_Object lua_getref (int ref);
void lua_pushref (int ref);
void lua_unref (int ref);
函数 lua_ref 新建一个栈顶对象的引用,并返回这个引用。如果 lock 是 ture 的话,对象就被锁定:这意味着这个对象将不会被垃圾回收。注意一个没有锁定的引用可能会被垃圾回收。任何需要对象引用的地方都可以调用 lua_getref 来返回一个对象的句柄.lua_pushref 把对象压栈。如果对象已经被垃圾回收了,lua_getref 返回一个 LUA_NOOBJECT, lua_pushobject 会出错。
如果一个引用已经没用了,可以使用 lua_unref 调用来释放它。
(未完待续)