第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于后续版本,但有一些差异。
第四版针对 Lua 5.3,可在 Amazon 和其他书店购买。
购买本书,您还可以帮助支持 Lua 项目


26.1 – 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_newtablelua_pushstringlua_settable。如果这些调用中的任何一个失败,它将引发一个错误并中断 l_dir,因此不会调用 closedir。正如我们之前讨论的,对于大多数程序来说,这不是一个大问题:如果程序内存不足,它能做的最好的事情就是关闭。不过,在 第 29 章 中,我们将看到一个避免此问题的目录函数的备用实现。)