枚举名 | 状态 |
---|---|
LUA_OK | 正常 |
LUA_YIELD | 挂起中 |
LUA_ERRRUN | 运行错误 |
LUA_ERRSYNTAX | 语法错误 |
LUA_ERRMEM | 内存错误 |
LUA_ERRGCMM | 内存回收错误 |
LUA_ERRERR | 错误系统错误 |
LUA_ERRFILE | 文件错误(扩展) |
Lua共8种类型
枚举名 | Lua类型 |
---|---|
LUA_TNIL | nil |
LUA_TBOOLEAN | bool |
LUA_TLIGHTUSERDATA | userdata(light) |
LUA_TNUMBER | number |
LUA_TSTRING | string |
LUA_TTABLE | table |
LUA_TFUNCTION | function |
LUA_TUSERDATA | userdata(full) |
LUA_TTHREAD | thread |
枚举名 | 算符 |
---|---|
LUA_OPADD | + |
LUA_OPSUB | - |
LUA_OPMUL | * |
LUA_OPDIV | / |
LUA_OPIDIV | // |
LUA_OPMOD | % |
LUA_OPPOW | ^ |
LUA_OPUNM | - |
LUA_OPBNOT | ~ |
LUA_OPBAND | & |
LUA_OPBOR | | |
LUA_OPBXOR | ~ |
LUA_OPSHL | << |
LUA_OPSHR | >> |
枚举名 | 算符 |
---|---|
LUA_OPEQ | = |
LUA_OPLT | < |
LUA_OPLE | <= |
lua_Number
: double
lua_Integer
: long long
lua_Unsigned
: unsigned long long
lua_KContext
: ptrdiff_t
lua_CFunction
: int(*)(lua_State*)
lua_KFunction
: int(*)(lua_State*, int status, lua_KContext ctx)
lua_Reader
: const char*(*)(lua_State*, void*ud, size_t *sz)
lua_Writer
: int(*)(lua_State*, const void*p, size_t sz, void*ud)
lua_Alloc
: void*(void*ud, void*ptr, size_t osize, size_t nsize)
初始化后栈内无内容.
可用索引存取栈中值, 设栈中有n个值, 则1引用栈底值, n引用栈顶值, -1引用栈顶值, -n引用栈底值. 其他值是非法值.
与Lua的交互通过栈进行, 每个函数都有入栈出栈和异常的栈注释.
[-a, +b, c]
a
和b
的注释:
a
: 出栈数b
: 入栈数?
: 入栈/出栈数未知x/y
: 入栈/出栈x
或y
个c
的注释:
-
: 无异常m
: 可能抛出Out-of-memory异常e
: 可能抛出任何异常v
: 将故意抛出异常函数将出栈a
个值, 然后入栈b
个值, 或者抛出异常.
下面在函数中使用这几个记号
记号 | 注释 |
---|---|
L | Lua状态机 |
n | 字符串 |
i | 索引 |
T | 类型码(实际为int) |
op | 算符码(实际为int) |
cop | 比较算符码(实际为int) |
b | bool(实际为int, 1=true, 0=false) |
s | size_t或int |
cf | C函数 |
kf | 协程函数 |
na | 参数数 |
nr | 返回值数 |
alloc | 内存分配器 |
ud | void*任意数据 |
^ | 被Lua托管的值, 需尽快使用以免被Lua释放 |
&* | 指针 |
函数注释中用 S 表示栈, U 表示闭包栈.
S[1] 表示栈底, S[-1] 表示栈顶. S.push(v) 表示入栈, S.pop() 表示出栈.
栈注释 | 函数名 | 参数 | 返回值 | 注释 |
---|---|---|---|---|
[-0, +0, -] | lua_newstate | (alloc, ud) | L | |
[-0, +0, -] | luaL_newstate | () | L | |
[-0, +0, -] | lua_close | (L) | ||
[-0, +1, m] | lua_newthread | (L) | L | 建立共享变量的独立栈 |
栈注释 | 函数名 | 参数 | 返回值 | 注释 |
---|---|---|---|---|
[-0, +0, -] | lua_status | (L) | 状态码 | |
[-0, +0, -] | lua_version | (L) | *lua_Number | 版本号 |
[-0, +0, m] | lua_gc | (L,gc,int) | int | 垃圾回收 |
仅LUA_OK时可调用函数, 仅LUA_OK(启动新协程)和LUA_YIELD(恢复协程)时可恢复
栈注释 | 函数名 | 参数 | 返回值 | 注释 |
---|---|---|---|---|
[-0, +0, -] | lua_absindex | (L,i) | i | 获取绝对索引 |
[-0, +0, -] | lua_gettop | (L) | i | 栈顶值索引 栈中值数 0则栈空 |
[-?, +?, -] | lua_settop | (L,i) | 设定栈中值数 变多则用nil填充 变少则删除值 0则删除所有值 |
|
[-0, +1, -] | lua_pushvalue | (L,i) | S.push(S[i]) 复制 |
|
[-0, +0, -] | lua_copy | (L,i1,i2) | S[i2] = S[i1] 复制替换 |
|
[-0, +0, -] | lua_checkstack | (L,s) | b | 检查栈是否还有 足够空间容纳s个值 |
L1 [-s, +0, -] L2 [-0, +s, -] |
lua_xmove | (L1,L2,s) | L2.push(L1.pop()) …(s次) L2.push(L1.pop()) 跨栈移值 |
|
[-n, +0, -] | lua_pop | (L,s) | 出栈s个 |
栈注释 | 函数名 | 参数 | 返回值 | 注释 |
---|---|---|---|---|
[-1, +0, -] | lua_replace | (L,i) | S[i] = S.pop() | |
[-1, +0, -] | lua_remove | (L,i) | S[i] = S[i+1] S[i+1] = S[i+2] … S[-2] = S[-1] S.pop() 不支持伪栈索引 |
|
[-1, +1, -] | lua_insert | (L,i) | t = S[-1] S[-1] = S[-2] S[-2] = S[-3] … S[i+1] = S[i] S[i] = t 不支持伪栈索引 |
栈注释 | 函数名 | 参数 | 返回值 | 注释 |
---|---|---|---|---|
[-0, +1, m] | lua_createtable | (L,narr,nrec) | S.push({}) 建立长度narr, 容量nrec的表 |
|
[-0, +1, m] | lua_newtable | (L) | S.push({}) | |
[-0, +1, m] | lua_newuserdata | (L,s) | ud | S.push(malloc(s)) 内存由Lua管理 |
跨域快速交换函数 (C -> Lua)
栈注释 | 函数名 | 参数 | 返回值 | 注释 |
---|---|---|---|---|
[-0, +0, e] | lua_register | (L,n,cf) | _G.n = cf |
栈注释 | 函数名 | 参数 | 返回值 | 注释 |
---|---|---|---|---|
[-0, +1, e] | lua_getglobal | (L,n) | T | S.push(_G.n) |
[-1, +1, e] | lua_gettable | (L,i) | T | S.push(S[i][S[-1]]) |
[-0, +1, e] | lua_getfield | (L,i,n) | T | S.push(S[i][n]) 或调用__index函数 |
[-0, +1, e] | lua_geti | (L,i,int) | T | S.push(S[i][int]) |
[-1, +1, e] | lua_rawget | (L,i) | T | S.push(S[i][S[-1]]) |
[-0, +1, e] | lua_rawgetp | (L,i,n) | T | S.push(S[i][n]) 不调用__index函数 |
[-0, +1, e] | lua_rawgeti | (L,i,int) | T | S.push(S[i][int]) |
[-0, +(0/1), -] | lua_getmetatable | (L,i) | b | 若S[i]有meta表: S.push(S[i]的meta表) |
[-0, +1, -] | lua_getuservalue | (L,i) | T | S.push(getuservalue(S[i])) 获取S[i](限userdata)的uservalue |
栈注释 | 函数名 | 参数 | 返回值 | 注释 |
---|---|---|---|---|
[-0, +0, -] | lua_toboolean | (L,i) | b | S[i] != false && S[i] != nil |
[-0, +0, -] | lua_tocfunction | (L,i) | cf | 非Lua_CFunction返回NULL |
[-0, +0, -] | lua_tointeger | (L,i) | lua_Integer | S[i]为有效string/number 其他返回0 |
[-0, +0, -] | lua_tointegerx | (L,i,&isnum) | Lua_Integer | S[i]为有效string/number 失败时返回0 |
[-0, +0, -] | lua_tonumber | (L,i) | Lua_Number | S[i]为有效string/number 其他返回0 |
[-0, +0, -] | lua_tonumberx | (L,i,&isnum) | Lua_Number | S[i]为有效string/number 失败时返回0 |
[-0, +0, m] | lua_tostring | (L,i) | ^n | 若S[i]不为number或string 则返回NULL |
[-0, +0, m] | lua_tolstring | (L,i, &len) | ^n | 若S[i]不为number或string 则返回NULL |
[-0, +0, -] | lua_tothread | (L,i) | L | |
[-0, +0, -] | lua_touserdata | (L,i) | ud |
栈注释 | 函数名 | 参数 | 返回值 | 注释 |
---|---|---|---|---|
[-0, +0, -] | lua_isnumber | (L,i) | b | |
[-0, +0, -] | lua_isstring | (L,i) | b | |
[-0, +0, -] | lua_iscfunction | (L,i) | b | 是否为C函数 |
[-0, +0, -] | lua_isinteger | (L,i) | b | |
[-0, +0, -] | lua_isuserdata | (L,i) | b | |
[-0, +0, -] | lua_type | (L,i) | T | |
[-0, +0, -] | lua_typename | (L,T) | n |
栈注释 | 函数名 | 参数 | 返回值 | 注释 |
---|---|---|---|---|
[-0, +1, -] | lua_pushnil | (L) | S.push(nil) | |
[-0, +1, -] | lua_pushnumber | (L,number) | S.push(number) | |
[-0, +1, -] | lua_pushinteger | (L,int) | S.push(int) | |
[-0, +1, m] | lua_pushlstring | (L,n,s) | ^n | S.push(n) 复制n,返回内部字符串 |
[-0, +1, m] | lua_pushstring | (L,n) | ^n | S.push(n) 复制n,返回内部字符串 |
[-0, +1, m] | lua_pushliteral | (L,n) | ^n | S.push(n) 复制n,返回内部字符串 限字面量字符串用 |
[-0, +1, e] | lua_pushfstring | (L,n,…) | ^n | S.push(格式化n), 返回内部字符串 |
[-0, +1, e] | lua_pushvfstring | (L,n,va_list) | ^n | S.push(格式化n), 返回内部字符串 |
[-s, +1, m] | lua_pushcclosure | (L,cf,s) | 将S[-1]~S[-s]的值作为 cf的闭包值制作闭包 |
|
[-0, +1, -] | lua_pushcfunction | (L,cf) | S.push(cf) | |
[-0, +1, -] | lua_pushboolean | (L,b) | S.push(b) | |
[-0, +1, -] | lua_pushlightuserdata | (L,ud) | S.push(ud) | |
[-0, +1, -] | lua_pushthread | (L) | S.push(L) | |
[-0, +1, -] | lua_pushglobaltable | (L) | S.push(_G) |
栈注释 | 函数名 | 参数 | 返回值 | 注释 |
---|---|---|---|---|
[-1, +0, e] | lua_setglobal | (L, n) | _G.n = S.pop() | |
[-2, +0, e] | lua_settable | (L,i) | S[i][S[-2]] = S[-1] S.pop(), S.pop() |
|
[-1, +0, e] | lua_setfield | (L,i,n) | S[i][n] = S.pop() | |
[-1, +0, e] | lua_seti | (L,i,int) | S[i][int] = S.pop() | |
[-2, +0, e] | lua_rawset | (L,i) | S[i][S[-2]] = S[-1] S.pop(), S.pop() |
|
[-1, +0, e] | lua_rawsetp | (L,i,n) | S[i][n] = S.pop() | |
[-1, +0, e] | lua_rawseti | (L,i,int) | S[i][int] = S.pop() | |
[-1, +0, -] | lua_setmetatable | (L,i) | S[i]的meta表 = S.pop() | |
[-1, +0, -] | lua_setuservalue | (L,i) | setuservalue(S[i], S.pop()) |
栈注释 | 函数名 | 参数 | 返回值 | 注释 |
---|---|---|---|---|
[-(2/1), +1, e] | lua_arith | (L,op) | S.push(S.pop() op S.pop()) | |
[-0, +0, -] | lua_rawequal | (L,i1,i2) | b | S[i1] === S[i2] 不调用__eq |
[-0, +0, e] | lua_compare | (L,i1,i2,cop) | b | S[i1] cop S[i2] |
[-1, +0, v] | lua_error | (L) | - | 用S.pop()构建异常并 做longjmp,永不返回 |
[-1, +(2/0), e] | lua_next | (L,i) | b | k, v = S[i][++S.pop()] S.push(k) S.push(v) kv是S.pop()的下一键值对 |
[-n, +1, e] | lua_concat | (L,s) | S.push(S.pop()…S.pop()… … …S.pop()) | |
[-0, +1, e] | lua_len | (L,i) | S.push(#S[i]) 或触发length函数 |
|
[-0, +1, -] | lua_stringtonumber | (L,n) | s | 字符串转数值,失败时返回0 |
栈注释 | 函数名 | 参数 | 返回值 | 注释 |
---|---|---|---|---|
[-(na + 1), +(nr/?), e] | lua_call | (L,na,nr) | 见调用协议 | |
[-(na + 1), +nr, e] | lua_callk | (L,na,nr, kctx,kf) |
||
[-(na + 1), +(nr/1), -] | lua_pcall | (L,na,nr,i) | 状态码 | i不能为伪栈索引 见调用协议 |
[-(na + 1), +(nr/1), -] | lua_pcallk | (L,na,nr,i, kctx,kf) |
状态码 | i不能为伪栈索引 见调用协议 |
有函数
function f(a1, a2, a3)
...
return r1, r2, r3
end
lua_call(L,na,nr) 按以下协议调用该函数, 栈注释 [-(na + 1), +nr, e], na=3, nr=3
S.push(f)
S.push(a1)
S.push(a2)
S.push(a3)
lua_call(L,na,nr)
r1 = S.pop()
r2 = S.pop()
r3 = S.pop()
nr 为LUA_MULTRET(-1)时, 按实际返回的参数入栈, 此时栈注释 [-(na + 1), +?, e]
S[i]应为 异常处理器 类同lua_call(L,na,nr), 栈注释 [-(na + 1), +(nr/?), -], 只在函数出现异常时有区别
函数调用出现异常时, 会将异常作为参数调用S[i], S[i]的返回值作为函数调用的返回值. 栈注释 [-(na + 1), +1, -].
lua_CFunction 类型为 int(*)(lua_State*L)
设Lua用该代码调用C函数
r1, r2, r3 = f(a1, a2, a3)
C函数的参数L的栈内数据为
返回时只需保证栈顶是需要返回的值, 然后C函数返回返回值的数量即可
或者抛出异常
栈注释 | 函数名 | 参数 | 返回值 | 注释 |
---|---|---|---|---|
L1 [-?, +?, -] L2 [-?, +?, -] |
lua_resume | (L1,L2,na,&nr) | 状态码 | 将L2作为协程栈恢复协程 返回运行结束后协程的状态 |
启动协程需要一个普通的lua状态机(最好是空的), 所谓普通的lua状态机即 lua_status(L)
返回 LUA_OK 的状态机.
首先栈底压入 协程启动函数, 然后按顺序压入启动参数, 最后调用 lua_resume 启动协程.
co.S.push(cf)
co.S.push(a1)
co.S.push(a2)
co.S.push(a3)
lua_resume(co, NULL, 3) // 第二个参数可选 为协程的外部环境, 用于保存错误信息
协程第一次挂起之后, lua_resume的调用即返回, 返回值为co的状态码, co中只剩下协程第一次挂起时返回的值. 而 lua_status(co)
应返回 LUA_YIELD.
恢复协程需要一个挂起的Lua状态机. 所谓挂起的Lua状态机即 lua_status(co)
返回 LUA_YIELD 的状态机.
恢复时可选择发送任意值给协程.
将 co 清空, 然后将要发送的值压入栈即可.
lua_settop(co, 0);
co.S.push(re1)
co.S.push(re2)
co.S.push(re3)
lua_resume(co, NULL, -) // 第三个参数无效, 可为任意值
协程可能因挂起而退出, 也可能因返回而退出
挂起退出时, lua_resume 返回 LUA_YIELD, 返回退出时返回 LUA_OK.
返回 LUA_OK, 即说明co回归成一个普通的Lua状态机. 其栈中的值为返回值. 否则为挂起值.
协程由一个lua_CFunction型的启动函数和多个lua_KFunction型的k函数组成.
启动函数中须使用lua_yieldk指定k函数做一次挂起, 否则该协程才刚创建即刻就销毁.
使用 lua_resume 启动协程后, 将马上调用启动函数, 启动函数内应调用 lua_yield, 否则该协程将弱化为普通的函数调用.
可使用 lua_yieldk(co, nr, ctx, kf) 中的kf指定下一次协程恢复时调用的恢复函数, Lua不对ctx有任何功能, Lua只负责在协程函数间传递ctx.
未完待续
int RangeKFunc(lua_State* L, int state, lua_KContext kctx) {
switch(kctx) {
case 0:
EXPECT_EQ(lua_gettop(L), 2);
lua_pushvalue(L, 1);
if(lua_compare(L, 2, 3, LUA_OPEQ) == 0) {
lua_pushvalue(L, 3);
lua_yieldk(L, 1, 2, RangeKFunc);
} else {
return 0;
}
case 2:
EXPECT_EQ(lua_gettop(L), 3);
lua_pushinteger(L, 1);
lua_arith(L, LUA_OPADD);
case 1:
EXPECT_EQ(lua_gettop(L), 3);
if(lua_compare(L, 2, 3, LUA_OPEQ) == 0) {
lua_pushvalue(L, 3);
lua_yieldk(L, 1, 2, RangeKFunc);
} else {
return 0;
}
}
return 0;
}
int RangeEnter(lua_State* L) {
lua_yieldk(L, 0, 0, RangeKFunc);
return 0;
}
int RangeGene2(lua_State* L) {
lua_Integer a = luaL_checkinteger(L, 1);
lua_Integer b = luaL_checkinteger(L, 2);
lua_State* co = lua_newthread(L);
lua_pushcfunction(co, RangeEnter);
lua_pushinteger(co, a);
lua_pushinteger(co, b);
lua_resume(co, L, 2);
return 1;
}
使用如下代码产生协程对象, 并用C遍历生成器.
lua_pushcfunction(L, RangeGene2);
lua_pushinteger(L, 4);
lua_pushinteger(L, 7);
lua_call(L, 2, 1);
lua_State* co = lua_tothread(L, 1);
EXPECT_EQ(lua_status(co), LUA_YIELD);
EXPECT_EQ(lua_resume(co, L, 0), LUA_YIELD);
EXPECT_EQ(lua_gettop(co), 1);
EXPECT_EQ(luaL_checkinteger(co, 1), 4);
lua_settop(co, 0);
EXPECT_EQ(lua_resume(co, L, 0), LUA_YIELD);
EXPECT_EQ(lua_gettop(co), 1);
EXPECT_EQ(luaL_checkinteger(co, 1), 5);
lua_settop(co, 0);
EXPECT_EQ(lua_resume(co, L, 0), LUA_YIELD);
EXPECT_EQ(lua_gettop(co), 1);
EXPECT_EQ(luaL_checkinteger(co, 1), 6);
lua_settop(co, 0);
EXPECT_EQ(lua_resume(co, L, 0), LUA_OK);
EXPECT_EQ(lua_gettop(co), 0);
EXPECT_EQ(lua_status(co), LUA_OK);
lua_settop(L, 0);
亦可用如下Lua代码遍历生成器:
lua_pushcfunction(L, RangeGene2);
lua_pushinteger(L, 4);
lua_pushinteger(L, 7);
lua_call(L, 2, 1);
lua_setglobal(L, "r2");
const char* s =
"print(coroutine.resume(r2))"
"print(coroutine.resume(r2))"
"print(coroutine.resume(r2))"
"print(coroutine.resume(r2))"
"print(coroutine.resume(r2))"
;
luaL_dostring(L, s);
运行生成
true 4
true 5
true 6
true
false cannot resume dead coroutine
栈注释 | 函数名 | 参数 | 返回值 | 注释 |
---|---|---|---|---|
[-0, +0, -] | lua_upvalueindex | (i) | i | 闭包栈索引转伪栈索引 |
未完待续