Lua Carp

lua-users home
wiki

Lua 的 [error] 函数接受一个可选的 level 参数,该参数指示在生成错误消息时,调用堆栈中哪个级别应该被指责。现在,这并不总是可扩展的,因为跟踪级别可能会容易出错(尤其是在重构之后),并且使用 error 的函数可能会在多个不同的级别被调用。

解决方案 A

下面的解决方案基于 [Perl 的 Carp] 模块,但已适应 Lua。这提供了一个 croak 函数来替换 errorcroak 调用 error,但在这样做之前,它会通过查找调用堆栈来确定要使用的级别,直到当前环境发生变化。

首先,这是主模块

-- Carp.lua
-- This package is based on Perl Carp
-- (http://search.cpan.org/~nwclark/perl-5.8.8/lib/Carp.pm)
-- David Manura, 2006-09, 2007-07

local M = {}

function M.croak(message)
  local current_env = getfenv(2)
  local level = 2
  while true do
    local is_success, result = pcall(function()
      return getfenv(level + 2)
    end)
    if is_success then
      local env = result
      --print("DEBUG:level", level, env._NAME)
      if env ~= current_env then
        --print("DEBUG:found", level, env._NAME)
        error(message, level)
      end
    elseif string.find(result, "(invalid level)") then
      break
    end
    level = level + 1
  end
end

return M

现在假设你编写了一个模块

-- Calculator.lua

-- Create environment for module (needed for Carp)
local env = setmetatable({}, {__index = _G})
setfenv(1, env)

local M = {} 
local Carp = require "Carp"

function M.calculate3()
  Carp.croak("calculation failed")
  return 1
end

function M.calculate2()
  local result = M.calculate3()
  return result + 1
end

function M.calculate()
  return M.calculate2()
end

return M

你编写了一个使用该模块的程序

-- example.lua
-- This uses the calculator module.

local Calc = require "Calculator"

local function main()
  local result = Calc.calculate()
  print(result)
end

main()

这是输出

lua: example.lua:7: calculation failed
stack traceback:
        [C]: in function 'error'
        ./Carp.lua:20: in function 'croak'
        ./Calculator.lua:10: in function 'calculate3'
        ./Calculator.lua:15: in function <./Calculator.lua:14>
        (tail call): ?
        example.lua:7: in function 'main'
        example.lua:11: in main chunk
        [C]: ?

注意:这可能不适用于尾调用。尾调用将被跳过,因为 Lua 不会为它们报告环境。

替代解决方案

RiciLake 提出的第二种替代方法是编写一个自定义的回溯函数,其思想是回溯函数检查每个级别的 env 表,并且只有在 env 表与当前 env 表不同时才开始生成回溯。

RiciLake 提出的第三种方法是简单地检查每个帧中堆栈索引 1 处的对象,其思想是,如果你嵌套了方法调用,你将始终具有相同的 self,但有时会超出范围。人们希望以某种方式标记堆栈,但如何做到这一点并不明显。

TODO--关于这些方法优缺点的评论,有人吗?

注意:代码与 Lua 5.1(而不是 5.0)兼容。

--DavidManura


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2007 年 8 月 9 日凌晨 3:16 GMT (差异)