lua与c交互主要通过栈,栈底为1,栈顶为-1,以下是其相互调用的常用接口:

c调lua

栈操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
int lua_gettop (lua_State *L);
返回栈顶元素的索引。 因为索引是从 1 开始编号的, 所以这个结果等于栈上的元素个数; 特别指出,0 表示栈为空。
void lua_settop (lua_State *L, int index); //相当于至栈顶HEAD指针
参数允许传入任何索引以及 0 。 它将把堆栈的栈顶设为这个索引。 如果新的栈顶比原来的大, 超出部分的新元素将被填为 nil 。 如果 index 为 0 , 把栈上所有元素移除。
void lua_pop (lua_State *L, int n);
从栈中弹出 n 个元素。
void lua_insert (lua_State *L, int index);
把栈顶元素移动到指定的有效索引处, 依次移动这个索引之上的元素。 不要用伪索引来调用这个函数, 因为伪索引没有真正指向栈上的位置。
void lua_remove (lua_State *L, int index);
从给定有效索引处移除一个元素, 把这个索引之上的所有元素移下来填补上这个空隙。 不能用伪索引来调用这个函数,因为伪索引并不指向真实的栈上的位置。
void lua_replace (lua_State *L, int index);
把栈顶元素放置到给定位置而不移动其它元素 (因此覆盖了那个位置处的值),然后将栈顶元素弹出。
void lua_pushvalue (lua_State *L, int index);
把栈上给定索引处的元素作一个副本压栈。
void lua_pushboolean (lua_State *L, int b);
把 b 作为一个布尔量压栈。
int lua_toboolean (lua_State *L, int index);
把给定索引处的 Lua 值转换为一个 C 中的布尔量( 0 或是 1 )。 和 Lua 中做的所有测试一样, lua_toboolean 会把任何不同于 false 和 nil 的值当作真返回; 否则就返回假。 (如果你想只接收真正的 boolean 值, 就需要使用 lua_isboolean 来测试值的类型。)
int lua_checkstack (lua_State *L, int n);
确保堆栈上至少有 n 个额外空位。 如果不能把堆栈扩展到相应的尺寸,函数返回假。 失败的原因包括将把栈扩展到比固定最大尺寸还大 (至少是几千个元素)或分配内存失败。 这个函数永远不会缩小堆栈; 如果堆栈已经比需要的大了,那么就保持原样。
lua_Number luaL_checknumber (lua_State *L, int arg);
检查函数的第 arg 个参数是否是一个 数字,并返回这个数字。
int lua_getglobal (lua_State *L, const char *name); //取全局变量并压栈
把全局变量 name 里的值压栈,返回该值的类型。
void lua_setglobal (lua_State *L, const char *name); //设置全局变量并出栈
从堆栈上弹出一个值,并将其设为全局变量 name 的新值。

table操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
void lua_createtable (lua_State *L, int narr, int nrec);
创建一张新的空表压栈。 参数 narr 建议了这张表作为序列使用时会有多少个元素; 参数 nrec 建议了这张表可能拥有多少序列之外的元素。 Lua 会使用这些建议来预分配这张新表。 如果你知道这张表用途的更多信息,预分配可以提高性能。 否则,你可以使用函数 lua_newtable 。
void lua_newtable (lua_State *L);
创建一张空表,并将其压栈。 它等价于 lua_createtable(L, 0, 0) 。
int lua_gettable (lua_State *L, int index); //取table里的值,在index处table栈顶k的值
把 t[k] 的值压栈, 这里的 t 是指索引指向的值, 而 k 则是栈顶放的值。
这个函数会弹出堆栈上的键,把结果放在栈上相同位置。 和在 Lua 中一样, 这个函数可能触发对应 "index" 事件的元方法 (参见 §2.4 )。
返回压入值的类型。
int lua_getfield (lua_State *L, int index, const char *k); // = lua_pushstring(L, key); lua_gettable(L, -2);
把 t[k] 的值压栈, 这里的 t 是索引指向的值。 在 Lua 中,这个函数可能触发对应 "index" 事件对应的元方法 (参见 §2.4 )。
函数将返回压入值的类型。
void lua_settable (lua_State *L, int index);
做一个等价于 t[k] = v 的操作, 这里 t 是给出的索引处的值, v 是栈顶的那个值, k 是栈顶之下的值。
这个函数会将键和值都弹出栈。 跟在 Lua 中一样,这个函数可能触发一个 "newindex" 事件的元方法 (参见 §2.4)。
void lua_setfield (lua_State *L, int index, const char *k);
做一个等价于 t[k] = v 的操作, 这里 t 是给出的索引处的值, 而 v 是栈顶的那个值。
这个函数将把这个值弹出栈。 跟在 Lua 中一样,这个函数可能触发一个 "newindex" 事件的元方法 (参见 §2.4)。
int lua_rawget (lua_State *L, int index);
类似于 lua_gettable , 但是作一次直接访问(不触发元方法)。
int lua_rawgeti (lua_State *L, int index, lua_Integer n);
把 t[n] 的值压栈, 这里的 t 是指给定索引处的表。 这是一次直接访问;就是说,它不会触发元方法。
void lua_rawset (lua_State *L, int index);
类似于 lua_settable , 但是是做一次直接赋值(不触发元方法)。
void lua_rawseti (lua_State *L, int index, lua_Integer i);
等价于 t[i] = v , 这里的 t 是指给定索引处的表, 而 v 是栈顶的值。
这个函数会将值弹出栈。 赋值是直接的;即不会触发元方法。
int lua_getmetatable (lua_State *L, int index);
如果该索引处的值有元表,则将其元表压栈,返回 1 。 否则不会将任何东西入栈,返回 0

调lua函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);
以保护模式调用一个函数。
nargs 和 nresults 的含义与 lua_call 中的相同。 如果在调用过程中没有发生错误, lua_pcall 的行为和 lua_call 完全一致。 但是,如果有错误发生的话, lua_pcall 会捕获它, 然后把唯一的值(错误消息)压栈,然后返回错误码。 同 lua_call 一样, lua_pcall 总是把函数本身和它的参数从栈上移除。
如果 msgh 是 0 , 返回在栈顶的错误消息就和原始错误消息完全一致。 否则, msgh 就被当成是 错误处理函数 在栈上的索引位置。 (在当前的实现里,这个索引不能是伪索引。) 在发生运行时错误时, 这个函数会被调用而参数就是错误消息。 错误处理函数的返回值将被 lua_pcall 作为错误消息返回在堆栈上。
典型的用法中,错误处理函数被用来给错误消息加上更多的调试信息, 比如栈跟踪信息。 这些信息在 lua_pcall 返回后, 由于栈已经展开,所以收集不到了。
lua_pcall 函数会返回下列常数 (定义在 lua.h 内)中的一个:
LUA_OK (0): 成功。
LUA_ERRRUN: 运行时错误。
LUA_ERRMEM: 内存分配错误。对于这种错,Lua 不会调用错误处理函数。
LUA_ERRERR: 在运行错误处理函数时发生的错误。
LUA_ERRGCMM: 在运行 __gc 元方法时发生的错误。 (这个错误和被调用的函数无关。)
int lua_pcallk (lua_State *L,
int nargs,
int nresults,
int msgh,
lua_KContext ctx,
lua_KFunction k);
这个函数的行为和 lua_pcall 完全一致,只不过它还允许被调用的函数让出

lua调c

注册c函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
typedef int (*lua_CFunction) (lua_State *L); //每个函数有自己的局部私有栈
C 函数的类型。
为了正确的和 Lua 通讯, C 函数必须使用下列协议。 这个协议定义了参数以及返回值传递方法: C 函数通过 Lua 中的栈来接受参数, 参数以正序入栈(第一个参数首先入栈)。 因此,当函数开始的时候, lua_gettop(L) 可以返回函数收到的参数个数。 第一个参数(如果有的话)在索引 1 的地方, 而最后一个参数在索引 lua_gettop(L) 处。 当需要向 Lua 返回值的时候, C 函数只需要把它们以正序压到堆栈上(第一个返回值最先压入), 然后返回这些返回值的个数。 在这些返回值之下的,堆栈上的东西都会被 Lua 丢掉。 和 Lua 函数一样,从 Lua 中调用 C 函数也可以有很多返回值。
void lua_pushcfunction (lua_State *L, lua_CFunction f); : //注册c函数 lua_pushcfunction(L, I_sin); lua_setglobal(L, "mysin");
将一个 C 函数压栈。 这个函数接收一个 C 函数指针, 并将一个类型为 function 的 Lua 值压栈。 当这个栈顶的值被调用时,将触发对应的 C 函数。
注册到 Lua 中的任何函数都必须遵循正确的协议来接收参数和返回值 (参见 lua_CFunction )。
lua_pushcfunction 是作为一个宏定义出现的:
#define lua_pushcfunction(L,f) lua_pushcclosure(L,f,0)
void lua_register (lua_State *L, const char *name, lua_CFunction f); //注册c函数
把 C 函数 f 设到全局变量 name 中。 它通过一个宏定义:
#define lua_register(L,n,f) \
(lua_pushcfunction(L, f), lua_setglobal(L, n))
typedef struct luaL_Reg {
const char *name;
lua_CFunction func;
} luaL_Reg;
用于 luaL_setfuncs 注册函数的数组类型。 name 指函数名,func 是函数指针。 任何 luaL_Reg 数组必须以一对 name 与 func 皆为 NULL 结束。

c模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//in c
static int I_dir (lua_State *L) {
//
}
static const struct luaL_Reg mylib[] = {
{"dir", I_dir},
{NULL, NULL} //结尾
}
int luaopen_mylib (lua_State *L) {
luaL_register(L, "mylib", mylib);
return 1
}
// in lua
require "mylib"

package.searchers

用于 require 控制如何加载模块的表。

这张表内的每一项都是一个 查找器函数。 当查找一个模块时, require 按次序调用这些查找器, 并传入模块名(require 的参数)作为唯一的一个参数。 此函数可以返回另一个函数(模块的 加载器)加上另一个将传递给这个加载器的参数。 或是返回一个描述为何没有找到这个模块的字符串 (或是返回 nil 什么也不想说)。

Lua 用四个查找器函数初始化这张表。

第一个查找器就是简单的在 package.preload 表中查找加载器。

第二个查找器用于查找 Lua 库的加载库。 它使用储存在 package.path 中的路径来做查找工作。 查找过程和函数 package.searchpath 描述的一致。

第三个查找器用于查找 C 库的加载库。 它使用储存在 package.cpath 中的路径来做查找工作。 同样, 查找过程和函数 package.searchpath 描述的一致。 例如,如果 C 路径是这样一个字符串

"./?.so;./?.dll;/usr/local/?/init.so"

查找器查找模块 foo 会依次尝试打开文件 ./foo.so,./foo.dll, 以及 /usr/local/foo/init.so。 一旦它找到一个 C 库, 查找器首先使用动态链接机制连接该库。 然后尝试在该库中找到可以用作加载器的 C 函数。 这个 C 函数的名字是 “luaopen_” 紧接模块名的字符串, 其中字符串中所有的下划线都会被替换成点。 此外,如果模块名中有横线, 横线后面的部分(包括横线)都被去掉。 例如,如果模块名为 a.b.c-v2.1, 函数名就是 luaopen_a_b_c。

第四个搜索器是 一体化加载器。 它从 C 路径中查找指定模块的根名字。 例如,当请求 a.b.c 时, 它将查找 a 这个 C 库。 如果找得到,它会在里面找子模块的加载函数。 在我们的例子中,就是找 luaopen_a_b_c。 利用这个机制,可以把若干 C 子模块打包进单个库。 每个子模块都可以有原本的加载函数名。

除了第一个(预加载)搜索器外,每个搜索器都会返回 它找到的模块的文件名。 这和 package.searchpath 的返回值一样。 第一个搜索器没有返回值。

参考

http://cloudwu.github.io/lua53doc/manual.html

Comments

May 21 2016