Metatables: Difference between revisions
>JulienDethurens |
>Crazypotato4 so yeah it took me like a few minutes or so to rewrite this stuff and i might not have done a great job but it's probably still better than what we had before trololo |
||
Line 20: | Line 20: | ||
===setmetatable() and getmetatable()=== | ===setmetatable() and getmetatable()=== | ||
The two primary functions for giving and finding a table's metatable, are setmetatable() and getmetatable(). | The two primary functions for giving and finding a table's metatable, are setmetatable() and getmetatable(). | ||
<code lua> | <code lua> | ||
local x = {} | local x = {} | ||
Line 27: | Line 28: | ||
</code> | </code> | ||
The setmetatable() function also returns the table that you're setting the metatable of, so these two scripts do the same thing: | |||
setmetatable() | |||
<code lua> | <code lua> | ||
local | local x = {} | ||
setmetatable(x, {}) | |||
} | |||
</code> | </code> | ||
<code lua> | <code lua> | ||
local | local x = setmetatable({}, {}) | ||
} | |||
</code> | </code> | ||
== | ===Metamethods=== | ||
== | |||
Metamethods are the functions that are stored inside a metatable. They can go from | Metamethods are the functions that are stored inside a metatable. They can go from | ||
Line 100: | Line 45: | ||
of metamethods that can be used: | of metamethods that can be used: | ||
*__index(<var>table</var>, <var>index</var>) — Fires when | *__index(<var>table</var>, <var>index</var>) — Fires when table[index] is indexed, where table[index] is nil. | ||
*__newindex(<var>table</var>, <var>index</var>, <var>value</var>) — Fires when | *__newindex(<var>table</var>, <var>index</var>, <var>value</var>) — Fires when table[index] tries to be set (table[index] = value), where table[index] is nil. | ||
*__call(<var>table</var>, <var>...</var>) — | *__call(<var>table</var>, <var>...</var>) — Fires when the table is called like a function, <var>...</var> is the arguments that were passed. | ||
*__concat(<var>table</var>, <var>value</var>) — | *__concat(<var>table</var>, <var>value</var>) — Fires when the .. concatenation operator is used on the table. | ||
*__unm(<var>table</var>) — | *__unm(<var>table</var>) — Fires when the unary – operator is used on the table. | ||
*__add(<var>table</var>, <var>value</var>) — The + addition operator. | *__add(<var>table</var>, <var>value</var>) — The + addition operator. | ||
*__sub(<var>table</var>, <var>value</var>) — The – subtraction operator. | *__sub(<var>table</var>, <var>value</var>) — The – subtraction operator. | ||
Line 111: | Line 56: | ||
*__mod(<var>table</var>, <var>value</var>) — The % modulus operator. | *__mod(<var>table</var>, <var>value</var>) — The % modulus operator. | ||
*__pow(<var>table</var>, <var>value</var>) — The ^ exponentiation operator. | *__pow(<var>table</var>, <var>value</var>) — The ^ exponentiation operator. | ||
*__tostring(<var></var>) — Fired when tostring is called on the table. | *__tostring(<var></var>) — Fired when [[Function_Dump/Core_Functions#tostring_.28e.29|tostring]] is called on the table. | ||
*__metatable — if present, locks the metatable so getmetatable will return this instead of the metatable and setmetatable will error. Non-function value. | *__metatable — if present, locks the metatable so getmetatable will return this instead of the metatable and setmetatable will error. Non-function value. | ||
*__eq(<var>table</var>, <var>value</var>) — The == equal to operator˚ | *__eq(<var>table</var>, <var>value</var>) — The == equal to operator˚ | ||
*__lt(<var>table</var>, <var>value</var>) — The < less than operator˚ | *__lt(<var>table</var>, <var>value</var>) — The < less than operator˚; '''NOTE:''' Using the >= greater than or equal to operator will invoke this metamethod and return the opposite of what this returns, as greater than or equal to is the same as not less than. | ||
*__le(<var>table</var>, <var>value</var>) — The <= operator˚ | *__le(<var>table</var>, <var>value</var>) — The <= operator˚; '''NOTE:''' Using the > greater than operator will invoke this metamethod and return the opposite of what this returns, as greater than is the same as not less than or equal to. | ||
*__mode — Used in [[Weak Tables]], declaring whether the keys and/or values of a table are weak. | *__mode — Used in [[Weak Tables]], declaring whether the keys and/or values of a table are weak. | ||
*__gc(<var>table</var>) — Fired when the table is garbage-collected. | *__gc(<var>table</var>) — Fired when the table is garbage-collected. | ||
*__len(<var>table</var>) — Fired when the # operator is used on the Object. '''NOTE:''' Only userdatas actually respect the __len() metamethod | *__len(<var>table</var>) — Fired when the # length operator is used on the Object. '''NOTE:''' Only userdatas actually respect the __len() metamethod in Lua 5.1 | ||
˚ Requires two | ˚ Requires two values with the ''same'' metatable; does not work with a table and another random value. | ||
==Using Metatables== | ==Using Metatables== | ||
Line 128: | Line 73: | ||
local table1 = {10,11,12} | local table1 = {10,11,12} | ||
local metatable = { | local metatable = { | ||
__unm = function(t) -- __unm is for the unary operator | __unm = function(t) -- __unm is for the unary - operator | ||
for key, value in pairs(t) do | for key, value in pairs(t) do | ||
t[key] = -value -- negate all of the values in this table | |||
end | end | ||
return | return t -- return the table | ||
end | end | ||
} | } | ||
Line 152: | Line 96: | ||
</code>}} | </code>}} | ||
__index was fired when x was | __index was fired when x was indexed in the table and not found. Lua then searched through the __index table for an index called x, and, finding one, returned that. | ||
Now you can easily do that with a simple function, but there's a lot more where | Now you can easily do that with a simple function, but there's a lot more where | ||
Line 197: | Line 141: | ||
local metatable = { | local metatable = { | ||
__add = function( | __add = function(t1, t2) | ||
local sum = {} | local sum = {} | ||
for key, value in pairs(table1) do | for key, value in pairs(table1) do | ||
Line 244: | Line 188: | ||
}, { | }, { | ||
__call = function(a, b) | __call = function(a, b) | ||
return table.concat(a, b | return table.concat(a, b) | ||
end | end | ||
}) | }) | ||
print(' | print('Table contains '..t(", ")) --> Table contains 10, 20, 30 | ||
</code>}} | </code>}} | ||
Line 279: | Line 223: | ||
} | } | ||
setmetatable(Table, Metatable) | |||
print(Table[1]) -- Will be slow because it's the first time using this number. | print(Table[1]) -- Will be slow because it's the first time using this number, so it has to run the math function. | ||
print(Table[2]) -- will be slow because it's the first time using this number. | print(Table[2]) -- will be slow because it's the first time using this number. | ||
print(Table[1]) -- will be fast because it's just grabbing the number from the table. | print(Table[1]) -- will be fast because it's just grabbing the number from the table. | ||
</code>}} | </code>}} | ||
==Rawset, Rawget, Rawequal== | |||
When playing with metatables, you may run into some problems. What happens if you need to use the __index metamethod to create new values in a table, but that table's metatable also has a __newindex metamethod in it? You'll want to use the Lua built-in function [[Function_Dump/Core_Functions#rawset_.28table.2C_index.2C_value.29|rawset]] to set the value without invoking any metamethods. Take the following code as an example of what happens if you '''don't''' use this functions. | |||
{{Example|<code lua> | |||
local Table = setmetatable({}, -- the table | |||
{ | |||
__index = function(self, i) | |||
self[i] = i * 10 -- just as an example | |||
return self[i] | |||
end, | |||
__newindex = function(self, i, v) | |||
--don't do anything because we don't want you to set values to the table the normal way | |||
end | |||
}) | |||
print(Table[1]) -- Causes a C-Stack overflow | |||
</code>}} | |||
Now why would that cause a stack overflow? Stack overflows happen when you try to call a function from itself too many times, but what would cause that to happen? In the __index function, we set self[i] to a value, so when it gets to the next line, self[i] should exist, so it won't call the __index metamethod, right? | |||
The problem is that __newindex doesn't let us set the value. Its presence stops values from being added to the table with the standard t[i] = v method. In order to get past this, you use the rawset function. | |||
{{Example|<code lua> | |||
local Table = setmetatable({}, -- the table | |||
{ | |||
__index = function(self, i) | |||
rawset(self, i, i * 10) | |||
return self[i] | |||
end, | |||
__newindex = function(self, i, v) | |||
--don't do anything because we don't want you to set values to the table the normal way | |||
end | |||
}) | |||
print(Table[1]) -- prints 10 | |||
</code>}} | |||
==See Also== | ==See Also== |
Revision as of 06:21, 30 January 2012
What is a Metatable?
Metatables allow tables to become more powerful than before. They are attached to data and contain values called Metamethods. Metamethods are fired when a certain action is used with the datum that it is attached to. You may think that if you have code like this:
local list = {1, 2}
print(list[3])
The code will search through the list for the third index in list, realize it's not there, and return nil. That's totally wrong. What really happens is the code will search through the list for the third index, realize it's not there, and then try to see if there's a metatable attached to the table, returning nil if there isn't one.
Manipulating Metatables
setmetatable() and getmetatable()
The two primary functions for giving and finding a table's metatable, are setmetatable() and getmetatable().
local x = {}
local metaTable = {} -- metaTables are tables, too!
setmetatable(x, metaTable) -- Give x a metatable called metaTable!
print(getmetatable(x)) --> table: [hexadecimal memory address]
The setmetatable() function also returns the table that you're setting the metatable of, so these two scripts do the same thing:
local x = {}
setmetatable(x, {})
local x = setmetatable({}, {})
Metamethods
Metamethods are the functions that are stored inside a metatable. They can go from calling a table, to adding a table, to even dividing tables as well. Here's a list of metamethods that can be used:
- __index(table, index) — Fires when table[index] is indexed, where table[index] is nil.
- __newindex(table, index, value) — Fires when table[index] tries to be set (table[index] = value), where table[index] is nil.
- __call(table, ...) — Fires when the table is called like a function, ... is the arguments that were passed.
- __concat(table, value) — Fires when the .. concatenation operator is used on the table.
- __unm(table) — Fires when the unary – operator is used on the table.
- __add(table, value) — The + addition operator.
- __sub(table, value) — The – subtraction operator.
- __mul(table, value) — The * mulitplication operator.
- __div(table, value) — The / division operator.
- __mod(table, value) — The % modulus operator.
- __pow(table, value) — The ^ exponentiation operator.
- __tostring() — Fired when tostring is called on the table.
- __metatable — if present, locks the metatable so getmetatable will return this instead of the metatable and setmetatable will error. Non-function value.
- __eq(table, value) — The == equal to operator˚
- __lt(table, value) — The < less than operator˚; NOTE: Using the >= greater than or equal to operator will invoke this metamethod and return the opposite of what this returns, as greater than or equal to is the same as not less than.
- __le(table, value) — The <= operator˚; NOTE: Using the > greater than operator will invoke this metamethod and return the opposite of what this returns, as greater than is the same as not less than or equal to.
- __mode — Used in Weak Tables, declaring whether the keys and/or values of a table are weak.
- __gc(table) — Fired when the table is garbage-collected.
- __len(table) — Fired when the # length operator is used on the Object. NOTE: Only userdatas actually respect the __len() metamethod in Lua 5.1
˚ Requires two values with the same metatable; does not work with a table and another random value.
Using Metatables
There are many ways to use metatables, for example the __unm metamethod (to make a table negative):
Here's an interesting way to declare things using __index:
__index was fired when x was indexed in the table and not found. Lua then searched through the __index table for an index called x, and, finding one, returned that.
Now you can easily do that with a simple function, but there's a lot more where
that came from. Take this for example:
local table = {10, 20, 30}
print(table(5))
Now, obviously you can't call a table. That's just crazy, but (surprise, surprise!) with metatables you can.
You can do a lot more as well, such as adding tables!
If the two tables have two different __add functions, then Lua will go to table1
first and if it doesn't have an __add function, then it'll go to the second one. That
means that you really only have to set the metatable of Table1 or Table2, but it's
nicer and more readable to set the metatable of both.
Here is one last example breaking away from using separate variables when it isn't necessary.
Use Cases
Now, I am well aware that you can do all of these as a simple function yourself, but there's a lot more than what you think it can do. Let's try a simple program that will memorize a number when a possibly laggy math problem is put into it.
For this one we will be using the __index metamethod just to make it simple:
Rawset, Rawget, Rawequal
When playing with metatables, you may run into some problems. What happens if you need to use the __index metamethod to create new values in a table, but that table's metatable also has a __newindex metamethod in it? You'll want to use the Lua built-in function rawset to set the value without invoking any metamethods. Take the following code as an example of what happens if you don't use this functions.
Now why would that cause a stack overflow? Stack overflows happen when you try to call a function from itself too many times, but what would cause that to happen? In the __index function, we set self[i] to a value, so when it gets to the next line, self[i] should exist, so it won't call the __index metamethod, right?
The problem is that __newindex doesn't let us set the value. Its presence stops values from being added to the table with the standard t[i] = v method. In order to get past this, you use the rawset function.