标签:
第25章 调用 C 函数
扩展 Lua 的基本方法之一就是为应用程序注册新的 C 函数到 Lua中去。
当我们提到 Lua 可以调用 C 函数,不是指 Lua 可以调用任何类型的 C 函数(有一些包可以让 Lua 调用任意的 C 函数,但缺乏便捷和健壮性)。正如我们前面所看到的,当C 调用 Lua函数的时候,必须遵循一些简单的协议来传递参数和获取返回结果。相似的, 从Lua 中调用 C 函数,也必须遵循一些协议来传递参数和获得返回结果。另外,从
Lua 调用 C 函数我们必须注册函数,也就是说,我们必须把C 函数的地址以一个适当的方式传递给 Lua解释器。
当 Lua调用 C 函数的时候,使用和 C调用Lua 相同类型的栈来交互。C 函数从栈中获取她的参数,调用结束后将返回结果放到栈中。为了区分返回结果和栈中的其他的值, 每个 C 函数还会返回结果的个数(the function returns(in C) the number of results it is leaving on the stack)。这儿有一个重要的概念:用来交互的栈不是全局变量,每一个函数都有他自己的私有栈。当
Lua调用 C 函数的时候,第一个参数总是在这个私有栈的index = 1 的位置。甚至当一个C函数调用 Lua 代码(Lua 代码调用同一个 C 函数或者其他的C函数),每一个 C 函数都有自己的独立的私有栈,并且第一个参数在 index =1 的位置。
25.1C 函数
先看一个简单的例子,如何实现一个简单的函数返回给定数值的 sin 值(更专业的 实现应该检查他的参数是否为一个数字):
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 ofresults */ }
typedef int (*lua_CFunction) (lua_State *L);
lua_pushcfunction(l,l_sin); lua_setglobal(l, "mysin");
static int l_sin (lua_State *L) { double d = luaL_checknumber(L, 1); lua_pushnumber(L, sin(d)); return 1; /* number ofresults */ }
bad argument#1 to 'mysin' (number expected, got string)注意看看 luaL_checknumber是如何自动使用:参数 number(1),函数名("mysin"), 期望的参数类型("number"),实际的参数类型("string")来拼接最终的错误信息的。
#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 thedirectory? */ lua_pushnil(L); /* return niland ... */lua_pushstring(L, strerror(errno)); /* errormessage */ return 2; /* number of results*/ } /* create resulttable */ 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 isalready on top*/ }
辅助库的 luaL_checkstring 函数用来检测参数是否为字符串,与 luaL_checknumber 类似。(在极端情况下,上面的l_dir 的实现可能会导致小的内存泄漏。调用的三个 Lua 函数 lua_newtable、lua_pushstring和 lua_settable可能由于没有足够的内存而失败。其中任何一个调用失败都会抛出错误并且终止
l_dir,这种情况下,不会调用 closedir。正如 前面我们所讨论过的,对于大多数程序来说这不算个问题:如果程序导致内存不足,最好的处理方式是立即终止程序。另外,在 28章我们将看到另外一种解决方案可以避免这 个问题的发生)
25.2 C 函数库
一个 Lua 库实际上是一个定义了一系列 Lua 函数的 chunk,并将这些函数保存在适当的地方,通常作为 table 的域来保存。Lua的 C 库就是这样实现的。除了定义 C 函数 之外,还必须定义一个特殊的用来和 Lua 库的主 chunk 通信的特殊函数。一旦调用,这个函数就会注册库中所有的 C 函数,并将他们保存到适当的位置。像一个 Lua 主 chunk一样,她也会初始化其他一些在库中需要初始化的东西。
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 structluaL_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; }
mylib = loadlib("fullname-of-your-library", "luaopen_mylib")将 luaopen_mylib 函数转换成 Lua 中的一个 C 函数,并将这个函数赋值给 mylib(那就是为什么 luaopen_mylib 必须和其他的 C 函数有相同的原型的原因所在)。然后,调用mylib(),将运行 luaopen_mylib打开你定义的函数库。
int luaopen_mylib (lua_State *L); #define LUA_EXTRALIBS { "mylib", luaopen_mylib },第一行声明了打开库的函数。第二行定义了一个宏 LUA_EXTRALIBS 作为函数数 组的新的入口,当解释器创建新的状态的时候会调用这个宏。(这个函数数组的类型为 struct luaL_reg[],因此我们需要将名字也放进去)
-DLUA_USERCONFIG=\"mylib.h\"(反斜线防止双引号被 shell 解释,当我们在 C 中指定一个头文件时,这些引号是 必需的。)在一个整合的开发环境中,你必须在工程设置中添加类似的东西。然后当你重 新编译 lua.c 的时候,它包含 mylib.h,并且因此在函数库的列表中可以用新定义的 LUA_EXTRALIBS 来打开函数库。
标签:
原文地址:http://blog.csdn.net/heyuchang666/article/details/51275650