Converting table to ... and return it - lua

Good day, I would like to know how to convert table to ... and return it.
function GetArgsNormal(...)
return ...;
end
local a,b,c=GetArgsNormal(1,2,3); --this works
print("a b c:",a,b,c);
function GetArgsTable(t,...)
for i,v in pairs(t)do
({...})[i]=v;
end
return ...;
end
local d,e,f=GetArgsTable({1,2,3},nil); --result: d=nil, e=nil, f=nil
print("def:",d,e,f);
I tried all possible ways, which I had in my mind, but without success:(
Could anyone help me in this, please?
And yes, could you please answer instead of down-voting?!

local d,e,f = unpack({1,2,3}); --result: d=1, e=2, f=3
function f()
local t = {}
-- fill table t
return unpack(t)
end

You need to be careful with 'holes' in args
function GetArgsTable(t,...)
local argc, argv = select("#", ...), {...}
-- #argv ~= argc
-- unpack(argv) ~= ...
-- assume t is array
for i,v in ipairs(t) do
argc = argc + 1
argv[argc] = v;
end
return unpack(argv, 1, argc); -- use explicit array size
end
print(GetArgsTable({1,2}, nil,'hello', nil)) -- nil hello nil 1 2
Or you can look at lua-vararg library.

Related

table.insert doesn't trigger __index?

I made a custom table using metatables that automatically tracks sizing when elements are added. It works very well and removes the need for the # operator or the getn function.
However it has a problem. If you call table.insert, the function apparently never calls __index or __newindex. Thus, my table cannot know when elements are removed this way. I assume the problem is the same with table.remove as well.
How can I either:
Capture the event of insert and use my own function to do so
Throw an error if insert is called on my table.
Thanks
function Table_new()
local public = { }
local tbl = { }
local size = 0
function public.size()
return size
end
return setmetatable(public, {
__newindex = function(t, k, v)
local previous_v = tbl[k]
rawset(tbl, k, v)
if previous_v ~= nil then
if v == nil then
size = size - 1
end
elseif v ~= nil then
size = size + 1
end
end,
__index = tbl
})
end
local t = Table_new()
t[5] = "hi"
t[17] = "hello"
t[2] = "yo"
t[17] = nil
print(t.size()) -- prints 2
local z = Table_new()
table.insert(z, "hey")
table.insert(z, "hello")
table.insert(z, "yo")
print(z.size()) -- prints 1 -- why?
If you print k,v in __newindex, you'll see that k is always 1. This is because table.insert asks for the size of table to find where to insert the value. By default, it's at the end. You should add a __len metamethod. But perhaps this defeats your purposes (which are obscure to me).

How to implement a trace function for table to functions?

I have a table like this
local ftable = {
getPinId = app.getPinId
}
ftable is passed to another function which exports it as a RPC interface.
This works but now I want to add function call tracing to a log file.
The simple approach is
local ftable = {
getPinId = function(...) print("getPinId") app.getPinId(...) end
}
But, this is not particularly nice.
I'd like to put something like:
local trace = function(func, ...)
return function(...) print(func) func(...) end
end
local ftable = {
getPinId = trace(app.getPinId)
}
But this doesn't produce quite the desired result. The parameters are not being passed through.
One other option is to use a metatable like this:
local ftable = {}
setmetatable(ftable, {
__index = function(_, k)
printf("Call: app.%s\n", k) return app[k] end
})
Which works. But I'd also like to be able to print the parameters that are passed if possible.
Any suggestions?
I'm exclusively using luajit if that makes any difference.
Wrapping a function call is easy in Lua:
local function wrap( f )
local function after( ... )
-- code to execute *after* function call to f
print( "return values:", ... )
return ...
end
return function( ... )
-- code to execute *before* function call to f
print( "arguments:", ... )
return after( f( ... ) )
end
end
local function f( a, b, c )
return a+b, c-a
end
local f_wrapped = wrap( f )
f_wrapped( 1, 2, 3 )
Output is:
arguments: 1 2 3
return values: 3 2
One problem for logging/tracing is that Lua values (including functions) don't have names themselves. The debug library tries to find suitable names for functions by inspecting how they are called or where they are stored, but if you want to make sure, you'll have to supply a name yourself. However, if your functions are stored in (nested) tables (as indicated in a comment), you could write a function that iterates the nested tables, and wraps all functions it finds using the table keys as names:
local function trace( name, value )
local t = type( value )
if t == "function" then -- do the wrapping
local function after( ... )
print( name.." returns:", ... )
return ...
end
return function( ... )
print( "calling "..name..":", ... )
return after( value( ... ) )
end
elseif t == "table" then -- recurse into subtables
local copy = nil
for k,v in pairs( value ) do
local nv = trace( name.."."..tostring( k ), v )
if nv ~= v then
copy = copy or setmetatable( {}, { __index = value } )
copy[ k ] = nv
end
end
return copy or value
else -- other values are ignored (returned as is)
return value
end
end
local ftable = {
getPinId = function( ... ) return "x", ... end,
nested = {
getPinId = function( ... ) return "y", ... end
}
}
local ftableTraced = trace( "ftable", ftable )
ftableTraced.getPinId( 1, 2, 3 )
ftableTraced.nested.getPinId( 2, 3, 4 )
Output is:
calling ftable.getPinId: 1 2 3
ftable.getPinId returns: x 1 2 3
calling ftable.nested.getPinId: 2 3 4
ftable.nested.getPinId returns: y 2 3 4
Some things to be aware of:
Table keys can be arbitrary Lua values, not just short strings entirely consisting of printable characters.
Tables can contain cyclic references. If they do, the naive implementation above will die with a stack overflow.
Use the __call metamethod instead:
M = { __call =
function (t,...) print("calling ",t.name,...) return t.func(...) end
}
trace = function(func,name)
return setmetatable({func=func,name=name},M)
end
function f(...)
print("in f",...)
end
g=trace(f,"f")
g(10,20,30)

Print a table in pairs

I'm trying to use in pairs inside a function but it doesn't work, it just prints the first row (key). This is my code:
set = {1,2,3,4};
unset = {5,6,7,8};
function listen(ftype)
if (ftype == [[~]]) then
for num,str in pairs(set) do
return str;
end
end
if (ftype == [[$]]) then
for num,str in pairs(unset) do
return str;
end
end
end
print(listen([[~]])..[[ =:= ]]..listen([[$]]));
If I do something like this..
for num,str in pairs(unset) do
print(str);
end
It works like a charm. That’s exactly what I want but inside a function.
You can build your own iterator:
function double_pair(t1, t2)
local i = 0
return function() i = i + 1
return t1[i] and t1[i] .. " =:= " .. t2[i]
end
end
Then you can use it like this:
for str in double_pair(set, unset) do
print(str)
end
Output:
1 =:= 5
2 =:= 6
3 =:= 7
4 =:= 8
Note that you don't need semicolons to end your statement, unless the statements are in one line and you want to make them clear. And [[ =:= ]] is usually used to build long multi-line strings, normally we choose to use double quote " =:= " or single quote ' =:= '.
A function can't return multiple times. Putting an unconditional return inside a loop is nonsensical - it will never get to the second iteration of the loop.
You are essentially trying to return multiple values from the function. Lua supports that; you could, for instance, just return 1,2,3,4. For an unknown number of return values, you can build them up in a table and call unpack on it, like so:
function listen(ftype)
local result = {}
local num, str
if (ftype == [[~]]) then
for num,str in pairs(set) do
table.insert(result, str)
end
elseif (ftype == [[$]]) then
for num,str in pairs(unset) do
table.insert(result, str)
end
end
return unpack(result)
end
But since your results are already in a couple tables, it would be silly to reconstruct them that way. You can just unpack the originals:
function listen(ftype)
if (ftype == [[~]]) then
return unpack(set)
elseif (ftype == [[$]]) then
return unpack(unset)
end
end
Great. But when you put the function call into an expression like your print statement, it will only return the first value, which puts you back where you started.
So to print out your pairs, you can't avoid having to either:
1) do some iteration outside the function
or
2) do the actual printing inside the function
The cleanest solution is probably a custom iterator, as suggested by #YuHao.

Lua Metatables - calling functions with colon syntax

I have the following problem, somebody can help me?
comp = {}
comp.__index = function(obj,val)
if val == "insert" then
return rawget(obj,"gr")["insert"]
end
return rawget(obj, val)
end
comp.new = function()
local ret = {}
setmetatable(ret, comp)
ret.gr = display.newGroup()
return ret
end
local pru = comp.new()
pru.gr:insert(display.newImage("wakatuBlue.png"))
This line works, but I don't want to access the insert method using the gr property, I want to call the insert method directly and the metatable __index function does the work
pru:insert(display.newImage("wakatuBlue.png"))
This line doesn't work and I get this error: "bad argument #-2 to 'insert' (Proxy expected, got nil)", but this is the way that I'm looking to use
Do you want something like this?
comp = {}
comp.__index = function(obj,val)
if val == "insert" then
return rawget(obj,"gr"):insert(val)
end
return rawget(obj, val)
end
__index works just fine; it's because your last call is interpreted as:
pru.insert(pru, display.newImage("wakatuBlue.png"))
whereas you want/need it to be:
pru.insert(pru.gr, display.newImage("wakatuBlue.png"))
You either need to call it like this or explain what you are trying to do.

Chain Lua metatables

I am have a situation where two libraries L,M, are trying to set a metatable for _G (named mL, mM respectively). The only thing in the metatables are __index.
How can I chain these two metatables so that if the __index in one fails it calls the index in the other?
Have one metatable that stores both mL and mM, and if one returns nil, check the other:
local metatbl = {}
metatbl.tbls = {mL, mM};
function metatbl.__index(intbl, key)
for i, mtbl in ipairs(metatbl.tbls) do
local mmethod = mtbl.__index
if(type(mmethod) == "function") then
local ret = mmethod(table, key)
if ret then return ret end
else
if mmethod[key] then return mmethod[key] end
end
return nil
end
end
setmetatable(_G,metatbl)
Assuming there's a point where your code can fiddle with _G's metatable itself, after the libraries have mucked about, to fix what L and M did, you can just stick in your own metatable that does the combined search, e.g.:
combined_metatable = {
__index = function (t, k)
return mL.__index (t, k) or mM.__index (t, k)
end
}
setmetatable (_G, combined_metatable)
That has the advantage of not fiddling with mL or mM themselves.
If you don't have the opportunity to correct things after the fact, you could just modify the __index entries of the library metatables to do the combined search:
local original_mM_index = mM.__index
local original_mL_index = mL.__index
local function L_then_M_index (t, k)
return original_mL_index (t, k) or original_mM_index (t, k)
end
mL.__index = L_then_M_index
mM.__index = L_then_M_index
[Note that as both library metatables are modified, this will work whichever gets installed last ("winning" the competition).]
Use __metatable to give them a table that isn't actually the metatable or give the library a different setmetatable: that way they can't change your _G metatable.
getmetatable(getfenv()).__metatable = function ( o ) return { } end
OR
local orig_setmetatable = setmetatable
function setmetatable ( ob , mt )
if ob == getfenv() or ob == _G then
return ob
else
return orig_setmetatable(ob,mt)
end
end
(depending on how the library does things)
If you still want to track the things it does to the metatable; look through mt before the return ob (and if you actually wanted to chain __index lookups; add to a table):
local env_indexes = {}
setmetatable(_G,{__index=function(t,k) for i,m in ipairs(env_indexes) do local v=m[k]; if v then return v end end return nil end } )
local orig_setmetatable = setmetatable
function setmetatable ( ob , mt )
if ob == _G then
table.insert ( env_indexes , mt.__index )
return ob
else
return orig_setmetatable(ob,mt)
end
end
Otherwise this is very bad practice for libraries to be doing; tell the author not to!

Resources