Can I select value from table based on function input? - lua

I would like to know if it's possible to select a value from a table, based on the argument of a function.
I've tried setting the value statically, and that returns the value. I would just like to do it using function arguments.
function CheckWeapon(ped, attachment)
for k,v in pairs(weapons)do
if GetHashKey(k) == GetSelectedPedWeapon(ped) then
print(v.attachment)
return v.attachment -- This needs to be based on the
-- argument "attachment"
end
end
return false
end
I would expect that if I supplied the argument "silencer" to this function, I would receive the corresponding value for silencer in the table. Instead it's nil. If I type return v.silencer manually, it works though.

In Lua you can index a table 2 ways.
As you have done you can use . such as sometable.key
but this is just syntactic sugar for the other method of indexing, sometable["key"]
both of these use the string key to index the table.
your code could look like:
function CheckWeapon(ped, key)-- where key is a string ie: "attachment"
for k,v in pairs(weapons)do
if GetHashKey(k) == GetSelectedPedWeapon(ped) then
print(v[key])
return v[key]
end
end
return false
end
using the sometable["key"] option also allows for keys that could not be accessed with . such as
sometable["my key"] -- note the space
sometable["1st_key"] -- note it begins with a number

Related

LUA - Create Meta Table with every sub table beeing a meta table again

but this will get confusing for sure.
I'm still very new to LUA and one thing i haven't worked much yet with is metatables.
I need to find a way to create a meta table which runs a function on editing values. This is not a problem if i stay on "one level", so the first index. Then i can simply use the __newindex to run it. But what i'm trying to do is to run the function whenever any value is changed.
This would require some way to set any table inside the metatable to be again a metatable running the same function as the "main" metatable
In my use case that would be a "save" function:
function MySaveFunction(tbl)
FileSave(my_settings_path, tbl)
end
MyTable = setmetatable()
MyTable.Value = value --> run MySaveFunction(MyTable.Value)
MyTable.SubTable = {} --> run setmetatable() on SubTable
MyTable.SubTable.Value = value --> run MySaveFunction(MyTable.SubTable.Value)
MyTable.SubTable.SubSubTable = {} --> run setmetatable() on SubSubTable
MyTable.SubTable.SubSubTable.Value = value --> run MySaveFunction(MyTable.SubTable.SubSubTable.Value)
MyTable.SubTable.SubSubSubTable = {} --> run setmetatable() on SubSubSubTable
MyTable.SubTable.SubSubSubTable.Value = value --> run MySaveFunction(MyTable.SubTable.SubSubSubTable.Value)
Hope someone can help me <.<
First thing to take a note of is that __newindex and __index metamethods are only triggered when they handle nil value in target table. If you want to track every single change, you can't just use __newindex, because once you write a value, subsequent calls won't do anything. Instead, you need to use a proxy table.
Another important thing to consider is tracking paths of accessed members.
Last, but not least, you need to remember to use raw access functions like e.g. rawget when implementing your handlers. Otherwise, you may encounter stack overflows or other weird behaviour.
Let's have a trivial example to illustrate the problems:
local mt = {}
function mt.__newindex (t, key, value)
if type(value) == "table" then
rawset(t, key, setmetatable(value, mt)) -- Set the metatable for nested table
-- Using `t[key] = setmetatable(value, mt)` here would cause an overflow.
else
print(t, key, "=", value) -- We expect to see output in stdout for each write
rawset(t, key, value)
end
end
local root = setmetatable({}, mt)
root.first = 1 -- table: 0xa40c30 first = 1
root.second = 2 -- table: 0xa40c30 second = 2
root.nested_table = {} -- /nothing/
root.nested_table.another = 4 -- table: 0xa403a0 another = 4
root.first = 5 -- /nothing/
Now, we need to deal with them. Let's start with a way to create a proxy table:
local
function make_proxy (data)
local proxy = {}
local metatable = {
__index = function (_, key) return rawget(data, key) end,
__newindex = function (_, key, value)
if type(value) == "table" then
rawset(data, key, make_proxy(value))
else
print(data, key, "=", value) -- Or your save function here!
rawset(data, key, value)
end
end
}
return setmetatable(proxy, metatable) -- setmetatable() simply returns `proxy`
end
This way you have three tables: proxy, metatable and data. User accesses proxy, but because it's completely empty on each access either __index or __newindex metamethods from metatable are called. Those handlers access data table to retrieve or set the actual values that user is interested in.
Run this in the same way as previously and you will get an improvement:
local root = make_proxy{}
root.first = 1 -- table: 0xa40c30 first = 1
root.second = 2 -- table: 0xa40c30 second = 2
root.nested_table = {} -- /nothing/
root.nested_table.another = 4 -- table: 0xa403a0 another = 4
root.first = 5 -- table: 0xa40c30 first = 5
This should give you an overview on why you should use a proxy table here and how to handle metamethods for it.
What's left is how to identify the path of the field that you are accessing. That part is covered in another answer to another question. I don't see a reason to duplicate it.

how can I do this with the variables in lua?

How can I make the 2 "print" give me true?
Code:
Config = {}
Config.option1.general = true
Config.option2.general = false
print(Config.option1.general)
print('Config.'..'option1'..'.general')
Output:
true
Config.option1.general
Excuse me for my ignorance
The objective was to create a function to which you give the option and execute a code with the variables of the corresponding list.
Just create a function that takes as input an option string, and use the string as a key into the Config table:
function getOption (opt)
return Config[opt].general
end
Then you can use the returned value however you like:
> getOption('option1')
true
> print(getOption('option1'))
true
> if (getOption('option1')) then print 'Yay!' else print 'Aw...' end
Yay!
If you want to live dangerously, you can use load to run a chunk of code from a string. Using this feature with user input is begging for security problems, though.
Just write a function that takes a string specifying the option, and use that input to fashion a string representing the chunk. The load function returns a function that has the chunk as its body, so you will need to call that returned function to get the result from the chunk:
function getOption (opt)
local cmd = 'Config.' .. opt .. '.general'
return load('return ' .. cmd)()
end
With getOption('option1'), the cmd string becomes 'Config.option1.general', and this is concatenated with 'return ' to create the chunk 'return Config.option1.general' which is passed to load. The statement load('return Config.option1.general')() calls the function returned by load, and the returned value is returned again from the getOption function.
Sample interaction:
> getOption('option1')
true
> getOption('option2')
false
the first print is collecting a variable and therefore displaying the value of this variable
the second print is already collecting a STRING. A string is a set of characters, they represent only text, and therefore that text will be displayed
for example, imagine that we have a variable test = true
if you do print(test), the value of the variable will be displayed, that is, true. Now, if you get print("test"), the "" means that we are talking about a text "test", not the variable test, so test will be displayed instead of true.
Note that in the second print, 2 dots .. are used, this is called CONCATENATION, it is when we join two or more strings, that is, two or more texts in one
For this reason there is no way you print true on the second print, because you are collecting a STRING with the name of the variable, and not the variable itself

Item within a table Lua

So I have a table using Lua and within it is a variable, but I can't figure out how to reach it because of its name. I was able to use json.prettify( _id ) in order to view whats in the table. All I want is the id within this table but because of how it's named I can't seem to get what's inside of _id, I tried both
_id[1] but that == nil
_id.$oid but that gave me a runtime error
printed response: _id = {
 "$oid":"597015b757203b04d6941d45"
}
interesting note is that #_id == 0
Use
tableName[ 'variableName' ]
or
tableName.variableName
Note: Sometimes you can not use both form for example:
tableName[ 'name with spaces' ] -- ok
tableNames.name with spaces -- error
I think operator # can not be used with non-indexed tables.
You need decode json string to Lua table and get value in any way:
local json = require("json")
local str = [[ { "$oid":"597015b757203b04d6941d45" } ]]
local t_res= json.decode(str)
-- access
print( t_res["$oid"] )
-- or
local k,v = next(t_res)
print( v )
-- or
for k, v in pairs(t_res) do
print(v)
break
end
PS: Lua operator # only for a regular array, with non-nil values.
In order to overcome the fact that the name of the variable included the '$' symbol the correct way to get the variable within the table was to use:
_id['$oid'], this worked.
_id.$oid gave a runtime error.

Lua: Interpreting undefined variable by its name

I have a function foo that can get nil values under certain circumstances, i.e. foo(VarA) while VarA is undefined. This undefined VarA should get interpreted as "VarA" but I can't invoke foo("VarA") because VarA should act as an option param (different from normal string params).
mt = {__index = function(t, k) return k end}
setmetatable(_G, mt)
This would get the desired result but now every other undefined variable would return its name. Like abc -> "abc". Do you see any way to only have a metatable active in this specific case? I could switch metatables in my foo method but when I'm in the foo block, the passed param is already nil and its too late.
Appendix: I think the question was not specific enough. Schollii's answer to pass params as a table was good but does not work as intended:
foo({option1 = delete, option2 = push})
This should have the key information (option1 and option2) plus the information of the value (even though delete and push do not exist in global or local namespace). Schollii's approach would give me the key information but not the value information (like "delete" and "push"). I can't define delete and push beforehand because these new "key-words" should get defined by the function itself later on. Passing those new keywords as a string is no option.
It seems like you are developing a domain-specific language within Lua. If that is your intent and you are successful, great. Otherwise, it would be better to stick to more a typical Lua programming style.
Similar to other suggestions:
-- lets the caller decide on the scope of varA
-- and the default string
foo(varA or "varA")
All you need is to test for nil and set VarA's value to its name:
function yourFunc(VarA)
VarA = VarA or "VarA" -- if VarA is nil, it becomes the string "VarA"
... use VarA ...
end
However, you say that VarA should act as an option parameter. I gather you mean that VarA is optional. OK, but Lua does not support named arguments in function calls so if you have
function yourFunc(arg1, arg2, optArg1, optArg2)
...
end
then in order to call yourFunc with optArg2=something, you have to set optArg1 to nil:
yourFunc(1, 2, nil, 3)
In other words you can't do yourFunc(1,2,optArg2=3) which would be pretty neat. But you can get pretty close by having yourFunc accept a table instead of a list of parameters:
function setOptionalArgs(tbl, defaults)
for i,v in ipairs(defaults) do
if tbl[v] == nil then
tbl[v] = v
end
end
for k,v in pairs(defaults) do
if tbl[k] == nil then
tbl[k] = v
end
end
end
function yourFunc(args)
setOptionalArgs(args, {'arg1', arg2=2, 'arg3'})
-- ... use args.arg1, args.arg2 etc ...
print(args.arg1, args.arg2, args.arg3)
end
yourFunc {}
Now you can call
yourFunc {arg1=1}
which will automatically have arg2="arg2". Note that to be clean you should modify setOptionalArgs so only non-array keys get inserted in second loop.

Get name of argument of function in lua

When call a lua function like
PrintMe(MyVariableName)
I would like to be able to actually print "MyVariableName" and not it's value(well, for demo purposes).
Obviously I could just pass the string but that requires extra quotes and I also would like to print it's value.
e.g.,
MyVariable = 4
PrintVariable(MyVariable)
Would print "MyVariable is 4" or whatever
I do not want to have to duplicate the name and variable like
PrintVariable(MyVariable, "MyVariable")
as this is unnecessary duplication.
Can lua handle it?
What I'm doing now is passing the variable name in quotes and using loadstring to get the value but I would like to just pass the variable directly without the extra unnecessary quotes(which I thought debug.getlocal did but it ends up returning the value instead of the name).
Here is mock example
function printme1(var, val)
print(var.." = "..val)
end
function printme2(v)
local r
loadstring("r = "..v)() -- equivalent to r = a but must be used since v is a string representing a and not the object a
print(v.." = "..tostring(r))
end
function printme3(v)
-- unknown
end
a = 3
printme1("a", a)
printme2("a")
printme3(a)
In this case all 3 should print the same thing. printme3 obviously is the most convenient.
You can't say PrintVariable(MyVariable), because Lua gives you no way of determining which variable (if any; a constant could have been used) was used to pass an argument to your function. However, you can say PrintVariable('MyVariable') then used the debug API to look for a local variable in the caller's scope which has that name:
function PrintVariable(name)
-- default to showing the global with that name, if any
local value = _G[name]
-- see if we can find a local in the caller's scope with that name
for i=1,math.huge do
local localname, localvalue = debug.getlocal(2,i,1)
if not localname then
break -- no more locals to check
elseif localname == name then
value = localvalue
end
end
if value then
print(string.format("%s = %s", name, tostring(value)))
else
print(string.format("No variable named '%s' found.", name))
end
end
Now you can say:
PrintVariable('MyVariable')
While in this case will print "MyVariable = 4".
Not, if you really want to do this without the quotes, you could check the caller's locals for variables that have a supplied value, but that's occasionally going to give you the wrong variable name if there is more than one variable in the caller's scope with a given value. With that said, here's how you'd do that:
function PrintVariable(value)
local name
-- see if we can find a local in the caller's scope with the given value
for i=1,math.huge do
local localname, localvalue = debug.getlocal(2,i,1)
if not localname then
break
elseif localvalue == value then
name = localname
end
end
-- if we couldn't find a local, check globals
if not name then
for globalname, globalvalue in pairs(_G) do
if globalvalue == value then
name = globalname
end
end
end
if name then
print(string.format("%s = %s", name, tostring(value)))
else
print(string.format("No variable found for the value '%s'.", tostring(value)))
end
end
Now you can say PrintVariable(MyVariable), but if there happened to be another variable in the caller's scope with the value 4, and it occurred before MyVariable, it's that variable name that will be printed.
you can do stuff like this with the debug library... something like this does what you seem to be looking for:
function a_func(arg1, asdf)
-- if this function doesn't use an argument... it shows up as (*temporary) in
-- calls to debug.getlocal() because they aren't used...
if arg1 == "10" then end
if asdf == 99 then end
-- does stuff with arg1 and asdf?
end
-- just a function to dump variables in a user-readable format
function myUnpack(tbl)
if type(tbl) ~= "table" then
return ""
end
local ret = ""
for k,v in pairs(tbl) do
if tostring(v) ~= "" then
ret = ret.. tostring(k).. "=".. tostring(v).. ", "
end
end
return string.gsub(ret, ", $", "")
end
function hook()
-- passing 2 to to debug.getinfo means 'give me info on the function that spawned
-- this call to this function'. level 1 is the C function that called the hook.
local info = debug.getinfo(2)
if info ~= nil and info.what == "Lua" then
local i, variables = 1, {""}
-- now run through all the local variables at this level of the lua stack
while true do
local name, value = debug.getlocal(2, i)
if name == nil then
break
end
-- this just skips unused variables
if name ~= "(*temporary)" then
variables[tostring(name)] = value
end
i = i + 1
end
-- this is what dumps info about a function thats been called
print((info.name or "unknown").. "(".. myUnpack(variables).. ")")
end
end
-- tell the debug library to call lua function 'hook 'every time a function call
-- is made...
debug.sethook(hook, "c")
-- call a function to try it out...
a_func("some string", 2012)
this results in the output:
a_func(asdf=2012, arg1=some string)
you can do fancier stuff to pretty this up, but this basically covers how to do what you're asking.
I have bad news, my friend. You can access function parameter names as they appear at the top of the function, but the data to access exactly what they were named in the calling function does not exist. See the following:
function PrintVariable(VariableToPrint)
--we can use debug.getinfo() to determine the name 'VariableToPrint'
--we cannot determine the name 'MyVariable' without some really convoluted stuff (see comment by VBRonPaulFan on his own answer)
print(VariableToPrint);
end
MyVariable = 4
PrintVariable(MyVariable)
To illustrate this, imagine if we had done:
x = 4
MyVariable = x
MyOtherVariable = x
x = nil
PrintVariable(MyVariable)
Now if you were Lua, what name would you attach in the metadata to the variable that ends up getting passed to the function? Yes, you could walk up the stack with debug.getint() looking for the variable that was passed in, but you may find several references.
Also consider:
PrintVariable("StringLiteral")
What would you call that variable? It has a value but no name.
You could just use this form:
local parms = { "MyVariable" }
local function PrintVariable(vars)
print(parms[1]..": "..vars[1])
end
local MyVariable = "bar"
PrintVariable{MyVariable}
Which gives:
MyVariable: bar
It isn't generic, but it is simple. You avoid the debug library and loadstring by doing it this way. If your editor is any good, you could write a macro to do it.
Another possible solution is add this facility your self.
The Lua C API and source is pretty simple and extendable.
I/we don't know the context of your project/work but if you ARE making/embedding your own Lua build you could extend the debug library with something to do this.
Lua passes it's values by reference, but unknown offhand if these contain a string name in them and if so if easily accessible.
In your example the value declaration is the same as:
_G["MyVariable"] = 4
Since it's global. If it were declared local then like others stated here you can enumerate those via debug.getlocal(). But again in the C context of the actual reference context it might not matter.
Implement a debug.getargumentinfo(...) that extends the argument table with name key, value pairs.
This is quite an old topic and I apologize for bringing it back to life.
In my experience with lua, the closest I know to what the OP asked for is something like this:
PrintVariable = {}
setmetatable(PrintVariable, {__index = function (self, k, v) return string.format('%s = %s', k, _G[k]) end})
VAR = 0
VAR2 = "Hello World"
print(PrintVariable.VAR, PrintVariable.VAR2)
-- Result: VAR = 0 VAR2 = Hello World
I do not give more explanation, because the code is quite readable, however:
What happens here is simple, you only set a metatable to the PrintVariable variable and add the __index metamethod that is called when the table is forced to search for a value in its index, thanks to this functionality you can achieve what you see in the example.
Reference: https://www.lua.org/manual/5.1/manual.html
I hope that future and new visitors will find this helpful.

Resources