2016-06-17 15 views
2

Ich habe eine Tabelle wie dieseWie implementiert man eine Trace-Funktion für Tabelle zu Funktionen?

local ftable = { 
    getPinId = app.getPinId 
} 

ftable an eine andere Funktion übergeben wird, die es als eine RPC-Schnittstelle exportiert. Das funktioniert, aber jetzt möchte ich Funktionsaufrufverfolgung zu einer Protokolldatei hinzufügen.

Der einfache Ansatz ist

local ftable = { 
    getPinId = function(...) print("getPinId") app.getPinId(...) end 
} 

Aber, das ist nicht besonders schön ist. Ich mag etwas setzen wie:

local trace = function(func, ...) 
    return function(...) print(func) func(...) end 
end 

local ftable = { 
    getPinId = trace(app.getPinId) 
} 

Aber diese produzieren nicht ganz das gewünschte Ergebnis. Die Parameter werden nicht übergeben.

Eine andere Möglichkeit ist, eine Metatabelle wie diese verwenden:

local ftable = {} 
setmetatable(ftable, { 
    __index = function(_, k) 
    printf("Call: app.%s\n", k) return app[k] end 
}) 

Welche funktioniert. Aber ich möchte auch die Parameter, die nach Möglichkeit übergeben werden, drucken können.

Irgendwelche Vorschläge? Ich verwende ausschließlich luajit, wenn das einen Unterschied macht. stattdessen

+0

Ihr Code sollte funktionieren, außer dass es die Adresse der Funktion, nicht seinen Namen, druckt. Bitte zeigen Sie ein Beispiel für den Fehler, den Sie melden. – lhf

+0

Das stimmt, ich habe versucht, die debug.getinfo Funktion, aber das hat nicht geholfen. Es behandelt es wie eine anonyme Funktion. Ich denke, ich könnte den Namen weitergeben. Im Moment verwende ich die Metatable-Methode, die den Code sehr verkürzt. – Matt

+0

Lua Funktionen, wie alle Werte, haben keine Namen.Deshalb hat @lhf in seiner Antwort eine Zeichenfolge mit dem Funktionswert verknüpft. –

Antwort

3

einen Funktionsaufruf Verpackung ist leicht in Lua:

local function wrap(f) 
    local function after(...) 
    -- code to execute *after* function call to f 
    print("return values:", ...) 
    return ... 
    end 
    return function(...) 
    -- code to execute *before* function call to f 
    print("arguments:", ...) 
    return after(f(...)) 
    end 
end 


local function f(a, b, c) 
    return a+b, c-a 
end 

local f_wrapped = wrap(f) 
f_wrapped(1, 2, 3) 

Ausgang ist:

arguments: 1 2 3 
return values: 3 2 

Ein Problem für die Protokollierung/Tracing ist dass Lua-Werte (einschließlich Funktionen) selbst keine Namen haben. Die Debugbibliothek versucht, geeignete Namen für Funktionen zu finden, indem überprüft wird, wie sie aufgerufen werden oder wo sie gespeichert sind. Wenn Sie jedoch sicherstellen möchten, dass Sie einen Namen selbst angeben müssen. wenn Ihre Funktionen gespeichert sind, in (verschachtelten) Tabellen jedoch (wie in einem Kommentar angezeigt), können Sie eine Funktion schreiben, die die verschachtelten Tabellen iteriert, und wickeln alle Funktionen findet es den Tabellenschlüssel als Name:

local function trace(name, value) 
    local t = type(value) 
    if t == "function" then -- do the wrapping 
    local function after(...) 
     print(name.." returns:", ...) 
     return ... 
    end 
    return function(...) 
     print("calling "..name..":", ...) 
     return after(value(...)) 
    end 
    elseif t == "table" then -- recurse into subtables 
    local copy = nil 
    for k,v in pairs(value) do 
     local nv = trace(name.."."..tostring(k), v) 
     if nv ~= v then 
     copy = copy or setmetatable({}, { __index = value }) 
     copy[ k ] = nv 
     end 
    end 
    return copy or value 
    else -- other values are ignored (returned as is) 
    return value 
    end 
end 


local ftable = { 
    getPinId = function(...) return "x", ... end, 
    nested = { 
    getPinId = function(...) return "y", ... end 
    } 
} 

local ftableTraced = trace("ftable", ftable) 
ftableTraced.getPinId(1, 2, 3) 
ftableTraced.nested.getPinId(2, 3, 4) 

Ausgang ist:

calling ftable.getPinId: 1 2 3 
ftable.getPinId returns: x 1 2 3 
calling ftable.nested.getPinId: 2 3 4 
ftable.nested.getPinId returns: y 2 3 4 

Einige Dinge zu beachten:

  1. Tabelle Tasten beliebig Lua Werte sein können, nicht nur ganz kurze Ketten von druckbaren Zeichen bestehen.
  2. Tabellen können zyklische Referenzen enthalten. Wenn sie dies tun, wird die obige naive Implementierung mit einem Stapelüberlauf enden.
2

Verwenden metamethod die __call:

M = { __call = 
    function (t,...) print("calling ",t.name,...) return t.func(...) end 
} 

trace = function(func,name) 
    return setmetatable({func=func,name=name},M) 
end 

function f(...) 
    print("in f",...) 
end 

g=trace(f,"f") 
g(10,20,30) 
+0

mit der metamethod __index-Methode war sehr praktisch, in dem ich nicht müssen jede Methode explizit eingeben. Gibt es eine Möglichkeit, beides zu kombinieren? d. h., füge eine __call-Methode hinzu? – Matt

+0

@Matt, versuche 'setmetatable (ftable, {__ index = Funktion (t, k) lokal f = Kurve (app [k], k); t [k] = f; gib das Ende zurück})'. – lhf

+0

hmm, nicht sicher warum, aber in meinem Fall funktioniert das nicht. Meine Tabelle ist ein wenig komplizierter. Es enthält Tabellen in Tabellen, aber ich füge es an die Tabelle in der Tabelle an. Naja, ich überlasse es einem anderen Tag, um es herauszufinden. – Matt