Lua 5.3 升级注意
最近在慢慢把公司的几个项目从 Lua 5.2 迁移到 Lua 5.3 ,为发布 skynet 1.0 alpha 版做准备。
在更新代码时发现了一些注意点,罗列一下:
Lua 5.3 去掉了关于 unsigned 等的 api ,现在全部用 lua_Integer
类型了。这些只需要换掉 api ,加上强制转换即可。通常不会有什么问题。
最需要细致 review 代码升级的是和序列化相关的库。在 skynet 里是序列化库、sproto、bson 等。我们还用到了 protobuffer ,也和序列化有关。
这是因为,Lua 5.3 提供了整型支持,而序列化工作通常需要区分浮点和整数分开处理。json 这种文本方式则不需要,同样还有 redis 的通讯协议也是如此。
过去判断一个 number 是浮点还是整数,需要用 lua_tonumber
与 lua_tointeger
各取一份做比较。虽然到了 Lua 5.3 这种代码理论上可以不用改动,但正确的方法应该是使用 lua_isinteger
。
浮点和整数的问题还存在于部分 lua 代码中。一些函数只接收整数参数,如果传入浮点数就会有运行时错误。比如用 string.format 格式化数字的时候用了 %d ,但传入了浮点数,之前没有问题,现在就需要多调用一次 math.floor 了。很多浮点数和除法有关(无论是否除尽,除法的结果一定是浮点数),整数除法应该尽量使用 // 新操作符。
bit32 库已经被废弃了,我个人建议不要用兼容方案,直接找到用到的代码换成 lua 5.3 支持的位操作符比较好。
同样,我们原来直接写了 utf8 库,应当换成 lua 5.3 官方版本。至于原来写的那个 int64 支持的过渡方案更应该废弃。
lua 5.3 提供的 string.pack 基本就是非官方的 struct 库,使用它可以节省不少对二进制流的处理代码。
我们项目的代码中也很多地方因为 unpack 这个函数而需要修改为 table.unpack 。这个函数从 lua 5.2 开始就从全局函数移到了 table 下,只是因为部分同学的习惯问题而一直没有改过来。这次 lua 5.3 把它从全局变量里删掉了,就逼大家改习惯了。
__ipairs
在 Lua 5.3 里去掉了,现在 ipairs 的行为改为从 1 开始迭代,直到第一个为 nil 的项停止。对于大多数库来说不太需要修改。但我们项目内部用的一个模块却因为这个修改导致了代码死循环。
追溯原因是因为该模块被设计成,每次调用 __index
都会生成一个默认对象。因为作者希望用的人可以随便写 a.b.c (即使 b 没有事先构造出来)。这个设计我个人并不喜欢,因为它修改了 __index
的字面意义。也因为如次, ipairs 永远取不到为 nil 的项,而死循环下去(直到内存耗净)。
有些 C 库在区分 Lua 5.1 和 Lua 5.2 时,直接比较 C 中的版号宏,把不是 5.2 的情况都当成 5.1 ,这种做法不再合适(比如 lpeg 就是这样)。
此外,Lua 5.3 的 lua_gettable
现在会返回取出的值的类型,这对需要检查 gettable 的项是否为空时,可以少一次 c api 调用。如果看到有类似处理的地方简单优化一下也好。
总的来说,Lua 5.2 到 5.3 的升级比 5.1 到 5.2 的过程要顺利的多。因为没有 jit 版本的问题,我个人建议在使用 Lua 5.2 的项目,如果肯花一点时间的话,还是都升级到 5.3 吧。
skynet 目前有一个 lua53 分支,它是 1.0 alpha 版的备选。希望跟进 skynet 开发的同学都切到这个分支上帮忙看看有没有什么问题。
我会在我们自己的项目跑一段时间后,把它切到 master 上,并发布 1.0 alpha 版。