26.2 C 函数库

一个Lua库实际上是一个定义了一系列Lua函数的chunk,并将这些函数保存在适当的地方,通常作为table的域来保存。LuaC库就是这样实现的。除了定义C函数之外,还必须定义一个特殊的用来和Lua库的主chunk通信的特殊函数。一旦调用,这个函数就会注册库中所有的C函数,并将他们保存到适当的位置。像一个Luachunk一样,她也会初始化其他一些在库中需要初始化的东西。

Lua通过这个注册过程,就可以看到库中的C函数。一旦一个C函数被注册之后并保存到Lua中,在Lua程序中就可以直接引用他的地址(当我们注册这个函数的时候传递给Lua的地址)来访问这个函数了。换句话说,一旦C函数被注册之后,Lua调用这个函数并不依赖于函数名,包的位置,或者调用函数的可见的规则。通常C库都有一个外部(public/extern)的用来打开库的函数。其他的函数可能都是私有的,在C中被声明为static

当你打算使用C函数来扩展Lua的时候,即使你仅仅只想注册一个C函数,将你的C代码设计为一个库是个比较好的思想:不久的将来你就会发现你需要其他的函数。一般情况下,辅助库对这种实现提供了帮助。luaL_openlib函数接受一个C函数的列表和他们对应的函数名,并且作为一个库在一个table中注册所有这些函数。看一个例子,假定我们想用一个我们前面提过的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的第二个参数是库的名称。这个函数按照指定的名字创建(或者reuse)一个表,并使用数组mylib中的name-function对填充这个表。luaL_openlib还允许我们为库中所有的函数注册公共的upvalues。例子中不需要使用upvalues,所以最后一个参数为0luaL_openlib返回的时候,将保存库的表放到栈内。luaL_openlib函数返回1,返回这个值给Lua。(The luaopen_mylib function returns 1 to return this value to Lua)(和Lua库一样,这个返回值是可选的,因为库本身已经赋给了一个全局变量。另外,像在Lua标准库中的一样,这个返回不会有额外的花费,在有时候可能是有用的。)

完成库的代码编写之后,我们必须将它链接到Lua解释器。最常用的方式使用动态连接库,如果你的Lua解释器支持这个特性的话(我们在8.2节已经讨论过了动态连接库)。在这种情况下,你必须用你的代码创建动态连接库(windows.dll文件,linux.so文件)。到这一步,你就可以在Lua中直接使用loadlib加载你刚才定义的函数库了,下面这个调用:

mylib = loadlib("fullname-of-your-library", "luaopen_mylib")

luaopen_mylib函数转换成Lua中的一个C函数,并将这个函数赋值给mylib(那就是为什么luaopen_mylib必须和其他的C函数有相同的原型的原因所在)。然后,调用mylib(),将运行luaopen_mylib打开你定义的函数库。

如果你的解释器不支持动态链接库,你必须将你的新的函数库重新编译到你的Lua中去。除了这以外,还不要一些方式告诉独立运行的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来打开函数库。

 

 

 



相关链接:
lua程序设计目录 - 中国lua开发者 - lua论坛