Hello all! I was taking a look at some metatables tutorials and I found this one, very simple one:
Edit by Znote: Playlist of the 3 videos:
See ya
Edit by Znote: Playlist of the 3 videos:
See ya
Last edited by a moderator:
That's cool tutorial you've got here.Here's a broad but very helpful tutorial for all metatable functions:
http://nova-fusion.com/2011/06/30/lua-metatables-tutorial/
Here's a broad but very helpful tutorial for all metatable functions:
http://nova-fusion.com/2011/06/30/lua-metatables-tutorial/
t = {} -- our normal table
mt = {} -- our metatable, which contains nothing right now
setmetatable(t, mt) -- sets mt to be t's metatable
getmetatable(t) -- this will return mt
t = setmetatable({}, {})
setmetatable
returns its first argument, therefore we can use this shorter form.__index
and __newindex
. The values corresponding to these keys will usually be functions or other tables. An example:t = setmetatable({}, {
__index = function(t, key)
if key == "foo" then
return 0
else
return table[key]
end
end
})
__index
key. Now let's have a look at what this key is all about.t[4]
, t.foo
, and t["foo"]
, for example), and a value hasn't been assigned for that key, Lua will look for an __index
key in the table's metatable (if it has a metatable). If __index
contains a table, Lua will look up the key originally used in the table belonging to __index
. This probably sounds confusing, so here's an example:other = { foo = 3 }
t = setmetatable({}, { __index = other })
t.foo -- 3
t.bar -- nil
__index
contains a function, then it's called, with the table that is being looked up and the key used as parameters. As we saw in the code example above the last one, this allows us to use conditionals on the key, and basically anything else that Lua code can do. Therefore, in that example, if the key was equal to the string "foo" we would return 0, otherwise we look up the table
table with the key that was used; this makes t
an alias of table
that returns 0 when the key "foo" is used.__index
function. This comes in handy if you use the same metatable for multiple tables, supporting code re-use and saving computer resources. We'll see an example of this when we take a look at the Vector
class.__newindex
, which is similar to __index
. Like __index
, it can contain either a function or table.__newindex
key in the metatable. It's the same sort of situation as __index
; if __newindex
is a table, the key and value will be set in the table specified:other = {}
t = setmetatable({}, { __newindex = other })
t.foo = 3
other.foo -- 3
t.foo -- nil
__newindex
is a function, it will be called with the table, key, and value passed as parameters:t = setmetatable({}, {
__newindex = function(t, key, value)
if type(value) == "number" then
rawset(t, key, value * value)
else
rawset(t, key, value)
end
end
})
t.foo = "foo"
t.bar = 4
t.la = 10
t.foo -- "foo"
t.bar -- 16
t.la -- 100
rawget
and rawset
.rawget
and rawset
rawget
allows you to get the value of a key without Lua using __index
, and rawset
allows you to set the value of a key without Lua using __newindex
(no these don't provide a speed increase to conventional way of doing things). You'll need to use these when you would otherwise get stuck in an infinite loop. For example, in that last code example, the code t[key] = value * value
would set off the same __newindex
function again, which would get you stuck in an infinite loop. Using rawset(t, key, value * value)
avoids this.rawset
, the value.+
, -
, etc.), allowing you to make tables support the use of operators on them. For example, say we wanted a table to support the multiplication operator (*
), this is how we would do it:t = setmetatable({ 1, 2, 3 }, {
__mul = function(t, other)
new = {}
for i = 1, other do
for _, v in ipairs(t) do table.insert(new, v) end
end
return new
end
})
t = t * 2 -- { 1, 2, 3, 1, 2, 3 }
__mul
; unlike __index
and __newindex
the keys for operators can only contain functions. The first parameter these functions always receive is the target table, and then the value on the right hand side (except for the unary -
which has the key of __unm
). Here's a list of the supported operators:__add
: Addition (+
)__sub
: Subtraction (-
)__mul
: Multiplication (*
)__div
: Division (/
)__mod
: Modulos (%
)__unm
: Unary -
, used for negation on numbers__concat
: Concatenation (..
)__eq
: Equality (==
)__lt
: Less than (<
)__le
: Less than or equal to (<=
)==
, <
, <=
because you can implement full support for the comparison operators with just those; in fact only ==
and <
are needed.)__call
key, which allows you to call tables as functions. A code example:t = setmetatable({}, {
__call = function(t, a, b, c, whatever)
return (a + b + c) * whatever
end
})
t(1, 2, 3, 4) -- 24
__call
is very useful for many things. One common thing it's used for is forwarding a call on a table to a function inside that table. An example is found in kikito's tween.lua library, where tween.start
can also be called by calling the table itself (tween
). Another example is found in MiddleClass, where a classes' new method can be called by just calling the class itself.__tostring
. If implemented, it's used by tostring
to convert a table into a string, most handy when using a function like print
. Normally, when you try to convert a table to a string, you something in the format of "table: 0x<hex-code-here>", but you can change that using __tostring
. An example:t = setmetatable({ 1, 2, 3 }, {
__tostring = function(t)
sum = 0
for _, v in pairs(t) do sum = sum + v end
return "Sum: " .. sum
end
})
print(t) -- prints out "Sum: 6"
Vector = {}
Vector.__index = Vector
Vector
class, and sets the __index
key to point back at itself. Now, what's going on here? You've probably noticed that we've put all the metatable methods inside the Vector
class. What you're seeing is the simplest way to achieve OOP (Object-Oriented Programming) in Lua. The Vector
table represents the class, which contains all the functions that instances can use. Vector.new
(shown below) creates a new instance of this class.function Vector.new(x, y)
return setmetatable({ x = x or 0, y = y or 0 }, Vector)
end
x
and y
properties, and then sets the metatable to the Vector
class. As we know, Vector
contains all the metamethods and especially the __index
key. This means that we can use all the functions we define in Vector through this new table. We'll come back to this in a moment.setmetatable(Vector, { __call = function(_, ...) return Vector.new(...) end })
Vector
instance by either calling Vector.new
or just Vector
.function t:method(a, b, c)
-- ...
end
function t.method(self, a, b, c)
-- ...
end
-- these are the same
t:method(1, 2, 3)
t.method(t, 1, 2, 3)
Vector
class? Here's an example:a = Vector.new(10, 10)
b = Vector(20, 11)
c = a + b
print(a:len()) -- 14.142135623731
print(a) -- (10, 10)
print(c) -- (30, 21)
print(a < c) -- true
print(a == b) -- false
__index
in Vector
, we can use all the methods defined in the class through the instances.Here's the new domain Lua Metatables Tutorial (https://ebens.me/post/lua-metatables-tutorial/)Link broken, here is backup:
nova-fusion.com/2011/06/30/lua-metatables-tutorial