Pcall 和协程

lua-users home
wiki

pcall 和协程并不总是能很好地协同工作。 [coxpcall] 为 Lua 5.1 重新实现了 pcall/xpcall,使用协程来实现,允许在 pcall 中进行 yield 操作。 还有像 ResumableVmPatch 这样的补丁。 Lua 5.2 (LuaFiveTwo) 实现了类似于 ResumableVmPatch 的方法 [1].

下面使用协程实现了类似于 pcall/error 的函数。 缺点是每个 pcall 都会产生协程构造的开销。 coxpcall 非常类似,但它没有将 error 重新实现为 coroutine.yield

local function tuple(...)
  return {n=select('#', ...), ...}
end

function pcall(f, ...)
  local co = coroutine.create(f)
  local res = tuple(coroutine.resume(co, ...))
  if res[1] and coroutine.status(co) == "suspended" then
    res[1] = false
  end
  return unpack(res, 1, res.n)
end

local handlers = setmetatable({}, {__mode='kv'})

function xpcall(f, err, ...)
  local co = coroutine.create(f)
  handlers[co] = err
  local res = tuple(coroutine.resume(co, ...))
  if res[1] and coroutine.status(co) == "suspended" then
    res[1] = false
  end
  if not res[1] and err then
    res[2] = err(co, res[2])
      -- note: assumes err can accept coroutine as first argument.
  end
  return unpack(res, 1, res.n)
end


function error(e, level_) --FIX:level not handled
  coroutine.yield(e)
end


-- test

local function g(x)
  if x == "fail" then
    error "fail"
  elseif x == "fail2" then
    local y = nil + nil  -- error raised by Lua
  end
end

local function f(x)
  print(1)
  g(x)
  print(2)
  return 3,4
end

print(pcall(f, "ok"))
print(pcall(f, "fail"))
print(pcall(f, "fail2"))
print(xpcall(f, debug.traceback, "fail"))
print(xpcall(f, debug.traceback, "fail2"))

--DavidManura


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2010 年 7 月 4 日凌晨 12:27 GMT (差异)