Lua Tricks: Detect Accidental Global Creation

Learning Lua can be a little tricky. The idea of variables implicitly being global can be confusing for some people, so using new users often dropped the much-needed local keyword.  If you are concerned about accidentally creating globals there is a quick and easy check you can do you show you when/where you are creating them!

To implement this we are going to create a new metatable for the globals table.  If you didn’t already know, all globals in Lua are stored in a table called the “environment“.  It is accessible anywhere in Lua by accessing a table called “_G“.  The fact that all globals are stored in the same table allows us to take advantage of a nifty metamethod call __newindex.

__newindex – From lua-users.org
Control property assignment. When calling “myTable[key] = value”, if the metatable has a __newindex key pointing to a function, call that function, passing it the table, key, and value.

  • Use “rawset(myTable,key,value)” to skip this metamethod.
  • (If the __newindex function does not set the key on the table (using rawset) then the key/value pair is not added to myTable.)

This is exactly what we’re looking for!
[lua]
setmetatable(_G, {__newindex =
function(self, key, value)
if not Engine:AllowGlobals() then
Engine:StopWithError(“Invalid Global created”)
else — Set value normally
rawset(self, key, value)
end
end
})
[/lua]

With this setup, you can see what this example will do:

[lua]
— OK
local testVar1 = 5

— Not OK, invalid global
testVar2 = 17
[/lua]

 

You can handle the invalid global creation however you want.  In my case I called a function that would halt the game and present the user with an error where it occurred:Invalid Global

 

Another nice trick you can do is make a global function that will explicitly create a global.  This clearly outlines when you are creating a new global and can make your code a bit easier to follow.  A example of such a function looks like this:

[lua]
DeclareGlobal = function(name, value)
— Allow creation of our new global
Engine:SetGlobalCreation(true)

_G[name] = value

— Turn off global creation
Engine:SetGlobalCreation(false)
end

–[[ Example usage ]]
— Not OK
testGlobal = 5

— OK
DeclareGlobal(“testGlobal”, 5)
[/lua]

You can expand this idea to work with any table you’d like as well. While I won’t go into in this post, you could easily set up a system in which you must explicitly declare key/values in tables to help prevent erroneous variable creation.

Hopefully this can help a few people from bashing their head on their keyboard because of a few typos!

Jake

I am a dedicated student developer who enjoys tools and game engine programming. I’d love to experience as many different aspects of the software industry as possible so I can find which part fits me best.

Leave a Reply

Your email address will not be published. Required fields are marked *