This is what I'd do:
-- pack one func + args into a table
function record( f, ... )
return { func = f, n = select( '#', ... ), ... }
end
-- take a list/array/sequence of `record`ed functions & run them
function run( list )
for _, callinfo in ipairs( list ) do
callinfo.func( table.unpack( callinfo, 1, callinfo.n ) )
end
end
Sample use:
todo = { }
todo[#todo+1] = record( print, "foo", "blah", nil, 23 )
todo[#todo+1] = record( print, "baz" )
run( todo )
--> foo blah nil 23
--> baz
Some obvious variations include doing a pcall
in run
(so errors won't abort it in the middle), or adding an extra function that takes ( list, f, ... )
and which packs the call info & appends it to the list.
If you can be certain that there's no nil
in the middle of the argument list, you can simplify to…
-- pack one func + args into a table
function record( f, ... )
return { func = f, ... }
end
-- take a list/array/sequence of `record`ed functions & run them
function run( list )
for _, callinfo in ipairs( list ) do
callinfo.func( table.unpack( callinfo ) )
end
end
…but I'd strongly suggest to do this only at the end when the rest of the code is done and you know (measure!) that this is slow. (If there's a bug that introduces accidental nil
s into the argument list, this version will alter the argument list (by dropping elements), while the first will pass it through unchanged no matter what, which is debug-friendlier.)
If you're really short on space, you might be able to save one value's worth of space by not using a named field for the function as in…
(addendum: Except for Lua 5.2, this also seems to be a bit faster than the other two variants above – see comments below.)
-- (you can actually just do this inline)
function record( f, ... ) return { f, ... } end
-- take a list/array/sequence of `record`ed functions & run them
function run( list )
for _, callinfo in ipairs( list ) do
callinfo[1]( table.unpack( callinfo, 2 ) )
end
end
…but that depends on the number of arguments and may actually waste space!
(For vanilla (PUC-Rio) Lua on x86_64, it saves 16 bytes (1 TValue) for 0, 1, 3, or 7 arguments, does nothing for 2 or 6 arguments, wastes 32 bytes for 4, 16 bytes for 5, and 112 bytes for 8 arguments (and the same power-of-two pattern keeps growing/repeating).)