如何实现table的trace函数到functions?
How to implement a trace function for table to functions?
我有一个table这样的
local ftable = {
getPinId = app.getPinId
}
ftable 被传递给另一个函数,该函数将其导出为 RPC 接口。
这可行,但现在我想将函数调用跟踪添加到日志文件。
简单的方法是
local ftable = {
getPinId = function(...) print("getPinId") app.getPinId(...) end
}
但是,这不是特别好。
我想放这样的东西:
local trace = function(func, ...)
return function(...) print(func) func(...) end
end
local ftable = {
getPinId = trace(app.getPinId)
}
但这并没有产生非常理想的结果。参数没有被传递。
另一种选择是像这样使用元table:
local ftable = {}
setmetatable(ftable, {
__index = function(_, k)
printf("Call: app.%s\n", k) return app[k] end
})
哪个有效。但如果可能的话,我也希望能够打印传递的参数。
有什么建议吗?
如果有任何不同,我只使用 luajit。
改为使用 __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)
在 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 )
输出为:
arguments: 1 2 3
return values: 3 2
logging/tracing 的一个问题是 Lua 值(包括函数)本身没有名称。调试库试图通过检查函数的调用方式或存储位置来找到函数的 suitable 名称,但如果您想确定,则必须自己提供一个名称。但是,如果您的函数存储在(嵌套的)tables 中(如评论中所示),您可以编写一个函数来迭代嵌套的 tables,并使用 table 键作为名称:
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 )
输出为:
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
需要注意的事项:
- Table 键可以是任意 Lua 值,而不仅仅是完全由 printable 字符组成的短字符串。
- Tables 可以包含循环引用。如果他们这样做,上面的天真实现将死于堆栈溢出。
我有一个table这样的
local ftable = {
getPinId = app.getPinId
}
ftable 被传递给另一个函数,该函数将其导出为 RPC 接口。 这可行,但现在我想将函数调用跟踪添加到日志文件。
简单的方法是
local ftable = {
getPinId = function(...) print("getPinId") app.getPinId(...) end
}
但是,这不是特别好。 我想放这样的东西:
local trace = function(func, ...)
return function(...) print(func) func(...) end
end
local ftable = {
getPinId = trace(app.getPinId)
}
但这并没有产生非常理想的结果。参数没有被传递。
另一种选择是像这样使用元table:
local ftable = {}
setmetatable(ftable, {
__index = function(_, k)
printf("Call: app.%s\n", k) return app[k] end
})
哪个有效。但如果可能的话,我也希望能够打印传递的参数。
有什么建议吗? 如果有任何不同,我只使用 luajit。
改为使用 __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)
在 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 )
输出为:
arguments: 1 2 3
return values: 3 2
logging/tracing 的一个问题是 Lua 值(包括函数)本身没有名称。调试库试图通过检查函数的调用方式或存储位置来找到函数的 suitable 名称,但如果您想确定,则必须自己提供一个名称。但是,如果您的函数存储在(嵌套的)tables 中(如评论中所示),您可以编写一个函数来迭代嵌套的 tables,并使用 table 键作为名称:
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 )
输出为:
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
需要注意的事项:
- Table 键可以是任意 Lua 值,而不仅仅是完全由 printable 字符组成的短字符串。
- Tables 可以包含循环引用。如果他们这样做,上面的天真实现将死于堆栈溢出。