This first edition was written for Lua 5.0. While still largely relevant for later versions, there are some differences.
The fourth edition targets Lua 5.3 and is available at Amazon and other bookstores.
By buying the book, you also help to support the Lua project.
![]() |
Programming in Lua | ![]() |
| Part II. Tables and Objects Chapter 13. Metatables and Metamethods |
Both __index and __newindex are relevant only when
the index does not exist in the table.
The only way to catch all accesses to a table
is to keep it empty.
So, if we want to monitor all accesses to a table,
we should create a proxy for the real table.
This proxy is an empty table, with proper
__index and __newindex metamethods,
which track all accesses and redirect them to the original table.
Suppose that t is the original table we want to track.
We can write something like this:
t = {} -- original table (created somewhere)
-- keep a private access to original table
local _t = t
-- create proxy
t = {}
-- create metatable
local mt = {
__index = function (t,k)
print("*access to element " .. tostring(k))
return _t[k] -- access the original table
end,
__newindex = function (t,k,v)
print("*update of element " .. tostring(k) ..
" to " .. tostring(v))
_t[k] = v -- update original table
end
}
setmetatable(t, mt)
This code tracks every access to t:
> t[2] = 'hello'
*update of element 2 to hello
> print(t[2])
*access to element 2
hello
(Notice that, unfortunately, this scheme does not allow us
to traverse tables.
The pairs function will operate on the proxy,
not on the original table.)
If we want to monitor several tables, we do not need a different metatable for each one. Instead, we can somehow associate each proxy to its original table and share a common metatable for all proxies. A simple way to associate proxies to tables is to keep the original table in a proxy's field, as long as we can be sure that this field will not be used for other means. A simple way to ensure that is to create a private key that nobody else can access. Putting these ideas together results in the following code:
-- create private index
local index = {}
-- create metatable
local mt = {
__index = function (t,k)
print("*access to element " .. tostring(k))
return t[index][k] -- access the original table
end,
__newindex = function (t,k,v)
print("*update of element " .. tostring(k) ..
" to " .. tostring(v))
t[index][k] = v -- update original table
end
}
function track (t)
local proxy = {}
proxy[index] = t
setmetatable(proxy, mt)
return proxy
end
Now, whenever we want to monitor a table t,
all we have to do is t = track(t).
| Copyright © 2003–2004 Roberto Ierusalimschy. All rights reserved. | ![]() |