###C库结构和导入到lua
导入全局性的库到lua中,这些库由C实现:
/* ** these libs are loaded by lua.c and are readily available to any Lua ** program */ static const luaL_Reg loadedlibs[] = { {"_G", luaopen_base}, {LUA_LOADLIBNAME, luaopen_package}, {LUA_COLIBNAME, luaopen_coroutine}, //.... {NULL,NULL} }; LUALIB_API void luaL_openlibs (lua_State *L) { const luaL_Reg *lib; /* "require" functions from 'loadedlibs' and set results to global table */ for (lib = loadedlibs; lib->func; lib++) { luaL_requiref(L, lib->name, lib->func, 1); lua_pop(L, 1); /* remove lib */ } }
每一个库封装了很多函数, 且每个库都由库名和open函数导入,如以协程人库为例:
{LUA_COLIBNAME, luaopen_coroutine},
通过看协程的库的创建过程可以知道如何将C函数写的库导入lua:
// 每个库必须有的open函数:luaopen_name(..) // newlib的实现就是一个table LUAMOD_API int luaopen_coroutine (lua_State *L) { luaL_newlib(L, co_funcs); return 1; } //下面是协程库的lua函数名和对应的C函数: static const luaL_Reg co_funcs[] = { {"create", luaB_cocreate}, {"resume", luaB_coresume}, {"running", luaB_corunning}, {"status", luaB_costatus}, {"wrap", luaB_cowrap}, {"yield", luaB_yield}, {"isyieldable", luaB_yieldable}, {NULL, NULL} };
单个C函数组成的库的open函数里, 调用的是下面的newlib函数luaL_newlib(L, co_funcs);
, 实现如下:
// 根据库函数数组 luaL_Reg的大小创建的table, 这里的createtable的实现就是在栈中创建一个哈希表, 表元素个数为sizeof(l)/sizeof((l)[0]) - 1 #define luaL_newlibtable(L,l) \ lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) // // 库的实现就是以l的大小创建了一个table #define luaL_newlib(L,l) \ (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
下面是上面调用的luaL_setfuncs函数的实现代码, 由于当前的栈顶是刚才newlibtable出来的table, 所以现在是将库函数名set到table中;
下面的实现可以看出, 每个函数创建的闭包前都先复制了一份闭包, 这样所有的库函数的闭包是一样的;
checkstack函数是检查当前的栈空间是否足够放得下nup个闭包元素;
lua_pushvalue()就是将索引处的值复制到栈顶/* ** set functions from list 'l' into table at top - 'nup'; each ** function gets the 'nup' elements at the top as upvalues. ** Returns with only the table at the stack. */ LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { // nup是闭包元素的个数 luaL_checkstack(L, nup, "too many upvalues"); // 如果空间不够会自动扩展栈空间 for (; l->name != NULL; l++) { /* fill the table with given functions */ int i; for (i = 0; i < nup; i++) /* copy upvalues to the top */ lua_pushvalue(L, -nup); // 压入所有的闭包, 当前栈顶(新的table)下的元素是nup个的闭包 lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ lua_setfield(L, -(nup + 2), l->name); } lua_pop(L, nup); /* remove upvalues */ }
下面看下luaL_checkstack调用的check函数, ci为当前栈中的调用的函数帧, 可以看成函数的局部空间, ci->func为底, ci->top为空间顶, 两者之间就是当前函数的局部空间:
// const int extra = LUA_MINSTACK; 5个额外的空间 // 调用的是该: lua_checkstack(L, space + extra)) .. LUA_API int lua_checkstack (lua_State *L, int n) { int res; CallInfo *ci = L->ci; // 当前的函数调用帧, ci->func为函数调用点 lua_lock(L); api_check(L, n >= 0, "negative 'n'"); if (L->stack_last - L->top > n) /* stack large enough? */ res = 1; /* yes; check is OK */ //空间足够 else { /* no; need to grow stack */ // 空间不够,需要增加栈空间 int inuse = cast_int(L->top - L->stack) + EXTRA_STACK; if (inuse > LUAI_MAXSTACK - n) /* can grow without overflow? */ res = 0; /* no */ else /* try to grow stack */ res = (luaD_rawrunprotected(L, &growstack, &n) == LUA_OK); } if (res && ci->top < L->top + n) ci->top = L->top + n; /* adjust frame top */ // 调用帧顶为栈顶+所需空间 lua_unlock(L); return res; }
前面的库导入过程中,luaL_requiref是真正的导入函数,实现如下, 说明符中将该函数表示为简化版的require, 完成了三件事:
1. 在全局表的_LOADED中查找有没有modulename对应的值;
2. 没有则压入库open函数和库名, 并调用open函数在栈中创建table, 将该table以key为modulename存入_LOADED全局表中;
3. 如果glb为真,即要求放入全局表中, 则放如全局, _G[modulename] = module该函数的功能和require的功能类似, 都会检查_loaded表, 没有则导入到表中取, 一共使用;
/* ** Stripped-down 'require': After checking "loaded" table, calls 'openf' ** to open a module, registers the result in 'package.loaded' table and, ** if 'glb' is true, also registers the result in the global table. ** Leaves resulting module on the top. */ LUALIB_API void luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int glb) { // 全局注册表找到_loaded表 luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED"); lua_getfield(L, -1, modname); /* _LOADED[modname] */ if (!lua_toboolean(L, -1)) { /* package not already loaded? */ lua_pop(L, 1); /* remove field */ lua_pushcfunction(L, openf); lua_pushstring(L, modname); /* argument to open function */ //调用库的open函数,在栈中创建了一个table lua_call(L, 1, 1); /* call 'openf' to open module */ // 复制一份以保存到_loaded里面取 lua_pushvalue(L, -1); /* make copy of module (call result) */ lua_setfield(L, -3, modname); /* _LOADED[modname] = module */ } lua_remove(L, -2); /* remove _LOADED table */ if (glb) { // 复制一份保存到_G里面去 lua_pushvalue(L, -1); /* copy of module */ lua_setglobal(L, modname); /* _G[modname] = module */ 是_G } }