此第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然与更高版本相关,但有一些差异。
第四版针对 Lua 5.3,可在 亚马逊 和其他书店购买。
购买本书,您还可以帮助 支持 Lua 项目


26.2 – C 库

Lua 库是一个定义多个 Lua 函数并将它们存储在适当位置(通常作为表中的条目)的块。Lua 的 C 库模仿此行为。除了定义其 C 函数之外,它还必须定义一个特殊函数,该函数对应于 Lua 库的主块。一旦调用,此函数将注册库的所有 C 函数并将它们存储在适当的位置。与 Lua 主块一样,它还会初始化库中需要初始化的其他任何内容。

Lua 通过此注册过程“看到”C 函数。一旦 C 函数在 Lua 中表示和存储,Lua 程序就会通过直接引用其地址(这是我们在注册函数时提供给 Lua 的)来调用它。换句话说,一旦注册,Lua 就不依赖于函数名称、包位置或可见性规则来调用函数。通常,C 库只有一个公共(extern)函数,即打开库的函数。所有其他函数都可以是私有的,在 C 中声明为 static

当您使用 C 函数扩展 Lua 时,即使您只想注册一个 C 函数,最好将您的代码设计为 C 库:迟早(通常是早)您将需要其他函数。与往常一样,辅助库为此任务提供了一个帮助器函数。luaL_openlib 函数接收一个 C 函数及其各自名称的列表,并将它们全部注册到具有库名称的表中。例如,假设我们想要创建一个包含我们之前定义的 l_dir 函数的库。首先,我们必须定义库函数

    static int l_dir (lua_State *L) {
       ...  /* as before */
    }
接下来,我们声明一个包含所有函数及其各自名称的数组。此数组具有类型为 luaL_reg 的元素,这是一个具有两个字段的结构:一个字符串和一个函数指针。
    static const struct luaL_reg mylib [] = {
      {"dir", l_dir},
      {NULL, NULL}  /* sentinel */
    };
在我们的示例中,只有一个要声明的函数(l_dir)。请注意,数组中的最后一对必须是 {NULL, NULL},以表示其结束。最后,我们使用 luaL_openlib 声明一个主函数
    int luaopen_mylib (lua_State *L) {
      luaL_openlib(L, "mylib", mylib, 0);
      return 1;
    }
luaL_openlib 的第二个参数是库名。此函数创建(或重新使用)一个具有给定名称的表,并用数组 mylib 指定的名称-函数对填充该表。luaL_openlib 函数还允许我们为库中的所有函数注册通用上值。现在,我们不使用上值,因此调用中的最后一个参数为零。返回时,luaL_openlib 将打开库的表保留在堆栈中。luaopen_mylib 函数返回 1 以将此值返回给 Lua。(与 Lua 库一样,此返回是可选的,因为该库已分配给全局变量。同样,与 Lua 库一样,它没有任何成本,并且偶尔可能有用。)

完成库后,我们必须将其链接到解释器。如果 Lua 解释器支持此功能,最方便的方法是使用动态链接功能。(请记住 第 8.2 节 中关于动态链接的讨论。)在这种情况下,您必须使用代码创建一个动态库(Windows 中的 .dll 文件,Linux 中的 .so 文件)。之后,您可以使用 loadlib 直接从 Lua 中加载库。调用

    mylib = loadlib("fullname-of-your-library", "luaopen_mylib")
luaopen_mylib 函数转换为 Lua 中的 C 函数,并将此函数分配给 mylib。(这就解释了为什么 luaopen_mylib 必须与任何其他 C 函数具有相同的原型。)接下来,调用 mylib() 运行 luaopen_mylib,打开库。

如果您的解释器不支持动态链接,那么您必须使用新库重新编译 Lua。除此之外,您需要某种方法来告诉独立解释器,当它打开一个新状态时,它应该打开此库。一些宏可以简化此任务。首先,您必须创建一个头文件(我们称之为 mylib.h),其内容如下

    int luaopen_mylib (lua_State *L);
    
    #define LUA_EXTRALIBS { "mylib", luaopen_mylib },
第一行声明打开函数。下一行将宏 LUA_EXTRALIBS 定义为解释器在创建新状态时调用的函数数组中的一个新条目。(此数组的类型为 struct luaL_reg[],因此我们需要在那里放置一个名称。)

要在解释器中包含此头文件,您可以在编译器选项中定义宏 LUA_USERCONFIG。对于命令行编译器,您通常必须添加类似以下的选项

    -DLUA_USERCONFIG=\"mylib.h\"
(反斜杠保护 shell 中的引号;当我们指定包含文件名时,C 中需要这些引号。)在集成开发环境中,您必须在项目设置中添加类似的内容。然后,当您重新编译 lua.c 时,它将包含 mylib.h,因此在要打开的库列表中使用 LUA_EXTRALIBS 的新定义。