构建和使用 Lua 构建 Lua 很容易;只需运行 make <platform>;如果不想依赖特定于平台的特性,那么可以依靠 posix,甚至是 ansi。构建后得到一个库 liblua.a,可以将它链接到程序。恭喜!您已经嵌入了 Lua。当然,要想真正使用它,还需要做一些工作。
Lua 的可重入性源于将所有解释器状态保存在一个对象中;可以有多个解释器,它们之间不共享变量,也没有共享的全局项。为了与 Lua 交互,必须从创建一个 Lua 状态开始:
lua_State *l;
l = lua_open();
如果 lua_open() 调用失败,则返回一个 null 指针。否则,就有了一个有效的 Lua 解释器状态。当然,如果没有库,还是不够。可以使用 luaL_openlibs() 函数添加标准库:
luaL_openlibs(l);
Lua 状态现在可以执行代码了。下面是一个程序中的一个循环,它只是执行作为 Lua 代码的参数:
清单 1. 将参数作为 Lua 代码执行1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| for (i = 1; i < argc; ++i) {
if (luaL_loadbuffer(l, argv, strlen(argv), "argument")) {
fprintf(stderr, "lua couldn't parse '%s': %s.\n",
argv, lua_tostring(l, -1));
lua_pop(l, 1);
} else {
if (lua_pcall(l, 0, 1, 0)) {
fprintf(stderr, "lua couldn't execute '%s': %s.\n",
argv, lua_tostring(l, -1));
lua_pop(l, 1);
} else {
lua_pop(l, lua_gettop(l));
}
}
}
|
luaL_loadbuffer() 函数将一个脚本编译成 Lua 代码。如果有语法错误,从这里可以观察到失败。错误消息被返回到栈上。否则,使用 lua_pcall() 函数执行编译后的代码。同样,如果有错误,错误消息被返回到栈上。注意,每个对 Lua 或关于 Lua 的 C 语言调用都带有一个 Lua 状态作为一个参数;没有缺省的状态。
理解 Lua 栈Lua 解释器使用一个栈接口来与调用代码通信。由 C 代码将发送到 Lua 代码的数据 push 到栈上;Lua 解释器返回的响应也被 push 到栈上。如果传递给 luaL_loadbuffer() 的代码有错误,则错误消息被 push 到栈上。
栈上的项有类型和值。lua_type() 函数查询一个对象的类型;lua_to<type>() 函数(例如 lua_tostring())产生被强制转换成特定 C 类型的值。用 Lua 编写的代码总是严格遵从栈模型;但是,C 代码则可以探查栈的其余部分,甚至可以在栈中插入值。
这种接口虽然简单,但是其功能出奇强大。待执行的代码都被同等对待;首先被 push 到栈上,然后等待 lua_pcall() 函数来执行它。
使用 lua_pcall() 除了要在其上面进行操作的 Lua 状态外,lua_pcall() 函数还带有 3 个参数。待执行的代码并非这些参数之一;这个代码由 luaL_loadbuffer() 或获得代码的其他函数 push 到栈上。实际上,lua_pcall() 以传递给要执行的代码的栈参数的数量、期望返回的结果的数量以及一个错误处理程序为参数,最后一个参数是可选的。要调用一个函数,首先要 push 该函数,然后 push 该函数??带的参数(按顺序)。返回的参数以同样的顺序 push。返回的第一个值在下,最后一个值在上。
无论是对于发送参数还是获取结果值,Lua 都自动更正值的数量,以便与传递给 lua_pcall() 的数量匹配;如果没有提供足够的值,那么剩下的参数以 nil 值填充,如果有额外的值,多出的值被自动丢弃。(这与 Lua 在多个赋值操作上的行为是一样的)。
如果提供错误处理程序,那么它应该是用于处理任何发生的错误的 Lua 代码在栈上的索引。对于这篇概述性的文章,我不会详细讨论错误处理;但是要知道两点,首先,错误处理是存在的,其次,错误处理是在 Lua 中进行的。这样非常方便。
将 C 嵌入到 Lua 中 用 C 编写 Lua 使用的函数非常容易。如果您曾经编写过嵌入到其他脚本语言中的代码,那么您也许会感到震惊。下面是一个 C 函数,它接收一个数字 x,返回 x + 1:
清单 2. 供 Lua 使用的 C 函数1
2
3
4
5
6
7
8
9
| int
l_ink(lua_State *L) {
int x;
if (lua_gettop(L) >= 0) {
x = (int) lua_tonumber(L, -1);
lua_pushnumber(L, x + 1);
}
return 1;
}
|
该函数是借助一个 Lua 状态参数来调用的;同样,C 与 Lua 之间的所有交互都是通过 Lua 状态栈发生的。返回值是该函数 push 到栈上的对象的数量。为了使 Lua 可以使用该函数,必须做两件事。首先是创建一个表示该函数的 Lua 对象,其次是为它提供一个名称:
lua_pushcfunction(L, l_ink);
lua_setglobal(L, "ink");
可以使用 lua_pushcfunction() 将一个 C 函数指针转换成一个内部 Lua 对象。当然,这个对象被 push 到栈上。然后,lua_setglobal() 函数将栈顶的值赋给一个有名称的全局变量。由于函数在 Lua 中就是值,所以这实际上就是创建一个回调函数。
栈接口大大简化了这一点;在此,没有必要定义或声明函数所带的参数。对于如何调用代码,Lua 是非常灵活的。不过您如果愿意,也可以进行更仔细的检查。luaL_checknumber() 函数还可以用于检查参数,并且还能打印包含信息的错误消息,以及中断函数的执行。 |