Easy Tutorial
❮ Lua Decision Making Lua While Loop ❯

Lua Metatables

In Lua, we can access the corresponding key to get the value in a table, but we cannot perform operations on two tables (such as addition).

Therefore, Lua provides metatables, which allow us to alter the behavior of tables, with each behavior associated with a corresponding metamethod.

For example, using metatables, we can define how Lua computes the addition of two tables, a + b.

When Lua attempts to add two tables, it first checks if either of them has a metatable and then checks if there is a field called __add. If found, it calls the corresponding value. Fields like __add are immediate fields, and their corresponding values (often functions or tables) are "metamethods".

There are two important functions for handling metatables:

The following example demonstrates how to set a metatable for a specified table:

mytable = {}                          -- Ordinary table
mymetatable = {}                      -- Metatable
setmetatable(mytable, mymetatable)    -- Set mymetatable as the metatable of mytable

The above code can also be written in one line:

mytable = setmetatable({}, {})

The following returns the object's metatable:

getmetatable(mytable)                 -- This will return mymetatable

__index Metamethod

This is the most commonly used key in metatables.

When you access a table by key, if this key has no value, Lua will look for the __index key in the table's metatable (assuming there is a metatable). If __index contains a table, Lua will look up the corresponding key in that table.

We can see this in action using the Lua interactive mode:

$ lua
Lua 5.3.0  Copyright (C) 1994-2015 Lua.org, PUC-Rio
> other = { foo = 3 }
> t = setmetatable({}, { __index = other })
> t.foo
3
> t.bar
nil

If __index contains a function, Lua will call that function, with the table and key as arguments.

The __index metamethod checks if an element exists in the table. If it does not exist, it returns nil; if it exists, it returns the result from __index.

Example

mytable = setmetatable({key1 = "value1"}, {
  __index = function(mytable, key)
    if key == "key2" then
      return "metatablevalue"
    else
      return nil
    end
  end
})

print(mytable.key1, mytable.key2)

The output of this example is:

value1    metatablevalue

Example Analysis:

We can simplify the above code to:

mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } })
print(mytable.key1, mytable.key2)

Summary

The rules Lua uses to look up a table element are as follows:

  1. Look in the table. If found, return the element; if not, continue.
  2. Check if the table has a metatable. If no metatable, return nil; if there is a metatable, continue.
  3. Check if the metatable has an __index method. If __index is nil, return nil; if __index is a table, repeat steps 1, 2, 3; if __index is a function, return the function's return value.

This content is from the author Huanzi: https://blog.csdn.net/xocoder/article/details/9028347


__newindex Metamethod

The __newindex metamethod is used for updating tables, while __index is used for accessing tables.

When you assign a value to a missing index in a table, the interpreter looks for the __newindex metamethod: if found, it calls this function instead of performing the assignment.

The following example demonstrates the use of the __newindex metamethod:

Example

mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })

print(mytable.key1)

mytable.newkey = "New Value 2"
print(mytable.newkey, mymetatable.newkey)

mytable.key1 = "New Value 1"
print(mytable.key1, mymetatable.key1)

The output of this example is:

value1
nil    New Value 2
New Value 1    nil

In this example, the table has the __newindex metamethod set. When assigning to a new index key (mytable.newkey = "New Value 2"), it calls the metamethod and does not perform the assignment. However, if assigning to an existing index key (key1), it performs the assignment without calling the __newindex metamethod.

The following example uses the rawset function to update the table:

Example

mytable = setmetatable({key1 = "value1"}, {
    __newindex = function(mytable, key, value)
        rawset(mytable, key, "\"" .. value .. "\"")
    end
})

mytable.key1 = "new value"
mytable.key2 = 4

print(mytable.key1, mytable.key2)

The output of this example is:

new value    "4"

Adding Operators to Tables

The following example demonstrates adding two tables:

Example

-- Function to calculate the maximum value in a table, as table.maxn is deprecated in Lua 5.2+
function table_maxn(t)
    local mn = 0
    for k, v in pairs(t) do
        if mn < k then
            mn = k
        end
    end
    return mn
end

-- Adding two tables
mytable = setmetatable({ 1, 2, 3 }, {
  __add = function(mytable, newtable)
    for i = 1, table_maxn(newtable) do
      table.insert(mytable, table_maxn(mytable) + 1, newtable[i])
    end
    return mytable
  end
})

secondtable = {4, 5, 6}

mytable = mytable + secondtable
for k, v in ipairs(mytable) do
  print(k, v)
end

The output of this example is:

1    1
2    2
3    3
4    4
5    5
6    6

The __add key is included in the metatable for addition operations. The corresponding operations for tables are as follows (Note: __ is two underscores):

Mode Description
__add Corresponding operator '+'.
__sub Corresponding operator '-'.
__mul Corresponding operator '*'.
__div Corresponding operator '/'.
__mod Corresponding operator '%'.
__unm Corresponding operator '-'.
__concat Corresponding operator '..'.
__eq Corresponding operator '=='.
__lt Corresponding operator '<'.
__le Corresponding operator '<='.

__call Metamethod

The __call metamethod is invoked when Lua calls a value. The following example demonstrates calculating the sum of elements in a table:

Example

-- Function to calculate the maximum value in a table, as table.maxn is deprecated in Lua 5.2+
function table_maxn(t)
    local mn = 0
    for k, v in pairs(t) do
        if mn < k then
            mn = k
        end
    end
    return mn
end

-- Define the __call metamethod
mytable = setmetatable({10}, {
  __call = function(mytable, newtable)
        sum = 0
        for i = 1, table_maxn(mytable) do
                sum = sum + mytable[i]
        end
    for i = 1, table_maxn(newtable) do
                sum = sum + newtable[i]
    end
        return sum
  end
})
newtable = {10, 20, 30}
print(mytable(newtable))

The output of this example is:

70

__tostring Metamethod

The __tostring metamethod is used to modify the output behavior of a table. The following example customizes the output of a table:

Example

mytable = setmetatable({ 10, 20, 30 }, {
  __tostring = function(mytable)
    sum = 0
    for k, v in pairs(mytable) do
                sum = sum + v
        end
    return "Sum of table elements is " .. sum
  end
})
print(mytable)

The output of this example is:

Sum of table elements is 60

Understanding metatables can greatly simplify our code and allow us to write more elegant Lua code.

❮ Lua Decision Making Lua While Loop ❯