I'm currently facing the problem that you can't use the __gc
method for tables in Lua 5.1, as they are implemented in Lua 5.2. However, I want to release allocated native resources once the lua table gets collected. Is it possible to make a workaround which gives me the functionality of __gc
metamethod in Lua 5.2 for Lua 5.1?
Lua 5.1 workaround for __gc metamethod for tables
4k views Asked by Turakar At
2
There are 2 answers
0

__gc
support in setmetatable
for Lua 5.1
The following code overwrites setmetatable
to make it to work with __gc
metamethod, not attaching anything to the original table.
It will make __gc
work exactly as Lua 5.2+, as you will see in the following examples.
-- Support to __gc metamethod
do
local gcProxies = { }
-- The values in gcProxies are strong because they refer to their own keys.
-- So, it needs to be forced to have also weak values, since we want to remove each entry from gcProxies if its key (metatable) is not in use anymore anywhere.
setmetatable(gcProxies, { __mode = 'kv' }) -- Make keys and values weak
local _setmetatable = setmetatable
function setmetatable(table, metatable)
if metatable.__gc then
-- Create an empty userdata (the only values in Lua 5.1 that work with __gc metamethod is userdata).
-- Then, we insert it in gcProxies (weak table); so when metatable is not in use anymore, it will also remove it from gcProxies.
gcProxies[metatable] = newproxy(true)
-- __gc from metatable of gcProxies[metatable] call __gc from metatable
getmetatable(gcProxies[metatable]).__gc = function()
if type(metatable.__gc) == 'function' then
metatable.__gc(table)
end
end
end
return _setmetatable(table, metatable)
end
end
Example 1:
local o = { x = 'hi' }
setmetatable(o, { __gc = function(o) print(o.x) end })
o = nil
collectgarbage() --> hi
Example 2:
local o = { x = 'hi' }
local mt = { __gc = true }
setmetatable(o, mt)
mt.__gc = function(o) print(o.x) end
o = nil
collectgarbage() --> hi
Example 3:
local mt = { __gc = function(o) print(o[1]) end }
local list = nil
for i = 1, 3 do
list = setmetatable({ i, link = list }, mt)
end
list = nil
collectgarbage() --> 3; 2; 1
Example 4:
local A = { x = 'This is A' }
local B = { f = A }
setmetatable(B, { __gc = function(o) print(o.f.x) end })
A, B = nil
collectgarbage() --> This is A
Example 5:
do
local t = {
__gc = function()
-- Your 'atexit' code comes here
print('Finishing Lua program')
end
}
setmetatable(t, t)
_G['*ATEXIT*'] = t
end
--> Finishing Lua program
Example 6:
do
local mt = {
__gc = function(o)
-- Executes on collectgarbage
print('New cycle')
setmetatable({ }, getmetatable(o)) -- Creates new object for next cycle
end
}
-- Creates first object
setmetatable({ }, mt) --> New cycle
end
collectgarbage() --> New cycle
collectgarbage() --> New cycle
collectgarbage() --> New cycle
In lua 5.1 the only lua values that work with
__gc
metamethod isuserdata
. Naturally any hack or workaround will have to involveuserdata
in someway. Normally there is no way to just create newuserdata from the lua side but there is one "hidden" undocumented functionnewproxy
for doing just that.newproxy
takes an optional bool or userdata parameter. If you pass intrue
then you get a userdata with a new metatable attached. If you pass in anotheruserdata
then the new userdata will be assigned the same metatable as the one passed in.So now you can just hack together a function that'll make
__gc
work on tables:And a quick test to confirm the behavior:
IDEOne Demo
Note that lua 5.2+ and later no longer have
newproxy
since__gc
is officially supported on tables.