Как создать функцию, которая вызывает функцию при вызове определенной функции? - PullRequest
5 голосов
/ 02 апреля 2020

Я пытался создать функцию, которая запускает событие / функцию после вызова определенной функции. Я хочу, чтобы использование было таким:

local a = nil

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

Я знаю, что есть функция debug.sethook (), но я не совсем знаю, как ее использовать.

Ответы [ 3 ]

1 голос
/ 02 апреля 2020

Есть два способа сделать это, но у каждого есть свои подводные камни, с которыми вам нужно иметь дело.

Один из способов - использовать 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

Основной недостаток этого подхода заключается в том, что он не гарантирует, что он выведет имя функции так, как вы ожидаете, поскольку значения могут быть связаны с разными именами / контейнерами для них.

Например, будет напечатано a is called:

local a = print
a("do something")

И будет напечатано ? is called:

local a = {print}
a[1]("do something")
1 голос
/ 03 апреля 2020

В вопросе отсутствует очень важная деталь: почему?

Если вы хотите это для отладки, тогда debug.sethook, вероятно, является более подходящим вариантом. Как отметил Пол Кульченко, 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 код (есть причина для этого мифа, но в большинстве случаев это не так); это означает, что ваша замена должна произойти до того, как загрузится любая из этих библиотек.

По этой же причине, кстати, вам обычно не следует локализовать глобальные переменные, если вы не уверены, что вам нужен постоянный доступ. ссылка на исходную функцию, как в случае с переменной p в примере выше.

1 голос
/ 02 апреля 2020

Простейшим способом было бы обернуть оригинальную функцию:

do
  local _print = print

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

print("hello world")

вывод:

print (...) вызывается.

hello world

то, что мы слышали, сохранило оригинал print в локальной переменной _print, которая является повышением по сравнению с новой глобальной функцией печати, которую мы создали после сохранения оригинала.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...