Lua自带的模块并不多,好处就是Lua足够的小,毕竟它的设计目标是定位成一个嵌入式的轻量级语言的.

相关的函数index2adr

static TValue *index2adr (lua_State *L, int idx) {
if (idx > 0) {
TValue *o = L->base + (idx - 1);
api_check(L, idx <= L->ci->top - L->base);
if (o >= L->top) return cast(TValue *, luaO_nilobject);
else return o;
}
else if (idx > LUA_REGISTRYINDEX) {
api_check(L, idx != 0 && -idx <= L->top - L->base);
return L->top + idx;
}
else switch (idx) { /* pseudo-indices */
case LUA_REGISTRYINDEX: return registry(L);
case LUA_ENVIRONINDEX: {
Closure *func = curr_func(L);
sethvalue(L, &L->env, func->c.env);
return &L->env;
}
case LUA_GLOBALSINDEX: return gt(L);
default: {
Closure *func = curr_func(L);
idx = LUA_GLOBALSINDEX - idx;
return (idx <= func->c.nupvalues)
? &func->c.upvalue[idx-1]
: cast(TValue *, luaO_nilobject);
}
}
}

一个Lua函数栈由两个指针base和top来指定,base指向函数栈底,top则指向栈顶.

回到index2addr函数中,几种情况:

  1. 如果索引为正,则从函数栈底为起始位置向上查找数据
  2. 如果索引为负,则从函数栈顶为起始位置向下查找数据
  3. 紧跟着是几种特殊的索引值,都定义了非常大的数据,由于Lua栈限定了函数的栈尺寸,所以不会有那么大的索引,大可放心使用.

索引值为LUA_REGISTRYINDEX时,则返回的是全局数据global_state的l_registry表;如果索引值为LUA_GLOBALSINDEX,则返回该Lua_State的l_gt表.

lua模块注册

Lua内部所有模块的注册都在linit.c的函数luaL_openlibs中提供.可以看到的是,它依次访问一个数组,数组中定义了每个模块的模块名及相应的模块注册函数,依次调用函数就完成了模块的注册.

static const luaL_Reg lualibs[] = {
{"", luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
{LUA_TABLIBNAME, luaopen_table},
{LUA_IOLIBNAME, luaopen_io},
{LUA_OSLIBNAME, luaopen_os},
{LUA_STRLIBNAME, luaopen_string},
{LUA_MATHLIBNAME, luaopen_math},
{LUA_DBLIBNAME, luaopen_debug},
{NULL, NULL}
}; LUALIB_API void luaL_openlibs (lua_State *L) {
const luaL_Reg *lib = lualibs;
for (; lib->func; lib++) {
lua_pushcfunction(L, lib->func);
lua_pushstring(L, lib->name);
lua_call(L, 1, 0);
}
}

我没有详细的查看每个模块的注册函数,不过还是以最简单的例子来讲解,就是最常用的print函数.

由于这个函数没有前缀,因此的它所在的模块是”",也就是一个空字符串,因此它是在base模块中注册的,调用的注册函数是luaopen_base.

紧跟着继续看luaopen_base内部调用的第一个函数base_open:

static void base_open (lua_State *L) {
/* set global _G */
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setglobal(L, "_G");
/* open lib into global table */
luaL_register(L, "_G", base_funcs); // ....
}

首先来看最前面的两句:

  /* set global _G */
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setglobal(L, "_G");

这两句首先将LUA_GLOBALSINDEX对应的值压入栈中,其次调用”lua_setglobal(L, “_G”);”,这句代码的意思是在Lua_state的l_gt表中,当查找”_G”时,查找到的是索引值为LUA_GLOBALSINDEX的表.如果觉得有点绕,可以简单这个理解,在Lua中的G表,也就是全局表,满足这个等式”_G = _G["_G"]“,也就是这个叫”_G”的表,内部有一个key为”_G”的表是指向自己的.怀疑这个结论的,可以在Lua命令行中执行print(_G)和print(_G["_G"])看看输出结果是不是一致的.

Lua中要这么处理的理由是:为了让G表和处理其它表使用同样的机制.查找一个变量时,最终会一直查到G表中,这是很自然的事情;所以为了也能按照这个机制顺利的查找到自己,于是在G表中有一个同名成员指向自己.

好了,前面两句的作用已经分析完毕.其结果有两个:

  1. _G = _G["_G"]
  2. _G表的值压入函数栈中方便了下面的调用.

继续看下面的语句:

luaL_register(L, “_G”, base_funcs);

它最终会将base_funcs中的函数注册到G表中,但是里面还有些细节需要看看的.

LUALIB_API void luaI_openlib (lua_State *L, const char *libname,
const luaL_Reg *l, int nup) {
if (libname) {
int size = libsize(l);
/* check whether lib already exists */
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1);
lua_getfield(L, -1, libname); /* get _LOADED[libname] */
if (!lua_istable(L, -1)) { /* not found? */
lua_pop(L, 1); /* remove previous result */
/* try global variable (and create one if it does not exist) */
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); /* _LOADED[libname] = new table */
}
lua_remove(L, -2); /* remove _LOADED table */
lua_insert(L, -(nup+1)); /* move library table to below upvalues */
} // ...
}

注册这些函数之前,首先会到l_registry表的成员_LOADED表中查找该库,如果不存在则再在G表中查找这个库,不存在则创建一个表.因此,不管是lua中内部的库或者是外部使用require引用的库,都会走这个流程并最终在G表和l_registry["_LOADED"]中存放该库的表.最后,再遍历传进来的函数指针数组,完成库函数的注册.

比如,注册os.print时,首先将print函数绑定在一个函数指针上,再去l_registry["_LOADED"]和G表中查询该名为”os”的库是否存在,不存在则创建一个表,即:

G["os"] = {}

紧跟着注册print函数,即: G["os"]["print"] = 待注册的函数指针.这样,在调用lua代码os.print(1)时,首先根据”os”到G表中查找对应的表,再在这个表中查找”print”成员得到函数指针,最后完成函数的调用.

注册外部模块

luaL_newlibtable 它仅仅是创建了一个table,然后把数组里的函数放进去而已

luaL_setfuncs它把数组l中的所有函数注册入栈顶的table,并给所有函数绑上nupupvalue

define luaL_newlibtable(L, l)

lua_createtble(L, 0, sizeof(l)/sizeof((l)[0]) - 1)

define luaL_newlib(L, l)

(luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)
LUALIB_API void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup){
luaL_checkversion(L);
luaL_checkstack(L, nup, "too_many_upvalue");
for(; l->name != NULL; i++){/* fill the table with given functions*/
int i;
for(i = 0; i < nup; i++)/copy upvalues to the top/
lua_pushvalue(L, -nup);
lua_pushclosure(L, l->func, nup);/closure with those upvalues/
lua_setfield(L, -(nup + 2), l->name);
}
lua_pop(L, nup);/remove upvalues/
}

lua模块注册的更多相关文章

  1. Lua和C++交互 学习记录之八:C++类注册为Lua模块

    主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍) 部分内容查阅自:<Lua 5.3  参考手册>中文版 译者 云风 制作 Kavcc vs2013+lua-5.3.3 1 ...

  2. Lua和C++交互 学习记录之七:C++全局函数注册为Lua模块

    主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍) 部分内容查阅自:<Lua 5.3  参考手册>中文版 译者 云风 制作 Kavcc vs2013+lua-5.3.3 1 ...

  3. lua 模块与环境

    编写一个模块的最简单方法: -- complex.lua -- 模块实际上是一个表 complex = {} -- 定义模块函数 function complex.add(c1,c2) ... end ...

  4. cocos2d-x 2.2.0 如何在lua中注册回调函数给C++

    cocos2d-x内部使用tolua进行lua绑定,但是引擎并没有提供一个通用的接口让我们可以把一个lua函数注册给C++层面的回调事件.翻看引擎的lua绑定代码,我们可以仿照引擎中的方法来做.值得吐 ...

  5. vim编译安装+lua模块

    vim编译安装+lua模块 使用背景:代码自动补全插件,需要安装lua模块 安装准备,首先下载安装vim所依赖的其它安装包,ncurses,lua,readline,vim 源码下载,编译安装 ncu ...

  6. lua 模块

    lua 模块 概述 lua 模块类似于封装库 将相应功能封装为一个模块, 可以按照面向对象中的类定义去理解和使用 使用 模块文件示例程序 mod = {} mod.constant = "模 ...

  7. lua模块demo(redis,http,mysql,cjson,本地缓存)

    1. lua模块demo(redis,http,mysql,cjson,本地缓存) 1.1. 配置 在nginx.conf中设置lua_shared_dict my_cache 128m; 开启ngi ...

  8. Lua模块的加载与内存释放

    今天早上听说一件事情让我觉得很诡异的事情:公司线上的一款游戏,加载一份配置资源后,内存涨了几十M,然后内存再也下不来了.因为好奇,所以要来了最大的一个配置文件(4.5M,去除空格与换行后的大小),进行 ...

  9. nginx 增加 lua模块

    Nginx中的stub_status模块主要用于查看Nginx的一些状态信息. 本模块默认是不会编译进Nginx的,如果你要使用该模块,则要在编译安装Nginx时指定: ./configure –wi ...

随机推荐

  1. 面向对象程序设计_Task4_Calculator1.1

    The 2nd part of the Calculator program 题目链接:Click Here github链接:Click Here 诶嘿,第二部分,要开始实现计算的功能了,估计离不是 ...

  2. php解决约瑟夫环的问题

    php里面解决约瑟夫环还是比较方面的,但是下面的方法太费空间 <?php class SelectKing{ private $m;//幅度 private $n;//总数 public fun ...

  3. Ubuntu18.04 使用过程遇到的问题记录

    索引: 1.Ubuntu 18.04 安装搜狗输入法 2.在 Ubuntu 18.04 中将第三方软件添加至 favorite 菜单栏 3.在 VMware workstation 中为虚拟机安装 V ...

  4. 解决redis-cli command not found问题

    在使用其他服务器连接Redis服务器时,一般使用的语句是 [redis-cli -h IP -p port] 但是早上连接时报错:redis-cli command not found 在redis服 ...

  5. hadoop学习;hdfs操作;执行抛出权限异常: Permission denied;api查看源代码方法;源代码不停的向里循环;抽象类通过debug查找源代码

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/u010026901/article/details/26587251 eclipse快捷键alt+s ...

  6. numpy的操作

    import numpy as np ######################## # 索引 n1 = np.random.randint(0, 100, 10) # print(n1) ''' ...

  7. Java并发编程--7.Java内存操作总结

    主内存和工作内存 工作规则 Java内存模型, 定义变量的访问规则, 即将共享变量存储到内存和取出内存的底层细节  所有的变量都存储在主内存中,每条线程有自己的工作内存,工作内存中用到的变量, 是从主 ...

  8. Hadoop学习之路(九)HDFS深入理解

    HDFS的优点和缺点 HDFS的优点 1.可构建在廉价机器上 通过多副本提高可靠性,提供了容错和恢复机制 服务器节点的宕机是常态   必须理性对象 2.高容错性 数据自动保存多个副本,副本丢失后,自动 ...

  9. 记一次爬虫经历(友话APP的Web端)

    背景:学校为迎接新生举办了一个活动,在友话APP的校园圈子内发布动态即可参与活动,最终抽取数名同学赠送福利. 分析:动态的数量会随着迎新的开始逐渐增加,人工统计显然不现实,因此可以使用爬虫脚本在友话A ...

  10. C#中控件Control的Paint事件和OnPaint虚函数的区别

    句柄 : 句柄,是整个Windows编程的基础.一个句柄是指使用的一个唯一的整数值,即一个4字节(64位程序中为8字节)长的数值,来标识应用程序中的不同对象和同类对象中的不同的实例,诸如,一个窗口,按 ...