Reputation: 91
I have been googling this issue to no avail so far so I was wondering if anyone might know the answer - when you index a nil value in lua, the script raises an error - attemp to index 'variableName' - a nil value - is it possible to instead capture that with may be a metamethod, do some processing and not error out? For example if variable 'num' is not defined and you say 'num = 2' you can set the __newindex metamethod to do some processing and you have both the variable name - 'num' and the value - '2' on the stack, but if you say 'num[2] = 3' and 'num' is not defined you error out - instead just like __newindex, I want to capture that event and get access to the name of the nil variable - 'num', the index - 2, and the set value - 3. Any help would be appreciated.
Upvotes: 1
Views: 6415
Reputation: 14565
you can override the __newindex metamethod on the global table.
function OnNewIndex(tbl, key, value)
if tbl[key] == nil then
print(tostring(key).. " doesn't exist.")
end
end
setmetatable(_G, { __newindex = OnNewIndex })
num = 15
however, by default I don't believe lua has this behavior. just doing something like:
num = 15
should just declare the variable 'num' as a global and assign the value 15 to it. are you using lua as part of some framework that has strict checking for this?
edit: an easy way to 'catch' this error without letting it bubble to the surface would be to wrap the assignment in a pcall like this...
pcall(function() num[2] = 15 end)
if you print this, you'll get the output:
false test2.lua:18: attempt to index global 'num' (a nil value)
which at least gives you some indication of what happened, if you named the function, i guess you could potentially try to recover.
function TrySetValue()
num[2] = 15
end
local result, msg = pcall(TrySetValue)
if (not result) then
num = {}
end
result, msg = pcall(TrySetValue)
print(result, msg)
print(num[2])
seems like an awful lot of work when you could probably just fix the code up front.
Upvotes: 1
Reputation: 28991
First, it's easier to help if you state the problem you're trying to solve. Are you trying to prevent uninitialized globals from being used? You can do that with a metatable. Are you trying to make sure script errors never percolate up to your app? You can do that with pcall
. Are you really only trying to catch this one specific usage of a nil
global, and recover from it in some way? The more context you give, the easier it is for others to find a solution you might not have thought of.
As for the situation you described, where you want to catch num[index]
where num
is nil
, you can't really detect that directly.
There are two parts to this:
num
is read from the current environment table (aka _ENV or _G)There's no way you can detect #2, because nil
is not a table so you can't give it a metatable.
However, you can detect #1. Depending on your requirements you could use #1 to return a proxy object which will then allow you to detect #2, however this will mean no global will ever be nil
, so logic like this won't work:
--- initialize globalFoo if it doesn't exists
if not globalFoo then
globalFoo = CreateGlobalFoo()
....
Of course you could always do something like name your proxy object NIL
, so you could write:
if globalFoo == NIL then -- globalFoo is not initilalize
globalFoo = CreateGlobalFoo()
Example of a proxy object that works like that:
local nil_proxy_mt = {
__index = function(t,k) print('Attempted to index nil!') end,
__newindex = function(t,k,v) print('Attempted to index nil!') end,
}
local NIL = setmetatable({}, nil_proxy_mt)
setmetatable (_G, { __index = function(t,k) return NIL end })
With this in place, if you tried either of these:
foo = num[2]
num[2] = 15
You'll get "Attempted to index nil!" and the program continues running.
Upvotes: 1