当前位置: 首页 > 工具软件 > OpenLua > 使用案例 >

lua5.3源码基础阅读(luaL_openlibs)

侯善
2023-12-01

###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}
};

lua_newlib(L,l)

单个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

前面的库导入过程中,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
  }
}
 类似资料: