第一版是为 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。保留所有权利。 |