知道是否可以调用一个值

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

请注意,此问题与纯Lua有关。我无权访问任何模块或C端。此外,我无法使用IO,操作系统或调试库。

我想做的是一个接收作为参数的函数:

  • 一个数字,等于秒
  • 可调用的值

“可调用值”是指可以调用的值。可以是:

  • 功能
  • 具有允许调用(通过__call元方法)的元表的表

这是可调用表的示例:

local t = {}
setmetatable(t, {
  __call = function() print("Hi.") end
})
print(type(t)) --> table
t() --> Hi.

这里是功能:

function delay(seconds, func)
  -- The second parameter is called 'func', but it can be anything that is callable.
  coroutine.wrap(function()
    wait(seconds) -- This function is defined elsewhere. It waits the ammount of time, in seconds, that it is told to.
    func() -- Calls the function/table.
  end)()
end

但是我有问题。如果参数'func'不可调用,我希望函数抛出错误。

我可以检查它是否为功能。但是,如果它是带有允许调用的元表的表,该怎么办?如果表的元表不受__metatable字段的保护,那么我可以检查该元表是否可调用,否则,我将如何处理?

[请注意,我还考虑过尝试使用pcall调用'func'参数,以检查它是否可调用,但是要这样做,我需要过早调用它。

基本上,这是问题所在:我需要知道一个函数/表是否可以调用,但是没有尝试调用它。

lua
3个回答
5
投票

通常,如果该元表不希望您能够获得它(通过将__metatable定义为特殊的东西),那么您就不会得到它。不是来自Lua。

但是,如果您想作弊,可以始终使用debug.getmetatable,它将返回与该对象关联的元表。


您不必使用pcall提前调用任何内容。观察:

pcall(function(...) return PossibleFunction(...) end, <insert arguments here>)

1
投票

尝试细化Nicol的答案仍然存在需要调用表的问题,但是它告诉提供的表是否实际上是可调用的。即使表是可调用的,如果在执行pcall()元方法期间发生错误,false也会返回__call()

local result = {pcall(PossibleFunction, ...)}

if not result[1] then
    if result[2]:match"^attempt to call" then
        error("The provided value is not callable.")
    else
        -- Table was callable but failed. Just hand on the error.
        error(result[2])
    end
end

-- Do something with the results:
for i = 2, #result do
    print(result[i])
end

但是以这种方式检查错误消息感觉不是很“干净”(如果用本地化的错误消息对使用过的Lua解释器进行了修改?)]

function iscallable(x)
    if type(x) == 'function' then
        return true
    elseif type(x) == 'table' then
        -- It would be elegant and quick to say
        -- `return iscallable(debug.getmetatable(x))`
        -- but that is actually not quite correct
        -- (at least in my experiments), since it appears
        -- that the `__call` metamethod must be a *function* value
        -- (and not some table that has been made callable)
        local mt = debug.getmetatable(x)
        return type(mt) == "table" and type(mt.__call) == "function"
    else
        return false
    end
end

return iscallable

那么你可以做

> = iscallable(function() end)
true
> = iscallable({})
false
> = iscallable()
false
> = iscallable(nil)
false
> x = {}
> setmetatable(x, {__call=function() end})
> = iscallable(x)
true

如果您无权访问调试库,那么您可能很难完全准确,因为您可能会弄乱元表。您可以使用pcall,但很难正确区分出错误。即使您按照此处其他答案的方式搜索特定的错误字符串,也可能是由于该函数内部的某些内容无法调用,如果有意义的话,该特定值也不是不可调用的。


0
投票
function iscallable(x)
    if type(x) == 'function' then
        return true
    elseif type(x) == 'table' then
        -- It would be elegant and quick to say
        -- `return iscallable(debug.getmetatable(x))`
        -- but that is actually not quite correct
        -- (at least in my experiments), since it appears
        -- that the `__call` metamethod must be a *function* value
        -- (and not some table that has been made callable)
        local mt = debug.getmetatable(x)
        return type(mt) == "table" and type(mt.__call) == "function"
    else
        return false
    end
end

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