While working on Lunatic Python, I’ve understood that Lua does specific type checking with lua_isfunction() in some places where a callable type is expected. As a side effect, these places only accept a real Lua function when the callable object might be used. An example of such behavior is, at the time this is being written, the table.foreach() function:
> obj = {} > mt = {__call=function() print("Called!") end} > setmetatable(obj, mt) > obj() Called! > table.foreach({a=1}, obj) stdin:1: bad argument #2 to `foreach' (function expected, got table) stack traceback: [C]: in function `foreach' stdin:1: in main chunk [C]: ?
The trick used in Lunatic Python to overwhelm this situation was to enclose the custom Python object type inside a real Lua C function closure. This trick might indeed be used anywhere this situation is found. Here are a few functions that allow this trick to be used from inside Lua:
static int lwrapcall(lua_State *L) { lua_pushvalue(L, lua_upvalueindex(1)); lua_insert(L, 1); lua_call(L, lua_gettop(L)-1, LUA_MULTRET); return lua_gettop(L); } static int lwrapfunc(lua_State *L) { luaL_checkany(L, 1); lua_pushcclosure(L, lwrapcall, 1); return 1; } static int luaopen_wrapfunc(lua_State *L) { lua_pushliteral(L, "wrapfunc"); lua_pushcfunction(L, lwrapfunc); lua_rawset(L, LUA_GLOBALSINDEX); }
And here is another implementation, by Alex Bilyk, in pure Lua:
function wrapfunc(callable) return function(...) return callable(unpack(arg)) end end
Using them one would be able to obtain the effect above as follows:
> table.foreach({a=1}, wrapfunc(obj)) Called!
Hopefully, in the future the standard Lua library will stop checking for a specific type in such cases, or implement some kind of lua_iscallable() checking.