Heh, I actually just wrote this today for something else that I’m going to be posting about sometime tonight, but I think it might be relevant to the discussion. A way that I’ve dealt with this is to create a function that returns a bound member function call. For example:
function bind(obj, methodname)
return function(...) return obj[methodname](obj, ...) end
end
Now, whenever you need something to bind an object with it’s method for use in a callback, you can use:
-- add callback to example touch event manager
touchManager:addCallback(bind(myObj, 'touched'))
-- later on, touchManager calls our bound callback with a touch object as an argument:
callbacks[i](touch)
-- which will functionally equate to:
-- myObj:touched(touch)
I’ve even actually taken it a step further: because passing the same inputs to bind() will always generate functionally identical outputs, I memoize the bound method. The main reason that I did this was because I wanted a simple way to be able to compare bound methods for later removal from callback lists. For example:
local bound1 = bind(someObj, 'someFunc')
local bound2 = bind(someObj, 'someFunc')
print(bound1 == bound2)
Will print ‘false’, because even though the functions generated by bind() in this case are functionally identical, they are not equal because they are completely separate objects. With memoized bound methods, we only need to create the bound method the first time it’s needed; subsequent times, we’ll just return the one we created earlier. Now:
-- caller() is bind() but with memoization
local bound1 = caller(someObj, 'someFunc')
local bound2 = caller(someObj, 'someFunc')
print(bound1 == bound2)
Will print ‘true’. This is especially useful for event systems, where (especially in Lua) you might find yourself needing to keep a separate reference to your bound callback so that you can de-register it later. Using caller(), you don’t have to, since it memoizes the bound method. Now you can register and deregister the same way, with no additional reference tracking:
touchManager:addCallback(caller(myObj, 'touched'))
touchManager:removeCallback(caller(myObj, 'touched'))
I’ve also added another whistle to the caller() function, which is that the tables used for memoization keep weak references, so that once there are no more references to the object and bound method, they will be garbage collected automatically. Here is the full code for caller():
-- caller
local _weakKeyMT = {__mode = "k"}
local _weakValueMT = {__mode = "v"}
local _methods = setmetatable({}, _weakKeyMT)
function caller(obj, method)
local methods, call = _methods[obj]
if methods then
call = methods[method]
else
methods = setmetatable({}, _weakValueMT)
_methods[obj] = methods
end
if not call then
call = function(...) return obj[method](obj, ...) end
methods[method] = call
end
return call
end