Vararg (`...`) function parameters don't work with `arg` variable - lua

For lua, according to this tutorial, triple dots of a function can be accessed by a hidden variable named arg.
https://www.lua.org/pil/5.2.html
I write a very simple program
require 'torch'
function triDot(...)
print('in triDot now')
print(arg)
for i,v in ipairs(arg) do
print('i is',i,'v is',v)
end
end
triDot('name1','name2')
It turns out that arg doesn't hold {'name1', 'name2'} at all but a bunch of system parameters.
For loop yields nothing.
{
0 : "/home/jun/torch/install/lib/luarocks/rocks/trepl/scm-1/bin/th"
-2 : "-e"
-5 : "/home/jun/torch/install/bin/luajit"
-3 : "package.path="/home/jun/.luarocks/share/lua/5.1/?.lua;/home/jun/.luarocks/share/lua/5.1/?/init.lua;/home/jun/torch/install/share/lua/5.1/?.lua;/home/jun/torch/install/share/lua/5.1/?/init.lua;"..package.path; package.cpath="/home/jun/.luarocks/lib/lua/5.1/?.so;/home/jun/torch/install/lib/lua/5.1/?.so;"..package.cpath"
-4 : "-e"
-1 : "local k,l,_=pcall(require,"luarocks.loader") _=k and l.add_context("trepl","scm-1")"
}
Can anyone help in this?

arg doesn't work for function parameters in Lua 5.1+ (it could work in 5.1 with some compatibility options turned on; it only works for script parameters). You need to use local arg = {...} to assign function parameters to a table or use select(i, ...) to get i-th parameter from the list and select('#', ...) to get the number of parameters. The former is simpler, but the latter option can deal with nil values in the passed parameters, so use it if you expect nil values.

Related

Struggling with calling lua functions from the command line

I have the following code:
dofile(arg[1])
function1 = arg[2]
argument = arg[3]
returned = _G[function1](argument)
print(returned)
It is designed to take three command-line arguments and run a function from a file.
So, i run the command lua libs.lua "printStuff.lua" "printStuff" "\"Hello, World\"", and i always end up with this:
"Hello, World"
nil
I don't understand why i always get "nil".
Here are the contents of printstuff.lua:
function printStuff(stuff)
print(stuff)
end
That is to be expected. What's going on here:
You're executing the file specified by the first argument, printstuff.lua, which will leave a function printStuff in the global table _G.
You're indexing the global table with the second argument, printStuff, obtaining that function
You're calling the function you just obtained with the third command line argument, "Hello World!", as parameter, which prints it, and storing the result of that in the global variable returned. The function printStuff doesn't return anything (there's no return in there, and even if there was, print doesn't return anything either), so you're assigning nil to returned.
You're printing returned, which is nil
Side note: I'd use the vararg ... instead of the arg table for improved readability:
local file, func, param = ...
dofile(file); print(func(param))
Why not simply...
-- xlua.lua
-- Example: lua xlua.lua os date "%H:%M:%S"
-- Or: lua xlua.lua _G print "Hello World"
-- Or: lua xlua.lua dofile /path/to/someusefull.lua "Arg for someusefull.lua"
local result = _G[arg[1]][arg[2]](arg[3])
-- 5. Only put out whats not nil
if (result ~= nil) then
print(result)
end

Lua strange behaviour with _ENV

Can anybody explain why lua 5.3.5 acts this way?
a="global"
local b="local"
function problem(_ENV)
a="fn_a"
b="fn_b"
end
problem{}
print(_VERSION)
print("a",a)
print("b",b)
--[[ https://www.lua.org/cgi-bin/demo output:
Lua 5.3
a global
b fn_b
]]
Why local variable can be changed after _ENV changed? Is it bug or feature?
Q: How to eliminate all upvalues?
A:
a="global"
local b="local"
-- load() actually eliminates all upvalues :-)
no_problem = assert(load[[
return function (_ENV)
a="fn_a"
b="fn_b"
end
]])()
no_problem{}
print(_VERSION)
print("a",a) --> a global
print("b",b) --> b local
You are creating upvalue with the following code:
local b="local"
function problem(_ENV)
b="fn_b"
end
During parsing of the function interpreter detects, that function refers to local variable from one of encompassing scopes that are visible from within the function and thus links it directly. Access to local variables precedes lookup of globals in the _ENV. If you put local b="local" after the function definition, it will not be altered by function calls.

Declaring global variable inside a function in LUA

I have a function inside which I declared a global variable obs, inside a function and assigned some value.If I want to access this in some other lua file, it gives an error: "attempt to call obs a nil value, what do I need to do to able able to access it?
Here is the dummy code for it
//A.lua
function X()
obs = splitText(lk,MAXLEN)
end
//B.lua
function Y()
for i=1, #obs do //error at this line
end
end
There are a few ways to do this. With your current setup, you could do this:
a.lua
function x()
-- _G is the global table. this creates variable 'obs' attached to
-- the global table with the value 'some text value'
_G.obs = "some text value"
end
b.lua
require "a"
function y()
print(_G.obs); -- prints 'some text value' when invoked
end
x(); y();
Stuffing things in the global table is usually a terrible idea though, as any script anywhere else could potentially overwrite the value, nil out the variable, etc. a much better way imo would be to have your a.lua return its functionality in a table which you can capture in files which require it. this will allow you to define a getter function to return the 'obs' variable attached directly to your 'a.lua' functionality in its current state.
you probably want to do something like this for better portability (it is also much clearer which modules define which functionality this way):
a.lua
local obs_
function x()
obs_ = "some text value"
end
function get_obs()
return obs_
end
return { x = x, obs = get_obs }
b.lua
local a = require "a"
function y()
print(a.obs())
end
a.x()
y()
since you mentioned you can't use require, i'll assume you're working in some other framework that uses some other function to load libraries/files. in that case, you will probably have to just stuff everything in the global table. maybe something like this:
a.lua
-- this will attach functions a_x and a_get_obs() to the global table
local obs_
function _G.a_x()
obs_ = "some text value"
end
function _G.a_get_obs()
return obs_
end
b.lua
-- ignore this require, i'm assuming your framework has some other way loading
-- a.lua that i can't replicate
require "a"
function y()
print(_G.a_get_obs())
end
_G.a_x()
y()
Remember that some Lua programs inside other programs (Garry's Mod, World of Warcraft, Vera, Domoticz) uses _ENV instead of _G to limit their scope. So global variables has to be:
_ENV.variable = 1
instead of:
_G.variable = 1
The reason why this happens is because the developer wants to limit the standard Lua library to avoid that the user access methods like: os.exit().
To see if _ENV is used instead of _G, print it out and if it returns a table instead of nil, it's most likely used. You can also test with the following snippet:
print(getfenv(1) == _G)
print(getfenv(1) == _ENV)
Where the one to print true is the one you are using.

Why is 'name' nil for debug.getinfo(1)

I'm trying to put together a lua testing framework that lets you know the function that had the problem, but when I switched from loadstring to _G, (I switched so my test harness could see the results of the function call) my functions started using 'nil' for the function name
Why can _G not detect the name of the current function in the following code? Also, how can I get the return results from loadstring (ie the 'false' from the blah call) or set the function name when using _G (ie. Tell the lua interpreter what the function name should be)?
function run_test(one, two)
if one ~= two then
print(debug.getinfo(2).name..' Failed')
end
end
function blah()
run_test(false, true)
return false
end
local fname = 'blah'
local status, result = pcall(_G[fname]) -- Outputs 'nil'; result is 'false'
local status, result = pcall(loadstring(fname..'()')) -- Outputs 'blah', result is 'nil'
The main thing I need is a way to call a function using a string of the function name, be able to see the function name inside the call (for test failures to point to the function that failed, like fname = 'blah' in the code above) and be able to get the return value
local fname = 'blah'
status, result = pcall(??Call fname somehow??)
assert(status)
assert(not result)
--stdout should be "blah Failed"
This is a limitation of the heuristics used by Lua to provide names for functions.
In Lua, all functions are anonymous. A given function can be the value of several variables: global, local, and table fields. The Lua debug system tries to find a reasonable name for a value based on where it came from by looking into the bytecode being executed.
Consider the simpler example
blah()
pcall(blah)
In the first call, the debug system sees that the function being called comes from the global blah and debug.getinfo(1).name gives the expected result, blah.
In the second call, the debug system sees that the function being called comes from the first argument to pcall but it does not look further to see where that argument came from, and debug.getinfo(1).name gives nil.
The same thing happens when you call _G[name](). All the debug system sees is a field of a table and the name of the field is too far off.
Try adding print(debug.traceback()) as the first line of blah to see another take on this explanation.

Lua Arguments and Numbers

function OnChat(PlayerId, Type, Message)
if Message == "!usecarepackage" then
if ReadKS1 == "CP" or ReadKS2 == "CP" or ReadKS3 == "CP" then
InputConsole("msg %s has requested a care package!", Get_Player_Name_By_ID(pID))
local pos = Get_Position(Get_GameObj(pID))
pos:AssignY(pos:GetY()+1)
building = Create_Object("SignalFlare_Gold_Phys3", pos)
Attach_Script(building, "Test_Cinematic", "caredrop.txt")
if building == nil then
InputConsole("ppage %d Cannot spawn Care Package here.", pID)
elseif math.random(50, 64) == 50 then
Attach_Script(building, "z_Set_Team", Get_Team(pID))
Attach_Script(building, "M00_No_Falling_Damage_DME")
--Add in rest of numbers and corresponding streak, such as UAV if math.random = 50
end
elseif ReadKS1 ~= "CP" or ReadKS2 ~= "CP" or ReadKS3 ~= "CP" then
InputConsole("ppage %d You are not allowed to use that with your current streak selection", pID)
end
end
return 1
end
I know this is scruffy code, but I'm receiving a "Bad argument #2 to 'format' (number expected, got no value)". This relates to this piece of code which prefixes all other pieces:
function InputConsole(...)
Console_Input(string.format(unpack(arg)))
end
Finally, this is for the game Command and Conquer Renegade (should you wish to look up API etc). Any help in what I'm doing wrong would be much appreciated.
The likely problem with this code is the use of the deprecated arg feature in your function InputConsole(). Lua 5.0 used arg as a way to access the actual arguments to a function declared as variadic using the ... token in the argument list.
Edit: But likely doesn't mean correct.
The actual problem looks like the switch in idiom from PlayerId to pID. The former is the named parameter of the function OnChat(), while the latter is a global variable used in the function body without further initialization. Uninitialized globals are nil, so nil is passed to InputConsole() and the error message is telling you the truth.
Old answer that didn't solve it
Lua 5.1 deprecated that usage, and Lua 5.2 removed it entirely.
I'm not sure from the code fragments provided which version of Lua is actually used in this game, but the symptom is consistent with missing the automatically generated arg table.
I would write the function like this:
function InputConsole(...)
Console_Input(string.format(...))
end
But you could also add local arg = {...} as the first line of the function and get much the same effect as provided by Lua 5.0 at the expense of creating (and discarding) the temporary table. The differences are subtle, and mostly have to do with the treatment of nil in tables.
For clarity, I would prefer name the first argument, as it is not really optional.
function InputConsole(fmt, ...)
Console_Input(string.format(fmt, ...))
end
And if you can count on that argument being a string, that could be simplified further to
function InputConsole(fmt,...)
Console_Input(fmt:format(...))
end
If its stringiness is of concern, just say fmt = tostring(fmt) or possibly assert(type(fmt)=="string") before the call to Console_Input().

Resources