我正在尝试序列化和反序列化Lua闭包
我的基本理解是下面的工厂应该生成闭包(并且Lua没有太多区分函数和闭包 – 即没有类型’闭包’)
> function ffactory(x) return function() return x end end > f1 = ffactory(5) > print(f1()) 5 <-- so far so good > s = string.dump(f1) > f2 = load(s) > print(f2()) table: 00000000002F7BA0 <-- expected the integer 5 > print(f2()==_ENV) true <-- definitely didn't expect this!
我期望整数5用f1序列化.或者,如果string.dump无法处理闭包,我预计会出错.
我得到了很大的不同(但更多的是我所期望的)会带来温和的变化.看起来f2确实是一个闭包,但是string.dump在序列化时没有尝试序列化x的值.
docs对我帮助不大. (他们的意思是“……新的升值”?)
> function ffactory(x) return function() return x+1 end end > f1 = ffactory(5) > print(f1()) 6 <-- good > s = string.dump(f1) > f2 = load(s) > print(f2()) stdin:1: attempt to perform arithmetic on upvalue 'x' (a table value) stack traceback: stdin:1: in function 'f2' stdin:1: in main chunk [C]: in ?
解决方法
你可以做这样的事情来保存/恢复那些upvalues(注意它不处理不同函数之间共享的upvalues):
local function capture(func) local vars = {} local i = 1 while true do local name,value = debug.getupvalue(func,i) if not name then break end vars[i] = value i = i + 1 end return vars end local function restore(func,vars) for i,value in ipairs(vars) do debug.setupvalue(func,i,value) end end function ffactory(x) return function() return x end end local f1 = ffactory(5) local f2 = (loadstring or load)(string.dump(f1)) restore(f2,capture(f1)) --<-- this restored upvalues from f1 for f2 print(f1(),f2())
这适用于Lua 5.1和Lua 5.2.
如果你稍微改变ffactory,请注意一个有趣的结果(添加了math.abs(0);任何以任何方式使用全局表的东西都会这样做):
function ffactory(x) return function() math.abs(0) return x end end
现在,如果恢复上升值,则会得到相同的结果,但如果不恢复upvalues,则在Lua 5.2下会出现运行时错误:
lua.exe: upvalues.lua:19: attempt to index upvalue '_ENV' (a nil value) stack traceback: upvalues.lua:19: in function 'f2' upvalues.lua:24: in main chunk [C]: in ?