我如何创建在调用某个函数时会调用该函数的函数?

问题描述 投票:4回答:3

我一直试图创建一个函数,一旦调用某个函数,该函数便会运行事件/函数。我希望用法如下:

local a = nil

a = hookfunction(print, function() -- When print() is called...
    warn("print(...) called.") 
    return nil
end)

我知道有一个debug.sethook()函数,但我不太了解如何使用它。

lua
3个回答
0
投票

有两种方法,但是每种方法都有一些陷阱需要处理。

一种方法是使用monkeypatching。这会将打印功能更改为将执行所需功能的其他功能,然后调用原始功能。

类似这样的东西:

local origprint = print;
(_G or _ENV).print = function(...)
  io.write("print is called\n")
  return origprint(...)
end
print("foo", 1)

此选项的主要陷阱是,如果某些组件在进行猴子修补之前保存了print的值,则其后续使用不会受到补丁的影响。

另一个选择是使用调试钩子:

debug.sethook(function()
    local info = debug.getinfo(2)
    if info.name then
      io.write(info.name, " is called\n")
    end
  end, "c")
function foo()
  print("do something")
end
foo()

这应该打印:

foo is called
print is called
do something

此方法的主要缺陷是,它不保证将按您的期望输出函数名称,因为values可能与它们的不同名称/容器关联。

例如,将打印a is called

local a = print
a("do something")

这将打印? is called

local a = {print}
a[1]("do something")

0
投票

最简单的方法是包装原始函数:

do
  local _print = print

  function print(...)
    _print("print(...) called.")
    _print(...)
  end
end

print("hello world")

输出:

打印(...)被调用。

你好世界

[我们听到的是将原始print存储在局部变量_print中,这是我们在存储原始字符后创建的新全局打印函数的上值。


0
投票

这个问题缺少一个非常重要的细节:为什么?

如果要调试时使用此选项,则debug.sethook可能是更合适的选择。正如Paul Kulchenko指出的那样,debug.getinfo可能返回错误的函数名称,但它还会返回函数对象本身,您可以使用该对象在表中查找规范名称。这也可以用作监视哪些功能的过滤器:

local watch = {}
debug.sethook(function()
   local info = debug.getinfo(2)
   if watch[info.func] then
      print(watch[info.func], "was called")
      -- This *should* not cause a recursion, as
      -- the hook *should* be disabled inside the hook
      -- callback.
   end
end, "c")

-- Somewhere else in your code
watch[print] = "print"

print("foobar")

但是,如果打算将其用于调试以外的其他用途,则应尽可能避免使用debug.sethook,因为它可能会与依赖调试钩子的其他库(例如luacov举例)混为一谈;在这种情况下,最好替换函数:

do local p = print
   function print(...)
      p("Print was called!")
      return p(...)
   end
end

但请记住,您不应更改ahy函数的行为,否则可能会破坏其他地方的代码。这还包括运行时;如果您要执行一个非常高效的功能而做很多繁重的工作,则可能会以意想不到的方式完全破坏性能。

还请记住,某些库会本地化它们希望使用的全局函数。尽管这通常是非常糟糕的做法,但似乎有一个神话,它神奇地使Lua代码更快(这是有这个神话的原因,但在大多数情况下是不正确的);这意味着您的替换必须在before

加载任何这些库之前进行。

出于同样的原因,顺便说一句,除非您知道需要持久地引用原始函数,否则通常不应该对全局变量进行本地化,就像上面示例中的p变量一样。

© www.soinside.com 2019 - 2024. All rights reserved.