Lua Get/Set Metatable - lua

local ents = {
GetLocalPlayer = function()
local tbl = {
localplayer = {"Ava", "1", {213,234,234}},
GetIndex = function(self)
return self.localplayer[2]
end,
}
setmetatable(tbl, getmetatable(tbl.localplayer))
return tbl
end
}
local function main()
print(ents.GetLocalPlayer()[2])
end
main() print returns nil. If I was to do ents.GetLocalPlayer():GetIndex() however, it returns 1.
The idea is to have the default return value to be localplayer if I don't do things such as GetIndex()

A table has no default metatable, which is why your getmetatable call returns nil. In order to do anything, the second argument to setmetatable must be a table that has at least one metamethod. (__index is the most common metamethod.)
The solution is to change getmetatable(tbl.localplayer) to {__index = tbl.localplayer}.

Related

Lua/Luajit: Indexing and named method at the same time?

The Lua PIL and Luajit FFI tutorial gave two usages of __index in the metatable.
One is for indexing like obj[123], e.g.,
__index = function (self, k) return self._data+(k-self._lower)
The other usage is to define named methods, as given in the tutorial,
__index = { area = function(a) return a.x*a.x + a.y*a.y end, },
We can then make function call like obj:area().
Can I do both at the same time, e.g., direct indexing and named methods?
The answer, as is usual for extra-interesting code in Lua, is more metatables.
When your __index metamethod is actually a table, Lua simply does a standard table access on the given table. This means you can set a metatable on your metatable. Then you can set an __index metamethod on this "meta-metatable".
foo = function()
print("foo")
end
bar = function(_, key)
return function()
print(string.format("bar: %s", key))
end
end
mmt = { __index = bar }
mti = { foo = foo }
mt = { __index = mti }
t = {}
setmetatable(mti, mmt)
setmetatable(t, mt)
t.foo() -- prints: "foo"
t.bar() -- prints: "bar: bar"
t.baz() -- prints: "bar: baz"
With this, when you try to access a field which is absent in both tables, lua will first try to access the top-level table which will access the first metatable which will then call your metamethod in the second metatable.
There is also another, possibly more straight forward, answer: Use your __index metamethod to check another table for named fields:
foo = function()
print("foo")
end
f = { foo = foo }
bar = function(_, key)
if f[key] then return f[key] end
return function()
print(string.format("bar: %s", key))
end
end
mt = { __index = bar }
t = {}
setmetatable(t, mt)
t.foo() -- prints: "foo"
t.bar() -- prints: "bar: bar"
t.baz() -- prints: "bar: baz"
Tested on Lua 5.3.

lua: how can i get the raw string in __tostring metamethod?

code below:
local t = {}
setmetatable(t, {__tostring = function(self) return 'MyTable is: '..tostring(self) end})
print(t)
running the code will cause error: "C stack overflow". Because in __tostring metamethod, tostring(self) will invoke the __tostring metamethod, that's a dead loop.
Is there a way to get the raw string of the value "t"?
To do what you're trying to do from Lua, you basically have to unset the metatable from the main table, then call tostring on it, then set the metatable back. Like this:
setmetatable(t, {__tostring = function(self)
local temp = getmetatable(self)
setmetatable(self, nil)
local ret = 'MyTable is: ' .. tostring(self)
setmetatable(self, temp)
return ret
end,
})
Also, note that the __tostring metafunction is supposed to return the string, not merely print it.

Referencing functions enclosing table in Lua

I have a helper function which returns a table like this:
function of_type(expected_type)
return {
expected = expected_type,
matches = function(value) return type(value) == expected_type end,
describe = "type " .. expected_type
}
end
Now this was fine with other matchers but here I would like to store the type(value) to a field in the same table when the matches function is called. Something like this:
function of_type(expected_type)
return {
expected = expected_type,
mismatch = nil, -- set it to nil on initialization
matches = function(value)
mismatch = type(value) -- how to do this?
return type(value) == expected_type
end,
describe = "type " .. expected_type
}
end
Is this possible?
Yes, but you need to split it into steps:
function of_type(expected_type)
local tbl = {
expected = expected_type,
mismatch = nil, -- set it to nil on initialization
describe = "type " .. expected_type
}
tbl.matches = function(value)
tbl.mismatch = type(value)
return type(value) == tbl.expected
end
return tbl
end
-- testing it
local foo = of_type("string")
print(foo.matches(1), foo.matches("1"))
This should output false true as you would expect.
Essentially, tbl.matches will store the reference to tbl (it's called "upvalue") and will be able to modify all the fields in that table (including the reference to itself it in).
Another way to do this would be the following (notice the changes in the tbl.matches function). Instead of capturing it as an upvalue, you can use tbl:method semantic and pass tbl as implicit self parameter:
function of_type(expected_type)
local tbl = {
expected = expected_type,
mismatch = nil, -- set it to nil on initialization
describe = "type " .. expected_type
}
function tbl:matches(value)
self.mismatch = type(value) -- how to do this?
return type(value) == self.expected
end
return tbl
end
local foo = of_type("string")
print(foo:matches(1), foo:matches("1"))
This will print the same result. Notice that you are using foo:matches notation to make foo to be passed as the first parameter (references as self in the method). This is the same as using foo.matches(foo, 1).
You don't. Well, not without storing a copy of the table, or passing the function the table as a parameter. Until all of the statements of the table constructor have been processed, the table doesn't exist yet. And since you never stored it anywhere (in this function), your function can't name it in order to find it.
So you should give it a name, even just for a moment:
function of_type(expected_type)
local temp = nil
temp = {
expected = expected_type,
mismatch = nil, -- set it to nil on initialization
matches = function(value)
temp.mismatch = type(value) -- Like this
return type(value) == expected_type
end,
describe = "type " .. expected_type
}
return temp
end
This works because Lua will store temp as an upvalue. Thus, the function you're creating will see changes in temp, such as when you set it to a table value. And since it's a local variable, it won't be visible outside of this function.

lua metatable registration from c question

Hello I have the following bit of code which seems to work, but I'm not sure why - I've built a testclass as follows
class testclass {
int ivalue;
public:
int getivalue();
void setivalue(int &v);
};
and then registered the testclass (bits left out for the actual functions but they're pretty basic). It's the registration of the metatables I'm not following. (etivalue and setivalue are c functions that call the class functions of the same name)
static const struct luaL_Reg arraylib_f [] = {
{"new", new_testclass},
{NULL, NULL}
};
static const struct luaL_Reg arraylib_m [] = {
{"set", setivalue},
{"get", getivalue},
{NULL, NULL}
};
int luaopen_testclass (lua_State *L) {
luaL_newmetatable(L, "LuaBook.testclass");
lua_pushvalue(L, -1); /* duplicates the metatable */
lua_setfield(L, -2, "__index");
luaL_register(L, NULL, arraylib_m);
luaL_register(L, "testclass", arraylib_f);
return 1;
}
The bit I don't understand is I'm adding the functions to the __index for the metatable but
when I run
a = testclass.new()
a:set(10)
print(a:get())
Then it works as expected. The bit I don't understand is why the set is being called when I think I've loaded it in the __index metatable? Is that what I've done or something else?
tia
int luaopen_testclass (lua_State *L) {
luaL_newmetatable(L, "LuaBook.testclass"); //leaves new metatable on the stack
lua_pushvalue(L, -1); // there are two 'copies' of the metatable on the stack
lua_setfield(L, -2, "__index"); // pop one of those copies and assign it to
// __index field od the 1st metatable
luaL_register(L, NULL, arraylib_m); // register functions in the metatable
luaL_register(L, "testclass", arraylib_f);
return 1;
}
That code is equivalent to the example Lua code:
metatable = {}
metatable.__index = metatable
metatable.set = function() --[[ stuff --]] end
metatable.get = function() --[[ stuff --]] end
I assume that 'new_testclass' C function sets the metatable "LuaBook.testclass" for the returned table.
In your code you dont add functions to the metatable __index field. You assign pointer to metatable to that metatable's field named __index, and you register set and get functions to it.
Now, if you set that metatable to the value returned from 'new_testclass' function (which I assume you do) - lets call that value 'foo', and you call foo:set(10), than Lua:
checks that there is no such field as 'set' in 'foo'
sees that 'foo' has a metatable
looks at that metatable's __index field - sees it's a table
checks if that table assigned to __index field has a field 'set' and it's value is a function
calls 'set' method passing 'foo' as self parameter
I hope that this will help you figure out whats going on here.
If I understand your question, you are asking how the set() get() get invoked through the __index metamethod.
The code can be expressed in pure lua:
local o = {}
function o.get(self)
return self.ivalue
end
function o.set(self, val)
self.ivalue = val
end
a = {}
mt = {
__index = function(t, n)
return o[n]
end
}
setmetatable(a, mt)
print(a:get())
a:set(10)
print(a:get())
results:
nil
10
In this example the mt table is set as the a table's metatable. The __index metamethod is invoked for both get and set since neither get or set currently exist in table a.
If this example is changed instead to this:
local o = {}
function o.get(self)
return self.ivalue
end
function o.set(self, val)
self.ivalue = val
end
a = {}
function a.get(self)
print('here')
return self.ivalue
end
mt = {
__index = function(t, n)
return o[n]
end
}
setmetatable(a, mt)
print(a:get())
a:set(10)
print(a:get())
results:
here
nil
here
10
In this case the __index metamethod is NOT invoked for get() since a get index already exists in the a table.
Many interesting constructs can be created using metamethods, once you understand how they work. I suggest reading 13.4.1 - The __index Metamethod in PiL and work through a few more examples. All of the above can also be done from the c api.

How can I change every index into a table using a metatable?

I'm trying to write a metatable so that all indexes into the table are shifted up one position (i.e. t[i] should return t[i+1]). I need to do this because the table is defined using index 1 as the first element, but I have to interface with a program that uses index 0 as the first element. Since reading Programming in Lua, I think that I can accomplish what I want with a proxy table, but I can't seem to get it working. So far, I have this:
t = {"foo", "bar"}
local _t = t
t = {}
local mt = {
__index = function(t, i)
return _t[i+1]
end
}
setmetatable(t, mt)
However, this does not create the expected result. In fact, it doesn't return any values at all (every lookup is nil). Is there a better way to do this, or am I just missing something?
t = {"foo", "bar"}
local _t = t
t = {}
local mt = {
__index = function(t, i)
return _t[i+1]
end
}
setmetatable(t, mt)
print(t[0])
outputs "foo" for me when run here: http://www.lua.org/cgi-bin/demo

Resources