目录

一、扩展库 - Open扩展库的配置

二、扩展库 - 扩展库Lib的配置详解

三、扩展库 - 基础库Base的配置详解


上一章,我们讲解了如何将Require进来。本章节具体将一下扩展库启动的实现。

一、扩展库 - Open扩展库的配置


启动一个扩展库,首先需要进行两个基础配置:loadedlibs数组配置 & 库名称和回调函数的定义(lualib.h)

  • loadedlibs是一个二维数组。第一个参数为库名称,第二个参数库open函数。
  • loadedlibs数组中,第一个对象为全局基础方法聚合(luaopen_base)。常见的loadfile函数等都在这上面配置,在lua语言中,直接使用函数即可,无需使用库名称.方法名称(例如:string.find)方式
  • 数组最后一个对象,为{NULL,NULL},主要用于在循环的时候遍历退出。
//---------------------linit.c
/*
** these libs are loaded by lua.c and are readily available to any Lua
** program
** LUA标准库常量
**
** 定义标准库名称 & 启动的方法
*/
static const luaL_Reg loadedlibs[] = {
  {"_G", luaopen_base},
  {LUA_LOADLIBNAME, luaopen_package},
  {LUA_COLIBNAME, luaopen_coroutine},
  {LUA_TABLIBNAME, luaopen_table},
  {LUA_IOLIBNAME, luaopen_io},
  {LUA_OSLIBNAME, luaopen_os},
  {LUA_STRLIBNAME, luaopen_string},
  {LUA_MATHLIBNAME, luaopen_math},
  {LUA_UTF8LIBNAME, luaopen_utf8},
  {LUA_DBLIBNAME, luaopen_debug},
#if defined(LUA_COMPAT_BITLIB)
  {LUA_BITLIBNAME, luaopen_bit32},
#endif
  {NULL, NULL}
};
//......................lualib.h

LUAMOD_API int (luaopen_base) (lua_State *L);

#define LUA_COLIBNAME	"coroutine"
LUAMOD_API int (luaopen_coroutine) (lua_State *L);

#define LUA_TABLIBNAME	"table"
LUAMOD_API int (luaopen_table) (lua_State *L);

#define LUA_IOLIBNAME	"io"
LUAMOD_API int (luaopen_io) (lua_State *L);

二、扩展库 - 扩展库Lib的配置详解


我们以协程coroutine的扩展库作为例子来看。扩展库实现重点需要分三个部分:函数配置数组,扩展库加载方法,函数实现。

  • 函数配置数组:主要定义了函数名称和对应的C语言函数。数组最后一个对象为{NULL,NULL},因为数组遍历的时候会根据NULL做判断进行弹出。
  • 扩展库加载方法:入参为L,对当前的线程栈。方法内容必须实现luaL_newlib。该函数主要将配置数组中的方法,遍历注册到一个module[funcname]=func数组上。该module数组,作为加载方法openf的结果放入栈上。
  • 函数实现:每个函数的入参都为lua_State *L,为当前线程栈。

大家可能会有疑问,为何每个函数入参都会L,那在lua中调用的时候如何进行多个参数入参。实际上,lua语言还会解析语法本身,对于多个参数入参,也都会通过lua_push*方法,放入栈上,所以当调用函数的时候,往栈上取参即可。

//方法配置数组
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}
};

//启动函数
LUAMOD_API int luaopen_coroutine (lua_State *L) {
  luaL_newlib(L, co_funcs);
  return 1;
}


/**
 * 协程挂起函数
 *
 * L为当前协程函数的协程栈
 * lua_gettop(L)  获取当前操作函数到栈顶的栈个数
 */
static int luaB_yield (lua_State *L) {
  return lua_yield(L, lua_gettop(L));
}

三、扩展库 - 基础库Base的配置详解


Lua的基础库就是定义了一些常用的函数,例如:print。这些函数作为全局函数,可以在Lua语言中直接调用。lbaselib.c

base_funcs:基础库的数组配置。主要定义函数名称和对应的方法。

luaopen_base:基础库的启动函数。主要有三步:

  • 从全局注册表中获取全局环境变量数组LUA_RIDX_GLOBALS,并通过luaL_setfuncs方法将基础库的函数逐个塞进全局环境变量上LUA_RIDX_GLOBALS[name]=func
  • lua_pushvalue拷贝全局环境变量数组LUA_RIDX_GLOBALS,然后LUA_RIDX_GLOBALS[_G]=LUA_RIDX_GLOBALS,并将拷贝的数组pop弹出栈顶
  • lua_pushliteral栈顶设置一个字符串常量,用于存放版本信息,并设置LUA_RIDX_GLOBALS['_VERSION']=LUA_VERSION,然后将栈顶的版本信息pop弹出栈顶。
//基础库启动
LUAMOD_API int luaopen_base (lua_State *L) {
  /* 打开全局环境变量数组LUA_RIDX_GLOBALS,放置到栈顶L->top open lib into global table */
  lua_pushglobaltable(L);
  /* 将base_funcs中的函数,逐个设置到LUA_RIDX_GLOBALS数组上 */
  luaL_setfuncs(L, base_funcs, 0); //module 表
  /* set global _G */
  /* LUA_RIDX_GLOBALS[_G] = LUA_RIDX_GLOBALS*/
  lua_pushvalue(L, -1);
  lua_setfield(L, -2, "_G"); //弹出栈顶L->top--
  /* /* LUA_RIDX_GLOBALS[_VERSION] = LUA_VERSION set global _VERSION */
  lua_pushliteral(L, LUA_VERSION);
  lua_setfield(L, -2, "_VERSION"); //弹出栈顶L->top--
  return 1;
}

//基础库配置
static const luaL_Reg base_funcs[] = {
  {"assert", luaB_assert},
  {"collectgarbage", luaB_collectgarbage},
  {"dofile", luaB_dofile},
  {"error", luaB_error},
  {"getmetatable", luaB_getmetatable},
  {"ipairs", luaB_ipairs},
  {"loadfile", luaB_loadfile},
  {"load", luaB_load},
#if defined(LUA_COMPAT_LOADSTRING)
  {"loadstring", luaB_load},
#endif
  {"next", luaB_next},
  {"pairs", luaB_pairs},
  {"pcall", luaB_pcall},
  {"print", luaB_print},
  {"rawequal", luaB_rawequal},
  {"rawlen", luaB_rawlen},
  {"rawget", luaB_rawget},
  {"rawset", luaB_rawset},
  {"select", luaB_select},
  {"setmetatable", luaB_setmetatable},
  {"tonumber", luaB_tonumber},
  {"tostring", luaB_tostring},
  {"type", luaB_type},
  {"xpcall", luaB_xpcall},
  /* placeholders */
  {"_G", NULL},
  {"_VERSION", NULL},
  {NULL, NULL}
};