Objective
Wenzu the corresponding English is metatable, the meta method is Metamethod. As we all know, in C + +, two classes cannot be added directly, but if you overload the "+" symbol, you can do the addition of the class. There is also this truth in Lua, where you cannot directly "+" a variable of two table types, and if you define a specified function, you can do so. So this post is mainly about how to define this specified function, what is this specified function? Hope to be helpful to a friend who is learning Lua.
How does Lua do that?
In general, each value in Lua has a set of predefined operations, such as numbers that can be added, strings that can be concatenated, but for two table types, no direct "+" operation. This requires some action. In Lua there is a meta table, which is the metatable above, in which we can modify a worthwhile behavior by using a meta table to perform a specified action when confronted with a predefined operation. For example, now there are two table-type variables A and B, and we can define how the expression a+b is computed by metatable, specifically in LUA by following these steps:
First, determine whether A and B have a meta table;
Check if there is a field called __add in the meta table;
If the field is found, the corresponding value of the field is called, which corresponds to a metamethod (in Lua, the method can be placed in a field, remember??? Forget the point here)
Call the __add corresponding metamethod to compute the values of A and B.
The four steps above are the process of calculating the a+b of a table type variable. In Lua, each value has a meta table, each variable of table and UserData type can have its own independent meta table, while other types of values share the singleton table to which the type belongs.
Farewell metatable Xiao Bai
Now let's talk about the most basic metatable content. LUA does not create a meta table when creating a new table, such as the following code can demonstrate:
Copy Code code as follows:
Local T = {1, 2}
Print (getmetatable (t))--Nil
We use getmetatable to get the meta table of a table or UserData type variable, and when we create a new table variable, we use the getmetatable to get the meta table and return the nil; We can also use setmetatable to set the meta table for a table or UserData type variable, such as the following code:
Copy Code code as follows:
Local T = {}
Print (getmetatable (t))-->nil
Local T1 = {}
Setmetatable (t, T1)
ASSERT (getmetatable (t) = = T1)
Any table can serve as any worthwhile meta table, and a set of related tables can share a common meta table that describes their common behavior. A table can even be used as its own meta table to describe its unique behavior. In short, any form of collocation is legal.
In the LUA code, you can only set the meta table for the table. To set other types of worthwhile meta tables, you must do so through C code. There is also a special case where, for strings, the standard string library sets a meta table for all strings, while other types do not have a meta table by default. If you look at the printed values of the two lines of code, you can see that:
Copy Code code as follows:
Print (Getmetatable ("Hello World")
Print (Getmetatable (10))
In the table, I can redefine the Meta method with the following several:
Copy Code code as follows:
__add (A, B)--Addition
__sub (A, b)--subtraction
__mul (A, b)--multiplication
__div (A, b)--Division
__mod (A, b)--modulo
__pow (A, b)--Power
__UNM (a)--opposite number
__concat (A, B)--Connection
__len (a)--length
__eq (A, b)--Equal
__lt (A, b)--less than
__le (A, b)--less than or equal to
__index (A, b)--index query
__newindex (A, B, c)--Index Update (PS: If you don't understand, there will be talk later)
__call (A, ...)--executing method calls
__tostring (a)--string output
__metatable--Protection meta table
The next step is to introduce if you want to redefine these methods.
The meta method of arithmetic class
Now I use the complete instance code to illustrate the use of the arithmetic class meta method in detail. I'm going to define some action methods on the set, all of the methods are put into the set table, and why the table can hold functions, refer to the "functions in Lua" article. The following code is the operation of a collection that I simulate:
Copy Code code as follows:
Set = {}
Local MT = {}--Wenzu of the collection
--Creates a new collection from the values in the argument list
function Set.new (l)
Local set = {}
Setmetatable (set, MT)
For _, V. in pairs (L) does set[v] = True End
return set
End
--and set operation
function Set.union (A, B)
Local retset = set.new{}--This is equivalent to Set.new ({})
For V. in pairs (a) does retset[v] = True End
For V. in pairs (b) does retset[v] = True End
Return Retset
End
--Intersection operation
function Set.intersection (A, B)
Local retset = set.new{}
For V. in pairs (a) does retset[v] = B[v] End
Return Retset
End
--operation of the print collection
function set.tostring (Set)
Local TB = {}
For e into pairs (set) do
tb[#tb + 1] = E
End
Return "{" ... Table.concat (TB, ",") ... "}"
End
function Set.print (s)
Print (set.tostring (s))
End
Now that I define "+" to compute the set of two sets, we need to share a meta table with all tables used to represent the collection, and define how to perform an addition operation in the meta table. You first create a regular table, prepare the meta table for the collection, and then modify the Set.new function to set a meta table for the new collection each time you create the collection. The code is as follows:
Copy Code code as follows:
Set = {}
Local MT = {}--Wenzu of the collection
--Creates a new collection from the values in the argument list
function Set.new (l)
Local set = {}
Setmetatable (set, MT)
For _, V. in pairs (L) does set[v] = True End
return set
End
After that, all the collections created by Set.new have an identical meta table, for example:
Copy Code code as follows:
Local Set1 = Set.new ({10, 20, 30})
Local Set2 = Set.new ({1, 2})
Print (Getmetatable (SET1))
Print (Getmetatable (Set2))
ASSERT (getmetatable (set1) = = Getmetatable (Set2))
Finally, we need to add the meta method to the meta table with the following code:
Copy Code code as follows:
After that, as soon as we use the "+" symbol to find the set of two sets, it automatically calls the Set.union function, and the two operands are passed as arguments. For example, the following code:
Copy Code code as follows:
Local Set1 = Set.new ({10, 20, 30})
Local Set2 = Set.new ({1, 2})
Local Set3 = Set1 + Set2
Set.print (SET3)
The meta methods that are listed above can be redefined using the above method. Now there is a new problem, Set1 and Set2 have a meta table, then we have to use whose Wenzu ah? While the sample code here uses a meta table, but the actual coding encounters the problem I'm talking about here, Lua solves the problem by following these steps:
1. For the two-dollar operator, if the first operand has a meta table, and the required field definitions are available in the meta table, such as the __add element method definition here, then Lua takes the word Kewei method, regardless of the second value;
2. For the two-dollar operator, if the first operand has a meta table, but there is no required field definition in the Meta table, such as the __add element method definition here, then Lua looks for the second operand's meta table;
3. If the two operands have no meta tables or have no corresponding meta method definitions, Lua throws an error.
That's the rule that Lua handles this problem, so what do we do in real-world programming? such as Set3 = Set1 + 8 Such code, will print out the following error message:
Copy Code code as follows:
Lua:test.lua:16:bad argument #1 to ' pairs ' (table expected, got number)
However, in the actual encoding, we can use the following methods to pop our defined error message, the code is as follows:
Copy Code code as follows:
function Set.union (A, B)
If Getmetatable (a) ~= MT or getmetatable (b) ~= Mt Then
Error ("MetaTable error.")
End
Local retset = set.new{}--This is equivalent to Set.new ({})
For V. in pairs (a) does retset[v] = True End
For V. in pairs (b) does retset[v] = True End
Return Retset
End
When the meta table of two operands is not the same meta table, it means that there is a problem when the two are set up and then we can print out the error message we need.
It summarizes the definition of the meta method of the arithmetic class, and the definition of the meta method of the relational class and the element method of the arithmetic class is similar.
__tostring element method
Anyone who has written Java or C # knows that there is a method of ToString in the object class that programmers can rewrite to achieve their own needs. This is also true in Lua, when we direct print (a) (A is a table), it is not possible. So, at this point, we need to redefine the __tostring meta method ourselves so that print can format the table type data.
The function print always calls tostring to format the output, and when any value is formatted, ToString checks whether the value has a __tostring method, and if so, ToString calls the Meta method with that value as a parameter, The rest of the actual formatting operation is done by a function referenced by the __tostring method, which eventually returns a formatted string. For example, the following code:
Copy Code code as follows:
mt.__tostring = set.tostring
How to protect our "cheese"--meta table
We will find that the use of getmetatable can easily get the meta table, using setmetatable can easily modify the meta table, then the risk is too large, then how to protect our meta table is not tampered with it?
In Lua, functions setmetatable and Getmetatable functions use a field in the meta table to protect the meta table, which is __metatable. When we want to protect the metadata table of the collection, it is the user can neither see nor modify the collection of the meta table, then need to use the __metatable field, and when the field is set, Getmetatable will return the value of the field, and Setmetatable will raise an error , such as the following demo code:
Copy Code code as follows:
function Set.new (l)
Local set = {}
Setmetatable (set, MT)
For _, V. in pairs (L) does set[v] = True End
Mt.__metatable = "You cannot get the metatable"--after setting up my Wenzu, don't let anyone else set up
return set
End
Local TB = Set.new ({1, 2})
Print (TB)
Print (Getmetatable (TB))
Setmetatable (TB, {})
The above code prints the following:
Copy Code code as follows:
{1, 2}
You cannot get the metatable
Lua:test.lua:56:cannot Change a protected metatable
__index element method
Do you remember what value we return when we access a field that does not exist in a table? By default, when we access a field that does not exist in a table, the result is nil. But this situation can easily be changed, and Lua decides whether to return to nil or something else that is worth the following steps:
1. When accessing a table's field, if the table has this field, return the corresponding value directly;
2. When the table does not have this field, it prompts the interpreter to look for a meta method called __index, and then calls the corresponding meta method, returning the value returned by the Meta method;
3. If you do not have this meta method, then return the nil result.
The following is a practical example to illustrate the use of __index. Suppose you want to create some description window, each table must describe some of the window parameters, such as color, position and size, and so on, these parameters are the default value, so we can create a window object that is different from the default worth of parameters.
Copy Code code as follows:
Windows = {}--Create a namespace
--Create a default value table
Windows.default = {x = 0, y = 0, Width = n, height = y, color = {r = 255, G = 255, b = 255}}
WINDOWS.MT = {}--create meta table
--Declaring constructors
function Windows.new (o)
Setmetatable (o, windows.mt)
Return o
End
--Defining the __index element method
Windows.mt.__index = function (table, key)
return Windows.default[key]
End
Local win = Windows.new ({x = ten, y = 10})
Print (win.x)-->10 access to values that they already own
Print (win.width)-->100 access to values in the default table
Print (WIN.COLOR.R)-->255 access to values in the default table
Based on the output of the above code, combined with the three steps above, let's take a look at print (win.x), because the win variable itself has the X field, so it directly prints the value of its own field; print (Win.width), Because the win variable itself does not have the Width field, then go to find whether owns the meta table, whether there are __index corresponding meta method, because of the existence of __index method, returned the value of the Width field in the default table, print (WIN.COLOR.R) is the same.
In practical programming, the __index element method does not have to be a function, it can also be a table. When it is a function, Lua calls the function with a table and a nonexistent key as a parameter, as in the above code; when it is a table, LUA accesses the table in the same way, so the above code can be:
Copy Code code as follows:
--Defining the __index element method
Windows.mt.__index = Windows.default
__newindex element method
The __newindex method is similar to __index, __newindex is used to update the data in the table, and __index is used to query the data in the table. When you assign a value to an index that does not exist in a table, the following steps are followed in Lua:
The 1.LUA interpreter first determines whether the table has a meta table;
2. If you have a meta table, look for whether there is a __newindex element in the meta table, if there is no meta table, add the index directly, then the corresponding assignment;
3. If there is this __newindex element method, the LUA interpreter executes it rather than executing the assignment;
4. If the __newindex corresponds not to a function, but to a table, the LUA interpreter executes the assignment in this table, not the original table.
So here's a question, look at the following code:
Copy Code code as follows:
Local tb1 = {}
Local TB2 = {}
Tb1.__newindex = TB2
Tb2.__newindex = tb1
Setmetatable (TB1, TB2)
Setmetatable (TB2, TB1)
tb1.x = 10
Did you find anything wrong? is not a loop, in the LUA interpreter, the error message pops up on this issue, and the error message is as follows:
Copy Code code as follows:
Get rid of that fucking Wenzu.
Sometimes, we do not want to query the value from the __index corresponding meta method, we do not want to update the table, do not want to perform __newindex corresponding method, or __newindex corresponding table. Then what? In Lua, when we query the values in the table, or update the values in the table, we do not want to ignore the damn meta table, we can use the Rawget function, call Rawget (TB, i) is a "raw (raw)" Access to table TB, That is, a simple access that does not take into account the meta table; you might think that once the original access, there is no access to the __index corresponding meta method, there may be performance improvement, in fact, an original access does not accelerate the speed of code execution. For the __newindex method, you can call the Rawset (T, K, v) function, which can directly set the value v associated with key k in table T without involving any of the meta methods.
Summarize
This post is a concrete summary of the Wenzu and Meta methods in Lua, and it can be said that the Wenzu and meta methods in LUA are the basis for a lot of content, so I'm summarizing it here in detail and combining a lot of code. If you have the honor to see this article, I hope you will also take some time to read it carefully, want to understand Lua, play the LUA, of course, can not just be some grammar, master meta table and meta method is essential. Finally, I hope this article will be useful to everyone. Next blog post, I will combine __index and __newindex to say some example code.