lua c api - registry
Lua
C, Lua
字数统计: 1.4k(字)
阅读时长: 6(分)
一 registry
registry
是一个单独的表,用来保存 lua
中的全局变量,同时 lua
代码不能直接访问(只能通过 c
函数接口访问)。registry
通过伪索引(LUA_REGISTRYINDEX
)访问,伪索引类似于栈上的索引,但是它的关联数据并未在栈中。
registry
的存在是用来解决一个问题:在 c
动态库中如何保存一个 lua
状态机的全局变量。如果使用 c
的全局变量、静态变量,相同进程内的所有 lua
状态机会共享此变量。如果将值保存在 lua
中的全局 table
中,lua
代码是可以直接访问、修改的;没有限制措施,数据被修改后非常容易导致 c
动态库崩溃。
使用 registry
可以实现针对每个 state
保存全局变量,同时不同动态库直接可以通过约定的 key
从 registry
中获得值,进行交互。
如果动态库(.so
)不希望其他库修改自己设置的全局变量,一个好的方法是使用全局变量的地址作为 key
。此时需要取全局变量地址,并转换为 lightuserdata
作为 registry
的 key
。
1. 示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
void state_store_global_var(lua_State *state, char *key, char *value) { if (!state||!key||!value) return;
lua_pushstring(state, key); lua_pushstring(state, value); lua_settable(state, LUA_REGISTRYINDEX); }
|
二 references
如果想在 registry
中添加记录而又不想费劲考虑如何分配 key
,可以使用 reference
系统。reference
系统可以自动生成一个唯一 key
。
1
| int r = luaL_ref(L, LUA_REGISTRYINDEX);
|
这个函数会从栈顶弹出一个元素,并保存到 registry
中,同时返回唯一数值 r
作为索引。数值 r
就叫做栈顶值的“引用”(reference)。
从这可以看出,用户不能在 registry
中使用数值作为 key
,避免影响 reference
系统。
在 c
中无法直接使用指向 lua
的 table
、function
类型指针(未提供此类接口),我们可以使用引用实现类似指针的功能。假设上面的例子中栈顶元素为 table
,我们已经创建了它的一个引用,下面我们将其再次放入栈顶:
1
| lua_rawgeti(L, LUA_REGISTRYINDEX, r);
|
在引用使用结束后,我们需要使用 luaL_unref
释放引用值与引用自身:
1
| luaL_unref(L, LUA_REGISTRYINDEX, r);
|
三 Upvalues
Upvalue
存在的目的是为了在 c
中实现闭包功能。Upvalue
仅在函数内可见,并且在每个函数中是相互独立的。示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| static int counter(lua_State *L);
int newCounter(lua_State *L) { lua_pushnumber(L, 0); lua_pushcclosure(L, &counter, 1); return 1; }
static int counter(lua_State *L) { double val = lua_tonumber(L, lua_upvalueindex(1)); lua_pushnumber(L, ++val); lua_pushvalue(L, -1); lua_replace(L, lua_upvalueindex(1)); return 1; }
|
1. 闭包示例代码
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 39 40 41 42 43 44 45 46
|
#include <stdio.h> #include <stdlib.h> #include <unistd.h>
#include "lua.h" #include "lauxlib.h" #include "lualib.h"
static int counter(lua_State *L) { double val = lua_tonumber(L, lua_upvalueindex(1)); lua_pushnumber(L, ++val); lua_pushvalue(L, -1); lua_replace(L, lua_upvalueindex(1)); return 1; }
static int newCounter(lua_State *L) { lua_pushnumber(L, 0); lua_pushcclosure(L, &counter, 1); return 1; }
static const struct luaL_reg counter_lib[] = { {"newCounter", newCounter}, {NULL, NULL} };
int luaopen_counter(lua_State *l) { luaL_openlib(l, "counter", counter_lib, 0); return 1; }
|
newCounter
在 C
中创建一个闭包,返回给 lua
使用。
1 2 3 4 5 6 7 8 9
|
require "counter" c1 = counter.newCounter() print(type(c1)) c2 = counter.newCounter() print(c1()) print(c1()) print(c2())
|
2. 程序说明
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
| #define luaI_openlib luaL_openLib typedef struct luaL_Reg { const char *name; lua_CFunction func; } luaL_Reg;
LUALIB_API void (luaL_register) (lua_State *L, const char *libname, const luaL_Reg *l) { luaI_openlib(L, libname, l, 0); } LUALIB_API void luaI_openlib (lua_State *L, const char *libname, const luaL_Reg *l, int nup) { if (libname) { int size = libsize(l);
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); lua_getfield(L, -1, libname); if (!lua_istable(L, -1)) { lua_pop(L, 1);
if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL) luaL_error(L, "name conflict for module " LUA_QS, libname); lua_pushvalue(L, -1); lua_setfield(L, -3, libname); } lua_remove(L, -2); lua_insert(L, -(nup+1)); } for (; l->name; l++) { int i; for (i=0; i<nup; i++) lua_pushvalue(L, -nup); lua_pushcclosure(L, l->func, nup); lua_setfield(L, -(nup+2), l->name); } lua_pop(L, nup); }
|
luaL_register\luaI_openlib\luaL_openlib
三个函数都提供了注册函数列表到 table
的功能。luaL_register
是通过调用 luaI_openlib
函数来实现,提供了无闭包函数的功能。luaI_openlib
与 luaL_openlib
是同一个函数都会将函数注册到 table
中,nup
指定”上值“数量。
luaI_openlib
思路是操作栈,将需要注册的函数保存到栈底的 table
中,函数执行完毕后栈底保留原先 table
。
四 参考链接