So I know that lua will look to a table's metatable if it doesn't contain the variable I reference, but it seems wrong that when I attempt to set a variable that doesn't exist yet in a table it sets it in the metatable instead.
Heres an example of what i mean
a = {__index = {tbl1 = {var = 1}, tbl2 = {var = 2}}}
b = setmetatable({}, a)
print(b.tbl1.var, a.__index.tbl1.var)
b.tbl1.var = 2
print(b.tbl1.var, a.__index.tbl1.var)
In this code it will replace the metatables variable instead of setting it in the table im referencing.
However this does not occur with this code
a = {__index = {4, 5, 6}}
b = setmetatable({}, a)
print(b[1], a.__index[1])
b[1] = 2
print(b[1], a.__index[1])
Is there more work needed when using metatables and nested tables?
Or is there a way around this?
In this code it will replace the metatables variable instead of setting it in the table im referencing.
I think this is to be expected; the code retrieves tbl1 key and sets a field in the table associated with that key. The key doesn't exist in the table, only in the metatable, so that's where it's set. If you add the same key in the table b = setmetatable({tbl1 = {}}, a), you'll see that the value is set in that table.
Is there more work needed when using metatables and nested tables? Or is there a way around this?
I'm not sure what result you expect. Lua doesn't do autovivification, which would make tbl.foo = 1 to create table tbl if it didn't already exist. If the field tbl1 is already present in the table, the the behavior is exactly what you'd expect. If it's present in the metatable, and you modify its field, this is exactly where it's going to be modified.
Related
I'm adding a string to a table in lua. When I use the table in a function the original table is getting altered. I'm only a beginner but I thought that the function could not do that because it is outside of it's scope. Is there something obvious I'm missing?
local testTable= {}
testTable.name = {}
testTable.name[1] = "Jon"
print(testTable.name[1])
local function testFunc(a)
a.name[1] = "Bob"
end
local newTable = testTable
testFunc(newTable)
print(testTable.name[1])
I expected the output to be:
Jon
Jon
The actual output is:
Jon
Bob
How can the testFunc change the testTable?
You assign testTable's address to newTable, so testTable and newTable point to the same table.
If you want to output be
Jon
Jon
You should copy the table when you assign newTable.
You can copy the table like this function:
function table.copy(old)
local new = {}
for k, v in pairs(old) do
new[k] = v
end
return new
end
When I use the table in a function the original table is getting altered. ... I thought that the function could not do that because it is outside of it's scope.
Local variables have their own scope, but tables do not. Two things to remember:
Variables store references, not values. (This only makes a difference for mutable values.)
Tables are mutable, i.e., they can be changed internally.
Breaking it down:
local newTable = testTable
In this line, you're assigning one variable to another, so both variables refer to the same table.
We mutate a table by assigning to an index within that table, so testFunc alters whatever a (actually a.name) refers to. This is handy, because it allows us to write functions that mutate tables that we pass as arguments.
The following function does nothing, like you would expect, because it assigns a new table to the bare name a (which happens to be a local variable):
local function doNothing(a)
a = {name = {'Bob'}}
end
The Lua 5.3 Reference Manual (in this part, scroll down) says:
__newindex: The indexing assignment table[key] = value. Like the index event, this event happens when table is not a table or when key is not present in table. The metamethod is looked up in table.
However I don't understand that the metamethod __newindex happens when table is not a table. What does that mean? I did try to re-assign a local with nil, but it didn't work (yes, I know it doesn't make sense to re-assign the table, but this would help it to be garbage-collected).
local v = {};
setmetatable(v, {
__newindex = function(t,k,v)
print("Aaahhh...!");
end
});
v = nil;
I'm using this online compiler to test it.
From the page you cited:
You can replace the metatable of tables using the setmetatable
function. You cannot change the metatable of other types from Lua code
(except by using the debug library (ยง6.10)); you should use the C API
for that.
You can't use setmetatable to change the metatable for something that's not a table, so you won't be able to verify what you expect (that the __newindex method is called when you index into something that's not a table).
Your code runs because you are setting the metatable for a table. (local v = {} creates a table.)
But reassigning the variable v to be something else means you no longer have a way to get to the table you created. If your last line were v[5] = 'Hello', then you would see that your metamethod is invoked.
EDIT
Reading your edit, it looks like you expected __newindex to get called when the table was garbage collected? I think you misunderstood the statement that __newindex is called "when table is not a table." That means if you did something like this:
local v = 5
print(v[3]) -- indexing into something that's not a table
__newindex would be called (because in the expression table[key], which here is v[3], table is not a table). But you can't actually set up __newindex via the setmetatable method, because that method only works on tables.
I want to "unhook" a metatable from a table and was wondering if:
tbl = setmetatable(tbl, false) -- or nil
is the correct way to do this? I could not find any info about how to do it correctly. Do I need to use an assignment operator?
Also, would this be enough to destroy the metatable attached to the table if the metatable never had a reference and was anonymous?:
tbl = setmetatable({}, {__index = something})
-- later on:
tbl = nil
and the garbage collector would be enough to remove both tables?
According to the Lua reference, which you should always consult befor putting up a question here, setmetatable(tbl, nil) will delete the metatable of table tbl unless tbl's original metatable is protected. Or let's better say it does not delete the metatable but the reference to it. The table that served as metatable will of course not be deleted as long as there are other references to it.
Befor you ask people if a simple function call works, try it yourself.
You can use https://www.lua.org/cgi-bin/demo or any other Lua interpreter and you get your answer in seconds without involving anyone else.
Running this code:
setmetatable({}, false)
or
setmetatable({})
will result in
input:1: bad argument #2 to 'setmetatable' (nil or table expected)
Now you know that you cannot enter false and you have to enter nil explicitly.
To checkout that __metatable thing you would have read in the reference manual you could then try this code
local tbl = setmetatable({}, {__metatable = true})
setmetatable(tbl, nil)
Which results in the following output:
input:2: cannot change a protected metatable
To the second part of your question:
tbl = nil will not delte the table referenced by tbl. It will only remove the reference tbl to it.
local a = {}
local b = a
b = nil
print(a)
a is still a table. You only removed one of its references.
Once there is no reference left the garbage collector may collect the table.
setmetatable(tbl, {}) will establish a reference to the table returned by the table constructor {} and store that reference somewhere in the guts of tbl.
If tbl was the last reference to that table it will be collected as garbage at some point. Then of course the only reference to the table you set as metatable will also be gone and it will removed as well.
If you do something like that:
local a = {}
local b = setmetatable({}, a)
,
a = nil will not delete b's metatable
So yes it will remove both tables if no other reference to either one of them is left.
I am confused of the following two syntaxes using "."
From what I understand, __index is called when a key doesn't exist in a table but exists in its metatable. So why does the list table call __index and then assign itself to list.__index?
list = {}
list.__index = list
setmetatable(list, { __call = function(_, ...)
local t = setmetatable({length = 0}, list)
for _, v in ipairs{...} do t:push(v) end
return t
end })
function list:push(t)
if self.last then
self.last._next = t
t._prev = self.last
self.last = t
else
self.first = t
self.last = t
end
self.length = self.length + 1
end
.
.
.
local l = list({ 2 }, {3}, {4}, { 5 })
Does Window.mt simply create a table? Why do we need Window = {} as a namespace here?
Window = {} -- create a namespace
Window.mt = {} -- create a metatable
Window.prototype = {x=0, y=0, width=100, height=100, }
function Window.new (o)
setmetatable(o, Window.mt)
return o
end
Window.mt.__index = function (table, key)
return Window.prototype[key]
end
w = Window.new{x=10, y=20}
print(w.width) --> 100
So why does the list table call __index and then assign itself to list.__index?
Nowhere in your code does the list table call __index. The assignment part however is a common Lua idiom (aka. hack) to save some memory. Conceptually there are 4 different kinds of tables involved:
list objects (the tables created via {length=0} in your code)
a metatable (containing an __index field) that modifies the behavior of list objects when you try to access non-existing fields in the object
the list class, which holds all the methods for list objects (like the push method), and also serves as a constructor for list objects
a metatable (containing a __call field) for the list class, so that you can call the list table as if it were a function
As metatable fields always start with two underscores (__), and normal methods usually don't, you can put metatable fields and normal methods side by side into a single table without conflict. And this is what happened here. The list class table also serves as metatable for list objects. So using this trick you can save the memory you would normally need for the separate metatable (the size in bytes for Lua 5.2 on an x86-64 Linux is shown in square brackets in the table title bars, btw.):
Does Window.mt simply create a table?
No, {} creates a table. However, this new table is saved under key "mt" in the Window table, probably to give users of this Window "class" direct access to the metatable that is used for window objects. Given only the code you showed this is not strictly necessary, and you could have used a local variable instead.
Why do we need Window = {} as a namespace here?
In principle, you could store Window.mt, Window.new, and Window.prototype separately, but that would get cumbersome if you have multiple "classes" like Window. This way you can avoid name clashes, and using the Window "class" looks nicer.
Another reason might be that require can only return a single value from a module definition, and if you want to export multiple values (like new, mt, and prototype) from a module, you need a table to wrap them together (or use global variables, but that is considered bad style).
tbl1 = {1}
tbl2 = tbl1
table.remove(tbl2,1)
print(tbl1[1])
-- >> nill
The above example is a simplification of the problem in my code, by removing a index from tbl2, it also removes from tbl1, is there a reason for this to be happening?
Variables in Lua are references to objects, and so a=b sets the variable named a to refer to the object that b refers to. If b is a table, then after the assignment both a and b point to the same table object.