The Global Table, _G

Everything in Lua is a table, including variables. Variables that you don't explicitly label as local, are placed into a table known as _G. Today, I learn a bit about how to work with it.


_G



This global table can be used like any other table. I was curious as to what's stored in it, so I wrote a test. The first thing I discovered was that _G is an array, and not a dictionary. That means I can iterate over it using pairs. I wrote a test program to dump the kind of things stored in _G.



Screen Shot 2021-03-09 at 9.09.28 AM

_G contains a lot of different things, everything from strings to coroutines. Well look at that! _G also has a table named _G. Let's dump that. I get the same thing. So, _G has a reference to itself. Is that important? It's probably important to the functioning of Lua. I could go through and dump all of the things within _G, but I won't do that now.

Because _G is just a table, albeit a special one, I can store my own data and modify _G. In fact, when I don't declare a variable as local, its stored in _G.

One problem with Lua is the it isn't a typed language. That can cause problems if I try to use variables that are either not declared or mistyped. I can fix this by setting up a way to track which variables are actually declared. I need a table to track my program's variables. I'm going to call it myvars and attach it to _G.

_G.myvars = {} // create an empty table to track my variables.

I need to set the metatable for _G as _G. Hold on… Remember that nested _G in _G? I'm betting that _G is its own metatable! That would explain the table dump. The reason I need set it again is to refresh the reference now that I've modified the base _G.

setmetatable(_G, _G) // set _G as its own metatable.

So what should I store in myvars? Well, I want to store variables I declare. I can do this as either an array or a dictionary. I like dictionaries. I can index into myvars with the variable name: _G.myvars[var]. What do I want to make the value? I'll just make this a boolean to indicate if the variable is defined or not. I need a function to insert the variable into myvars and set the flag.

_G.Create = function (k,v)
_G.myvars[k] = true — I don't see a valid reason for a false here.
end


We should now be able to do this:


Screen Shot 2021-03-09 at 10.17.40 AM

In lines 9-10 I use my function to create a variable abc and then assign a value to it. That's what I want. In lines 13-14, I create a variable def and assign a value to it. That shouldn't work because I haven't declared def using Create.


When I run this, I have an obvious problem.


Screen Shot 2021-03-09 at 9.38.42 AM

The reason is _G still returns any variable stored in it. I need to adjust _G so that it only returns variables that are declared through my Create function.

I think I need to modify the __index function of _G. The __index metamethod is called to retrieve values if the value isn't nil. __index does the variable lookup. I have to get __index to search the myvars table and return a value only if it exists in myvars.

_G.__index = function(v,k)
if not _G.myvars[k] then
print("ERROR - Undefined variable: " .. k)
return nil
end
— we found the variable return it using the normal Lua routes…
return rawget(v,k)
end

rawget
retrieves a variable, but it bypasses __index. I do this because if I just had:

return _G.myvars[k]

I might end up calling __index again in recursive loop.

In addition to __index, I need to modify __newindex which assigns values to a missing key. It's the complement of __index.

_G.__newindex = function (x,k,v)
if not _G.myvars[k] then
print("ERROR - Can't assign to undeclared variable" .. k)
else
rawset(x,k,v)
end
end

Here, I just check to see if the variable exists in myvars. If it does, I use rawset to bypass __newindex the same way I used rawget to bypass __index.

Here is my final code:

Screen Shot 2021-03-09 at 10.03.03 AM


And this is what happens when I run it:


Screen Shot 2021-03-09 at 10.03.58 AM

Pretty cool! We now have a method of ensuring a variable is declared without using types. One thing I tried was to dump myvars, but I'm either doing something wrong, or my guess is that by modifying _G.__index, I've mangled iteration over myvars, but not the rest of _G for some reason.



Screen Shot 2021-03-09 at 10.13.42 AM

This returns:



Screen Shot 2021-03-09 at 10.16.04 AM

…and nothing else.

That's enough for today.



This site does not track your information.