lua 给userdata设置元表_Lua设计与实现--数据类型篇

v2-b900af8564d3e3c940e5d5993e2c039c_1440w.jpg?source=172ae18b

本篇文章是Lua设计与实现专栏的第一篇,主要结合《Lua设计与实现》书中的第二章(数据类型),以及lua5.3源码进行一些总结,由于原书中主要是基于lua5.1进行书写的,所以可能会有跟书中列举代码不一致的地方,不过大体上是保持一致的。

第一次在知乎上写技术分享,如有错误请不吝指出。

数据类型

Lua中的数据类型主要分为以下几类:

v2-b33bb928368b6ccf9e20e393e6bcc2ad_b.jpg

可以看到除了LUA_TNONE,lua支持了9种基本类型。其实对于某些基本类型,lua还用了一个叫做variant tag的标记来定义其子类型,具体参见下图:

v2-6fa7f4d0377c649369dbb83a73c7e606_b.jpg

比如string又细分为短string和长string,主要区别在于它们的hash值生成;而number也细分为了float和integer。

Type的组织方式

由于lua是pure C实现的,所以对于Type的组织方式,也不像C++、C#这类面向对象的语言直接可以基于继承来实现。我们先来看C里实现面向对象的两种方式:

  • struct的嵌套

v2-0fe38887f94832fd432a3addc4a2e297_b.jpg
  • 使用union包含所有可能的类型字段

v2-af194761c2875d876b25f129a3075878_b.jpg

lua主要采用了两种方式结合的形式。我们简单来看一下lua的类型系统牵涉的一些关键数据结构:

Value和TValue

首先,lua为了方便对所有的类型进行统一管理,把它们都抽象成了一个叫做Value的union结构,具体定义如下:

v2-077150f25370e989d339925e2b23a472_b.jpg

从定义可以看出,主要把这些类型划分为了需要GC的类型和不需要GC的类型。由于Value是union的结构,所以每个Value实例里同时只会有一个字段是有效的。而为了知道具体哪个字段是有效的,也就是具体该Value是什么类型,从而有了TValue这个struct结构,主要在Value基础上wrap了一个_tt字段来标识Value的具体类型。TValue的定义如下:

v2-7f8f202439fd4529da4457392176db3d_b.jpg

GCUnion、GCObject、CommonHeader

lua把所有值按是否需要被GC,划分为了GCObject和一般类型。所有需要被GC的类型,被定义在了GCUnion里:

v2-a30bfdc9d646cb0ad1cd005bcfcec0e3_b.jpg

可以发现String、UserData、Closure、Table、Proto、luaState等类型都是需要被GC的,GCUnion结构和Value类似,也是同时只有一个字段是有效的。所以我们自然而然会想到,是不是类似TValue一样,在外面给包一层type呢,但是lua实现这边并没有这样做,而是让TString、UData这些"子类"都在各自定义的开头定义这个type字段。实际上,是定义了一个叫做CommonHeader的宏,这个宏里包含了type和一些其他字段,而每个GC类型都需要在在其struct头部定义该宏,从而可以造成一种所有GC类型都继承自一个带有CommonHeader宏的基类的假象。该宏定义如下:

v2-88810812ad55188536834422d58a1d34_b.jpg

可以发现,它一共有三个字段:

  • tt,即该GC对象的具体类型
  • next,指向GCObject的指针,用于GC算法内部实现链表
  • marked,用于GC算法内部实现

那这边的GCObject又是个什么东东呢?其实它就是把CommonHeader这个数据区包成了一个struct,它的好处在于lua可以把所有的GC类型的对象都视作是一个GCObject,比如在lua_State结构里就定义了一个GC列表: GCObject* gclist。 再比如,lua里创建单个gcobject的函数如下:

v2-beea523dc209553dfdcc0ce6918c4465_b.jpg

每个像String、Table这种子类型,它们就都会调用这个接口来创建一个GCObject实例,区别只是在于传入的type和内存size不一样而已。而这个公用函数也会帮你初始化掉CommonHeader部分的数据,每个类型只用把创建出来的实例剩余内存部分的数据设置好即可,比如下面举String类型的例子:

v2-36c09810b62d424ce00f87619208f369_b.jpg

可以看出string在创建完成以后,调用了内部的gco2ts函数,把本来指向GCObject指针强转成了指向TString的指针,然后赋予了一些TString的额外元数据。

v2-529a8e34f17ef02ad073fd5de0f9e13b_b.jpg

最后它把请求的字符串拷贝到了该TString内部,并且进一步更新了全局string的hashmap结构。String的具体实现我们会在后面String章节讲到,这里不深入研究了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值