lua-users wiki: Lua Carp (original) (raw)

lua-users home

Lua's [error] function accepts an optional level parameter indicating who up the call stack is to be blamed for the error when generating the error message. Now, this is not always scalable since keeping track of the level can be error prone (especially following refactoring), and a function that uses error may be called at multiple different levels.

Solution A

The solution below is based on [Perl's Carp] module but adapted to Lua. This provides a croak function as a replacement for error. croak calls error but before doing so, it determines which level to use by looking up the call stack until the current environment changes.

First, here's the main module:

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

  if env ~= current_env then
    
    error(message, level)
  end
elseif string.find(result, "(invalid level)") then
  break
end
level = level + 1

end end

return M

Now let's say you write a module:

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

And you write a program that uses the module:

local Calc = require "Calculator"

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

main()

Here's the output:

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]: ?

Note: this might not work correctly with tail calls. Tail calls will be skipped since Lua does not report an environment for them.

Alternate Solutions

A second, alternate approach mentioned by RiciLake was to write a custom traceback function, the idea being that the traceback function checks the env table at each level and doesn't start producing tracebacks until it changes from the current one.

A third approach mentioned by RiciLake was to simply check the object at stack index 1 in each frame, the idea being that if you were nesting method calls, you'd always have the same self, but that sometimes overshot. There was a desire to in some way to mark the stack, but it wasn't obvious how to do that.

TODO--comments on the merits of these approaches, anyone?

Note: Code is Lua 5.1 (not 5.0) compatible.

--DavidManura


RecentChanges · preferences
edit · history
Last edited August 8, 2007 9:16 pm GMT (diff)