第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于后续版本,但有一些差异。
第四版针对 Lua 5.3,可在 Amazon 和其他书店购买。
购买本书,您还可以帮助支持 Lua 项目。
![]() |
用 Lua 编程 | ![]() |
| 第四部分 C API 第 26 章 从 Lua 调用 C |
作为第一个示例,让我们看看如何实现一个简化版的函数,该函数返回给定数字的正弦值(更专业的实现应检查其参数是否为数字)
static int l_sin (lua_State *L) {
double d = lua_tonumber(L, 1); /* get argument */
lua_pushnumber(L, sin(d)); /* push result */
return 1; /* number of results */
}
向 Lua 注册的任何函数都必须具有相同的原型,该原型在 lua.h 中定义为 lua_CFunction
typedef int (*lua_CFunction) (lua_State *L);从 C 的角度来看,C 函数将其单个参数作为 Lua 状态获取,并(在 C 中)返回一个整数,其中包含它正在返回的值的数量(在 Lua 中)。因此,该函数无需在压入结果之前清除堆栈。返回后,Lua 会自动删除结果下方堆栈中的任何内容。
在我们可以从 Lua 使用此函数之前,我们必须注册它。我们使用 lua_pushcfunction 完成此神奇操作:它获取指向 C 函数的指针,并创建一个类型为 "function" 的值来表示 Lua 中的此函数。测试 l_sin 的一种快速而肮脏的方法是将其代码直接放入文件 lua.c 中,并在调用 lua_open 后立即添加以下行
lua_pushcfunction(l, l_sin);
lua_setglobal(l, "mysin");
第一行压入类型为函数的值。第二行将其分配给全局变量 mysin。在进行这些修改后,您重新构建 Lua 可执行文件;然后您可以在 Lua 程序中使用新函数 mysin。在下一节中,我们将讨论将新的 C 函数与 Lua 链接的更好方法。
对于更专业的正弦函数,我们必须检查其参数的类型。在此,辅助库对我们有帮助。luaL_checknumber 函数检查给定参数是否为数字:如果出错,它会抛出一个信息丰富的错误消息;否则,它会返回该数字。我们函数中的修改很小
static int l_sin (lua_State *L) {
double d = luaL_checknumber(L, 1);
lua_pushnumber(L, sin(d));
return 1; /* number of results */
}
使用上述定义,如果您调用 mysin('a'),您会收到以下消息
bad argument #1 to `mysin' (number expected, got string)请注意
luaL_checknumber 如何自动使用参数编号 (1)、函数名称 ("mysin")、预期的参数类型 ("number") 和实际参数类型 ("string") 填充消息。
作为一个更复杂的示例,让我们编写一个函数来返回给定目录的内容。Lua 并未在其标准库中提供此函数,因为 ANSI C 没有用于此工作的函数。在此,我们将假定我们有一个符合 POSIX 的系统。我们的函数 dir 获取一个字符串作为目录路径的参数,并返回一个包含目录项的数组。例如,调用 dir("/home/lua") 可能会返回表 {".", "..", "src", "bin", "lib"}。如果出错,该函数将返回 nil 加上包含错误消息的字符串。
#include <dirent.h>
#include <errno.h>
static int l_dir (lua_State *L) {
DIR *dir;
struct dirent *entry;
int i;
const char *path = luaL_checkstring(L, 1);
/* open directory */
dir = opendir(path);
if (dir == NULL) { /* error opening the directory? */
lua_pushnil(L); /* return nil and ... */
lua_pushstring(L, strerror(errno)); /* error message */
return 2; /* number of results */
}
/* create result table */
lua_newtable(L);
i = 1;
while ((entry = readdir(dir)) != NULL) {
lua_pushnumber(L, i++); /* push key */
lua_pushstring(L, entry->d_name); /* push value */
lua_settable(L, -3);
}
closedir(dir);
return 1; /* table is already on top */
}
辅助库中的 luaL_checkstring 函数等效于字符串的 luaL_checknumber。
(在极端情况下,l_dir 的实现可能会造成轻微的内存泄漏。它调用的三个 Lua 函数可能会由于内存不足而失败:lua_newtable、lua_pushstring 和 lua_settable。如果这些调用中的任何一个失败,它将引发一个错误并中断 l_dir,因此不会调用 closedir。正如我们之前讨论的,对于大多数程序来说,这不是一个大问题:如果程序内存不足,它能做的最好的事情就是关闭。不过,在 第 29 章 中,我们将看到一个避免此问题的目录函数的备用实现。)
| 版权所有 © 2003–2004 Roberto Ierusalimschy。保留所有权利。 | ![]() |