2013-01-24 11 views
5

sto cercando di serializzare e deserializzare una chiusura LuaLua e serializzati chiusure

la mia comprensione di base è che la fabbrica di seguito dovrebbe generare chiusure (e che Lua non molto di distinguere tra le funzioni e le chiusure - cioè ci nessun tipo 'chiusura')

> 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! 

I expected numero intero 5 da serializzare con f1. Oppure, se string.dump non è in grado di gestire le chiusure, mi aspettavo un errore.

Sono diventato abbastanza diverso (ma più quello che mi aspettavo) risultati con un lieve cambiamento. Sembra che f2 sia effettivamente una chiusura, ma string.dump non ha tentato di serializzare il valore di x nel momento in cui è stato serializzato.

Il docs non mi aiuta molto. (cosa intendono per "... con nuovi valori"?)

> 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 ? 

risposta

1

i documenti sono abbastanza chiari. string.dump non gestisce le chiusure usando upvalues. questo perché gli upvalues ​​possono essere qualsiasi cosa (compresi i dati utente e come Lua può sapere come serializzarli?)

gli upvalues ​​sono le variabili esterne locali a una funzione a causa dell'ambito/chiusura. dato che x nell'esempio è un valore superiore alla funzione restituita da ffactory, non viene serializzato.

se si desidera sostenere questo in qualche modo, si dovrà memorizzare i upvalues ​​te stesso e impostare di nuovo dopo aver deserializzato la funzione, in questo modo:

function ffactory(x) 
    return function() return x+1 end 
end 

local f1 = ffactory(5) 
print(f1()) 

local s = string.dump(f1) 
f2 = loadstring(s) 
debug.setupvalue(f2, 1, 5) 
print(f2()) 
+1

Grazie. Non ero a conoscenza di debug.setupvalue. Puoi mostrarmi dove è stata spiegata la gestione di upvalue di string.dump (non qui: http://www.lua.org/manual/5.2/manual.html#pdf-string.dump). Inoltre, il rendimento di _ENV è il comportamento atteso nel mio primo esempio? – Paul

+0

si può leggere letto su debug.getupvalue [qui] (http://www.lua.org/manual/5.2/manual.html#pdf-debug.setupvalue). non sono sicuro di quale sia l'accordo nel tuo primo esempio poiché non dovrebbe nemmeno essere compilato. stai usando load(), ma in realtà dovresti usare loadstring(). il caricamento richiede una funzione e una stringa. –

+0

Non più. Lua 5.2 ha deprecato 'loadstring' e usa solo' load' per entrambe le stringhe e funzioni: http://www.lua.org/manual/5.2/manual.html#8.2 'loadstring' è ancora disponibile, tuttavia, e fornisce stesso risultato di 'load'. Grazie ancora! – Paul

6

Si può fare qualcosa di simile per salvare/ripristinare tali upvalues ​​(notare che non gestisce upvalues ​​condivisi tra le diverse funzioni):

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()) 

Questo funziona sia sotto Lua Lua 5.1 e 5.2.

Nota un risultato interessante se si cambia ffactory leggermente (aggiunto math.abs(0); tutto ciò che utilizza tabella globale in alcun modo lo farà):

function ffactory(x) return function() math.abs(0) return x end end 

Ora, se si ripristina upvalues ​​si ottiene lo stesso risultato, ma se si non ripristinare gli upvalues ​​si ottiene un errore di runtime sotto 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 ?