Lua5.1代码阅读(六):ltm.h/ltm.c

转载 2013年12月04日 09:14:14

一、概览

ltm.h/ltm.c的作用是提供查询元方法(元方法的值可能是函数,也可能是非函数的值)的API。

源码中把元方法称为标签方法(tag method)。

Lua的元方法有点像C++的运算符重载,用于覆盖一些Lua内部定义的默认行为。

参考链接:

1. LUA源码分析五:元表

http://lin-style.iteye.com/blog/1012138

2. Lua GC 的源码剖析 (2)

http://blog.codingnow.com/2011/03/lua_gc_2.html

查询元表的方式有两种:

* 方式一、查询某个元表(Table结构体)的元方法(见fasttm, gfasttm)

首先,把事件(类型是枚举值)转换为元方法名称(类型是TString *字符串)。

然后,通过查询Table结构体(相当于关联数组),获取元方法的值,

如果结果为空(nil值),则把这个信息缓存到Table结构体中(flags域),

下次查询时就可以直接返回空指针。

* 方式二、查询某个对象的元方法(见luaT_gettmbyobj)

首先,判断对象是表、用户数据还是其它基本类型

(TValue实际是个联合体,所以需要判断类型,

然后用hvalue和uvalue宏转换为正确的类型,

见lobject.h的TValue、Value结构体,lstate.h的GCObject联合体)

这里,根据类型用相应的宏取出作为元表的Table *指针:

* 表:hvalue(o)对应GCObject的struct Table h字段。元表是->metatable。

* 用户数据:uvalue(o)对应GCObject的union Udata u字段。元表是->metatable。

* 其它基本类型:G(L)对应lua_State的global_State *l_G字段,元表是->mt[ttype(o)],

ttype(o)是o的基本类型索引(定义在lua.h的LUA_TNIL到LUA_TTHREAD常量)。

然后,用类似情况一的方式查元表,但不缓存nil结果,也不读取Table的flags缓存标志。

二、相关结构体(均定义在ltm模块外)

* 元表内部实现是结构体Table:

lobject.h:

typedef struct Table {

 CommonHeader;

 lu_byte flags;  /* 1<<p means tagmethod(p) is not present */ 

 lu_byte lsizenode;  /* log2 of size of `node' array */

 struct Table *metatable;

...

其中flags用于缓存nil结果(位数组)。

* Table的flags域用于缓存元表的查询(加速判断元方法是否存在)

详细见gfasttm的宏定义以及luaT_gettm的实现

* global_State结构体携带元方法名称数组信息,用于快速把枚举值转换为TString *字符串。

lstate.h

... 

 struct Table *mt[NUM_TAGS];  /* metatables for basic types */

 TString *tmname[TM_N];  /* array with tag-method names */

} global_State;

其中mt是把LUA_TNIL到LUA_TTHREAD的常量(定义在lua.h中)映射为基本类型的元表。

(基本类型的元表由lapi.c的lua_setmetatable修改)

tmname用于所有元方法枚举映射为TString *数组,

以便于查询元表时获取luaH_getstr()需要传入的作为键名的字符串指针参数。

三、头文件

#include <string.h>

#include "lua.h"

#include "lobject.h"

#include "lstate.h"

#include "lstring.h"

#include "ltable.h"

#include "ltm.h"

 

四、宏定义

1. #define ltm_c

表示源于ltm.c。

2. #define LUA_CORE

表示ltm作为Lua的底层实现。

五、全局的枚举值

1. TMS(元标签类型)

typedef enum {

 TM_INDEX,

 TM_NEWINDEX,

 TM_GC,

 TM_MODE,

 TM_EQ,  /* last tag method with `fast' access */

 TM_ADD,

 TM_SUB,

 TM_MUL,

 TM_DIV,

 TM_MOD,

 TM_POW,

 TM_UNM,

 TM_LEN,

 TM_LT,

 TM_LE,

 TM_CONCAT,

 TM_CALL,

 TM_N /* number of elements in the enum */

} TMS;

注意它的值分别对应luaT_init内的luaT_eventname数组

static const char *const luaT_eventname[] = {  /* ORDER TM */

"__index", "__newindex",

"__gc", "__mode", "__eq",

"__add", "__sub", "__mul", "__div", "__mod",

"__pow", "__unm", "__len", "__lt", "__le",

"__concat", "__call"

};

另外,fasttm和gfasttm宏只支持查询TM_INDEX和TM_MODE之间元方法的值。

而luaT_gettmbyobj可以查所有TMS类型。

六、全局变量

1. const char *const luaT_typenames[] = {

 "nil", "boolean", "userdata", "number",

 "string", "table", "function", "userdata", "thread",

 "proto", "upval"

};

这个数组是用来把LUA_TNIL到LUA_TTHREAD常量(定义在lua.h中)转换为字符串。

跟元方法的关系不大(配合ttype宏获取TValue结构体的类型信息)

七、全局宏

1. #define gfasttm(g,et,e) ((et) == NULL ? NULL : \

((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))

这个宏的作用是快速获取指定元方法对应的TValue *值。

给定——

* 全局状态g(类型为global_State结构体的L栈全局状态G(L),见lua_State的l_G域)、

* 表et(类型为Table结构体的元表,事件表?)、

* 事件e(类型为TMS的元方法枚举值),

返回获取的标签方法(元方法)的对应值(类型为TValue *)。

作用类似下面的伪代码(et可以看成是一个以字符串为下标的关联数组)。

tm gfasttm(g, et, event)

{

return et[g.metaname[event]]

}

由于lgc的traversetable方法要取__mode元方法的值,

但traversetable方法是直接以global_State类型作为参数,

所以需要定义这个gfasttm()宏(实际上它和fasttm是同等作用的)

2. #define fasttm(l,et,e) gfasttm(G(l), et, e)

相当于gfasttm(G(L), et, event)。

和gfasttm作用相同,都是快速获取指定元方法对应的TValue *值。

除了lgc的traversetable函数外,Lua其它代码(分布在lgc, ltm, lvm三个模块)

都使用它从元表中取出元方法的值。

八、全局导出函数

1. void luaT_init (lua_State *L) {

由于需要查Table的值(使用luaH_getstr),要用到TString *的键,需要缓存元方法常数。

同时确保每个常数都不会被回收。

2. const TValue *luaT_gettm (Table *events, TMS event, TString *ename) {

作为gfasttm和fasttm的内部实现(判断nil缓存的工作交给宏处理)

当发现结果为nil,该函数会对events的flags做标记(把某个位从0变成1)以达到缓存效果。

返回的TM值可能是闭包、值或者是空指针。

3. const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) {

查询o的event元方法,如果o既不是表也不是用户数据,则查询L的event元方法。

九、关键代码

1. luaT_gettm / luaT_gettmbyobj

实际上它们都使用luaH_getstr进行索引查询,

不同的是,前者使用元表的位数组缓存查询到的nil结果,

但是只支持__index, __newindex, __gc和__mode,

而后者没有做结果的缓存处理,所以没有限制元方法的类型范围。

Lua5.1代码阅读(一):lua.c

一、Lua5.1相关资源 下载见: http://luabinaries.sourceforge.net/download.html 在线版: http://www.lua.org/sourc...
  • Chinamming
  • Chinamming
  • 2013年12月04日 09:08
  • 848

Lua5.1代码阅读(八):ldo.h/ldo.c

一、概览 ldo.h/ldo.c描述Lua的堆栈和调用的结构。 提供对调用、协程、异常等复杂控制流的支持。 模块中对外公开的API主要分为以下几类: (1) 错误恢复: luaD_seter...
  • Chinamming
  • Chinamming
  • 2013年12月04日 09:15
  • 1224

Lua5.1代码阅读(二):llex.h/llex.c

一、作用和参考资料 llex.c是Lua的词法分析器(把单个输入字符串切割为多个输出符号) 参考: 1. Lua 5.1.3源代码分析之词法分析 By 天地沙鸥 http://xenyinze...
  • Chinamming
  • Chinamming
  • 2013年12月04日 09:10
  • 921

Dl4j-fit(DataSetIterator iterator)源码阅读(八) 根据参数更新梯度

前面经过反向传播,已经计算出了模型的损失函数得分以及梯度,在反向传播完成之后会返回到package org.deeplearning4j.optimize.solvers包下的BaseOptimize...
  • u011669700
  • u011669700
  • 2017年12月08日 17:02
  • 63

Lua5.1代码阅读(七):lvm.h/lvm.c

一、概览 lvm.h/lvm.c的作用是提供底层的Lua虚拟机。 这个模块主要是用于循环读取并分解指令, 然后根据其操作码的枚举值进行处理或跳转到Lua的其它模块。 内部的函数引用图如下: ...
  • Chinamming
  • Chinamming
  • 2013年12月04日 09:14
  • 1705

Lua5.1代码阅读(三):lcode.h/lcode.c

一、概述 lcode.h/lcode.c是Lua的代码生成器, 用于优化和生成目标二进制代码。 lcode.c的所有导出函数只被lparser.c引用。 lcode内部的函数引用图如下:   二、...
  • Chinamming
  • Chinamming
  • 2013年12月04日 09:11
  • 1187

MFC 技巧之三

21. 介绍函数过程中一种任意键退出同时能处理消息的实现方法  1. 设置定时器,用于使::GetMessage(...)函数总能快速取到消息. 2. 在函数处理中加入: 函数每执行完一...
  • zang141588761
  • zang141588761
  • 2016年01月22日 14:18
  • 564

高效阅读源代码指南

最近一年里,我阅读了不少开源项目的源代码,之前也和朋友讨论过阅读源代码时遇到的一些问题。我觉得有必要写一篇博文分享一下自己的经验。 序章:准备工作 通常情况下,我们不会无缘无故拿到一份源代...
  • kai8wei
  • kai8wei
  • 2016年05月13日 00:10
  • 727

代码阅读工具使用入门

一 初次使用Source Insight 第一次运行作些设置; 根据自己的安装路径设置; 打开一个CPP文件,左边显示类和成员,右边代码;比较清晰; 选中某个成员,可J...
  • bcbobo21cn
  • bcbobo21cn
  • 2016年04月15日 12:53
  • 4535

代码阅读工具学习总结

代码阅读工具:Source Navigator和Source Insight 一、Source Insight实用技巧: Source Insight(下文的SI指的也是它)就是这样的一个...
  • bcbobo21cn
  • bcbobo21cn
  • 2016年04月15日 12:25
  • 6409
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Lua5.1代码阅读(六):ltm.h/ltm.c
举报原因:
原因补充:

(最多只允许输入30个字)