Metatables are an advanced feature of Lua that can be used to define custom operations for variables. Each variable has a set of metatable events that are called in certain situations, usually when a particular operator is used on the variable. For example, when the length operator is used on a variable var
(e.g., #var
), the metatable event __len
is called. A variable can have a metatable applied to it, which is a table where the keys are the metatable events and the values are metamethods that will be executed when the corresponding metatable event is called. Metamethods are usually functions, although in some cases tables or other types of values are used. Applying metamethods to metatable events is similar to how functions can be bound to hooks. For example, if you want to control what happens when #var
is called, you can write a metamethod, create a metatable for var
and assign your metamethod to the metatable's __len
key. Then, whenever #var
is called, your metamethod is executed. For more information on how metatables work, consult the Metamethods Tutorial on the lua-users wiki.
Usage
Metatables are created like normal tables, with the keys being the names of the metatable events that you want to use:
local metatable = {__event = value}
local metatable = {__event1 = value1, __event2 = value2}
Individual events can be modified afterwards:
metatable.__event = newvalue
You can also assign the metamethods to other variables outside of the metatable:
local var = metatable.__event
If the metamethod of an event is a function, it may be called as such:
metatable.__event(arguments)
setmetatable
is used to apply a metatable to a table:
local myTable = {}
local metatable = {__event = value}
setmetatable(myTable, metatable)
Alternatively the metatable can be applied implicitly:
local myTable = {}
setmetatable(myTable, {__event = value})
Because setmetatable
returns the table that was supplied to it (in this example, {}
is used as a dummy table), the metatable can also be created and applied in the same line:
local myTable = setmetatable({}, {__event = value})
getmetatable
can be used to retrieve the metatable for a table or other variable:
local metatable = getmetatable(myTable)
getmetatable
can retrieve the metatable for any type of variable, not just tables. It will return nil
if no metatable is set. Note that getmetatable
returns a direct reference to the metatable rather than just a copy – if this reference is modified, it will directly affect the metatable for the variable itself!
By contrast, setmetatable
can only be used on tables. This means that it is not possible to create new metatables for other types of variables, including userdata. However, strings and userdata already have metatables applied to them in SRB2, which you can retrieve and modify with getmetatable
.
If a variable's metatable has the metatable event __metatable
set, the metatable cannot be retrieved or modified – instead, getmetatable
will return the value of __metatable
, while setmetatable
will print an error in the console.
List of events
For any examples below, a
represents the variable the metatable events are applied to. Binary operations such as addition or subtraction use a second variable, which will be represented by b
. In the "Type of value" column, functions are written in the format returntype function(args)
, where returntype
of the type of value returned by the function and args
is the list of arguments taken by the function.
Name
|
Event
|
Type of value
|
Further details
|
Table indexes
|
__index
|
Accessing a table at a key that does not exist (a.key or a["key"] , value is nil )
|
any function(a, key) or table
|
__index is highly useful for allowing a table to fall back on another table if a key doesn't exist in the first. This can be use to simulate object inheritance and object-oriented programming in Lua (see this page for more info): If a table a falls back on a table b for keys that don't exist in a , it will appear as if a inherits those keys from b . The value for a key key that is inherited from b can be overridden in a by assigning a new value to a.key , which will prevent the fallback from occurring.
- As a reminder,
a.key is the same as a["key"] , i.e., key from a.key is a variable of type string in this situation.
- If a function is used as the value for
__index , that function will be executed and its return value will be returned as the value of the a.key call.
- If a table
table is used as the value for __index , it will be used as the fallback table. If a.key does not exist, table.key will be used instead. If table.key does not exist either and table has its own __index metamethod, this metamethod will be called. This can be used to create a chain of tables falling back on each other.
|
__newindex
|
Assigning a value to a new key in a table (a.key = value or a["key"] = value , old value is nil )
|
void function(a, key, value) or table
|
- When simulating inheritance, as described for
__index above, __newindex can be used to ensure that if the key whose value is changed is inherited from a fallback table, the value is changed in that fallback table.
- If a function is used as the value for
__newindex , that function will be executed with value as a parameter instead of assigning value to a.key .
- If a table
table is used as the value for __newindex , it will be used as the fallback table. If a.key does not exist, the value will be assigned to table.key instead. However, if table.key already exists and table has its own __usedindex metamethod, that metamethod will be called instead. If table.key does not exist and table has its own __newindex metamethod, this metamethod will be called.
|
__usedindex
|
Assigning a value to an existing key in a table (a.key = value or a["key"] = value , old value is not nil )
|
void function(a, key, value) or table
|
This metamethod is only usable for tables.
- If a function is used as the value for
__usedindex , that function will be executed with value as a parameter instead of assigning value to a.key .
- If a table
table is used as the value for __usedindex , it will be used as the fallback table. Instead of assigning the value to a.key , it will be assigned to table.key instead. However, if table.key already exists and table has its own __usedindex metamethod, that metamethod will be called instead. If table.key does not exist and table has its own __newindex metamethod, this metamethod will be called.
|
Arithmetic
|
__add
|
Addition (a + b )
|
any function(a, b)
|
- This operation can be performed even when the other value does not share the same metamethod or does not have one.
- If
a does not have a metamethod, b 's metamethod is used instead.
a is always the first argument in the function, and b the second, even if b 's metamethod is the one called.
|
__sub
|
Subtraction (a - b )
|
any function(a, b)
|
- This operation can be performed even when the other value does not share the same metamethod or does not have one.
- If
a does not have a metamethod, b 's metamethod is used instead.
a is always the first argument in the function, and b the second, even if b 's metamethod is the one called.
|
__mul
|
Multiplication (a * b )
|
any function(a, b)
|
- This operation can be performed even when the other value does not share the same metamethod or does not have one.
- If
a does not have a metamethod, b 's metamethod is used instead.
a is always the first argument in the function, and b the second, even if b 's metamethod is the one called.
|
__div
|
Division (a / b )
|
any function(a, b)
|
- This operation can be performed even when the other value does not share the same metamethod or does not have one.
- If
a does not have a metamethod, b 's metamethod is used instead.
a is always the first argument in the function, and b the second, even if b 's metamethod is the one called.
|
__mod
|
Modulus (a % b )
|
any function(a, b)
|
- This operation can be performed even when the other value does not share the same metamethod or does not have one.
- If
a does not have a metamethod, b 's metamethod is used instead.
a is always the first argument in the function, and b the second, even if b 's metamethod is the one called.
|
__pow
|
Exponentation (a ^ b )
|
any function(a, b)
|
- This operation can be performed even when the other value does not share the same metamethod or does not have one.
- If
a does not have a metamethod, b 's metamethod is used instead.
a is always the first argument in the function, and b the second, even if b 's metamethod is the one called.
|
__unm
|
Unary negation ( -a )
|
any function(a)
|
|
__len
|
Length ( #a )
|
any function(a)
|
|
Comparison
|
__eq
|
Equals check (a == b )
|
any function(a, b)
|
a and b must share the same metamethod for __eq for the function to be run.
|
__lt
|
Less-than (a < b ) or Greater-than (b > a )
|
any function(a, b)
|
- Greater-than comparisons are done by flipping the order of
a and b so it is treated as a less-than operation.
a and b must share the same metamethod for __lt for the function to be run.
__lt may also be run for less-than operations if a __le metamethod does not exist for both a and b (or they do not share the same __le metamethod). In this case, a <= b is checked as b > a , and b >= a as a < b .
|
__le
|
Less-than-or-equals (a <= b ) or Greater-than-or-equals (b >= a )
|
any function(a, b)
|
- Greater-than-or-equals comparisons are done by flipping the order of
a and b so it is treated as a less-than-or-equals operation.
a and b must share the same metamethod for __le for the function to be run.
|
Bitwise operations
|
__and
|
Bitwise AND (a & b )
|
any function(a, b)
|
- This operation can be performed even when the other value does not share the same metamethod or does not have one.
- If
a does not have a metamethod, b 's metamethod is used instead.
a is always the first argument in the function, and b the second, even if b 's metamethod is the one called.
|
__or
|
Bitwise OR (a | b )
|
any function(a, b)
|
- This operation can be performed even when the other value does not share the same metamethod or does not have one.
- If
a does not have a metamethod, b 's metamethod is used instead.
a is always the first argument in the function, and b the second, even if b 's metamethod is the one called.
|
__xor
|
Bitwise XOR (a ^^ b )
|
any function(a, b)
|
- This operation can be performed even when the other value does not share the same metamethod or does not have one.
- If
a does not have a metamethod, b 's metamethod is used instead.
a is always the first argument in the function, and b the second, even if b 's metamethod is the one called.
|
__shl
|
Left shift (a << b )
|
any function(a, b)
|
- This operation can be performed even when the other value does not share the same metamethod or does not have one.
- If
a does not have a metamethod, b 's metamethod is used instead.
a is always the first argument in the function, and b the second, even if b 's metamethod is the one called.
|
__shr
|
Right shift (a >> b )
|
any function(a, b)
|
- This operation can be performed even when the other value does not share the same metamethod or does not have one.
- If
a does not have a metamethod, b 's metamethod is used instead.
a is always the first argument in the function, and b the second, even if b 's metamethod is the one called.
|
__not
|
Bitwise NOT ( ~a )
|
any function(a)
|
|
Miscellaneous
|
__concat
|
Concatenation (a..b )
|
any function(a, b)
|
- This operation can be performed even when the other value does not share the same metamethod or does not have one.
- If
a does not have a metamethod, b 's metamethod is used instead.
a is always the first argument in the function, and b the second, even if b 's metamethod is the one called.
|
__tostring
|
Conversion to string using tostring
|
string function(a)
|
Lua will pass a itself to the function set for __tostring . The function should return a string as the result of the conversion.
|
__call
|
Calling as a function (a([args]) )
|
any function(a, [args])
|
This is used to treat a like a function, even if it is not one, when followed by parantheses as shown. a and any arguments passed to it will be passed along to the function set for __call , and the return value of this function will be the return value of the a([args]) call.
|
__gc
|
Garbage collection for userdata
|
void function(a)
|
This metamethod is only usable for userdata. This metamethod is called before the garbage collector collects the userdata, making it possible to define additional garbage collection behavior.
|
__mode
|
Weak references handling
|
string
|
This metamethod is only usable for tables. This controls whether the keys or values of the table are weak references, depending on whether certain letters are present in the string:
- "k" – Keys are marked as weak
- "v" – Values are marked as weak.
|
__metatable
|
Metatable protection
|
any
|
This prevents the metatable for a from being accessed and modified:
getmetatable will return the value set for __metatable instead of the metatable itself.
setmetatable will print an error in the console instead of setting a new metatable.
|
List of existing metatables
|
This article or section is outdated and has not been fully updated to reflect the current version of SRB2.
Please help the Wiki by correcting or removing any misinformation, as well as adding any new information to the page.
|
SRB2's userdata types and global tables are in fact simply empty tables whose entire functionality comes through the implementation of their metatable events, mainly __index
for accessing elements and __newindex
for modifying them. The metatables and events applied to them are listed here.
Name/Type of variable
|
Metamethods set
|
Userdata
|
(mobj_t )
|
__index → mobj.var
__newindex → mobj.var = value
|
(player_t )
|
__index → player.var
__newindex → player.var = value
__len → #player
|
player.powers (player is player_t )
|
__index → player.powers[pw_powername]
__newindex → player.powers[pw_powername] = value
__len → #player.powers
|
(ticcmd_t )
|
__index → ticcmd.var
__newindex → ticcmd.var = value
|
(skin_t )
|
__index → skin.var
__newindex → warning message to state skin_t cannot be modified
__len → #skin
|
skin.soundsid (skin is skin_t )
|
__index → skin.soundsid[SKSNAME]
__len → #skin.soundsid
|
(mobjinfo_t )
|
__index → info.var
__newindex → info.var = value
__len → #info
|
(state_t )
|
__index → info.var
__newindex → info.var = value
__len → #info
|
(sfxinfo_t )
|
__index → info.var
__newindex → info.var = value
__len → #info
|
(hudinfo_t )
|
__index → info.var
__newindex → info.var = value
__len → #info
|
(mapheader_t )
|
__index → mapheader.var
__len → #mapheader
|
(mapthing_t )
|
__index → mthing.var
__newindex → mthing.var = value
|
(sector_t )
|
__index → sector.var
__newindex → sector.var = value
__len → #sector
|
sector.lines (sector is sector_t )
|
__index → sector.lines[i]
__len → #sector.lines
|
(subsector_t )
|
__index → subsector.var
__len → #subsector
|
(line_t )
|
__index → line.var
__len → #line
|
line.sidenum (line is line_t )
|
__index → line.sidenum[i]
|
(side_t )
|
__index → side.var
__newindex → side.var = value
__len → #side
|
(vertex_t )
|
__index → vertex.var
__len → #vertex
|
(ffloor_t )
|
__index → ffloor.var
__newindex → ffloor.var = value
|
(camera_t )
|
|
(consvar_t )
|
|
(patch_t )
|
__index → patch.var
__newindex → warning message to state patch_t cannot be modified
|
(colormap )
|
__index → warning message to state colormap is not a struct
|
Tables
|
mobjinfo
|
__index → mobjinfo[MT_NAME]
__newindex → mobjinfo[MT_NAME] = { }
__len → #mobjinfo
|
states
|
__index → states[S_NAME]
__newindex → states[S_NAME] = { }
__len → #states
|
S_sfx or sfxinfo
|
__index → sfxinfo[sfx_name]
__newindex → sfxinfo[sfx_name] = { }
__len → #sfxinfo
|
sprnames
|
__index → sprnames[SPR_NAME] or sprnames["NAME"]
__len → #sprnames
|
players
|
__index → players[i]
__len → #players
|
skins
|
__index → skins[i] or skins["name"]
__len → #skins
|
mapthings
|
__index → mapthings[i]
__len → #mapthings
|
vertexes
|
__index → vertexes[i]
__len → #vertexes
|
lines
|
__index → lines[i]
__len → #lines
|
sides
|
__index → sides[i]
__len → #sides
|
subsectors
|
__index → subsectors[i]
__len → #subsectors
|
sectors
|
__index → sectors[i]
__len → #sectors
|
mapheaderinfo
|
__index → mapheaderinfo[i]
__len → #mapheaderinfo
|
hudinfo
|
__index → hudinfo[HUD_NAME]
__len → #hudinfo
|
Miscellaneous
|
(string)
|
__add → Concatenation using "+" (string1"+variable+"string2 )
__index → String library functions, called as stringvar.function([args])
|