Volume widget works except for one error - lua

Every time I use my keybindings I get an error
/usr/share/awesome/lib/awful/key.lua:42: attempt to call upvalue 'press' (a string value)
Line 42:
ret[#ret]:connect_signal("press", function(kobj, ...) press(...) end)
Here's all the lines around it
--- Create a new key to use as binding.
-- This function is useful to create several keys from one, because it will use
-- the ignore_modifier variable to create more key with or without the ignored
-- modifiers activated.
-- For example if you want to ignore CapsLock in your keybinding (which is
-- ignored by default by this function), creating key binding with this function
-- will return 2 key objects: one with CapsLock on, and the other one with
-- CapsLock off.
-- #see key
-- #return A table with one or several key objects.
function key.new(mod, _key, press, release)
local ret = {}
local subsets = util.subsets(ignore_modifiers)
for _, set in ipairs(subsets) do
ret[#ret + 1] = capi.key({ modifiers = util.table.join(mod, set),
key = _key })
if press then
ret[#ret]:connect_signal("press", function(kobj, ...) press(...) end)
end
if release then
ret[#ret]:connect_signal("release", function(kobj, ...) release(...) end)
end
end
return ret
end

Related

Lua overloads the operator to create a fake array

I want to create a 0-memory lua array that actually jumps to my custom function when I use operators like # [] on it
Any ideas on how to do this?
I want the user using this fake array to not perceive it as fake, it is worse than a normal array in terms of access speed, but has better memory performance
Lua implements something called metamethods (documentation)
Metamethods are functions which exist subsequently to a table and fire on certain operations such as indexing the array, reading missing indices, gathering the length of the array, or even math operations such as + - * /
-- Start by creating your array, and another for demonstration purposes
local object = {}
local demo = {1, 2, 3, 4}
-- Create a your metamethods contained in a table
local metamethods = {
__index = function(self, index) return demo[index] end;
__newindex = function(self, index, value) demo[index] = value end;
}
-- Lets print out what is in the object table for demonstration
print(object[1]) -- nil
print(object[2]) -- nil
print(object[3]) -- nil
print(object[4]) -- nil
-- Use the setmetatable(table a, table b) function to set the metamethods
-- stored in 'b' to 'a'
setmetatable(object, metamethods);
-- Lets print out what is in the object table for demonstration
print(object[1]) -- 1
print(object[2]) -- 2
print(object[3]) -- 3
print(object[4]) -- 4
Why does the above code work? When a metatable is set with the index __index (metamethods.__index), if the attached table's (object) is indexed and the key isn't present (nil), then it will call the specified function. In the __index function, it returns the demo's table with the index passed straight to it. So its as if: when you do object[1], you actually do demo[1] but with a metamethod's help of course. This effectively creates a proxy of sorts.
One cool and quick usage of setmetatable() is that it returns the value you pass as the first parameter (a table).
local object1 = setmetatable({}, { __index = function(self, i) return 1 end })
print(object1["a"]) -- 1
print(object2[321]) -- 1

How can i use table.insert without it inserting the index?

local t={}
for i,v in pairs(game.Players:GetPlayers()) do
if v~=game.Players.LocalPlayer then
table.insert(t,v.Name)
end
end
if i do table.foreach(t,print) it also shows the index and it causes problems when i want to select a random player. The error usually tends to be something like "1 is not a valid member of workspace"
Your problem is not that table.insert adds an index, but that you use it instead of the value you want do use.
table.insert inserts an element to a list. If no index is given the value is added to the end of the list.
So there is no way to use table.insert without "inserting the index". Actually there is no way at all to add an element to a Lua table without providing a key. A Lua table is nothing but a set of key value pairs.
table.foreach calls a function for each key value pair.
You should rather use table.foreachi btw.
So table.foreachi(t, print) is equivalent to
print(1, t[1])
print(2, t[2])
...
If you just want to print the values:
for i,v in ipairs(t) do print(v) end
or for older Lua versions
table.foreachi(t, function(i,v) print(v) end)
Alternatively you run a numeric for loop:
for i = 1, t.n do print(t[i]) end
or
for i = 1, #t do print(t[i]) end
table.foreach(t,f) applies the function f to each key-value pair of t as is written. Also, since Lua 5.1 the function is deprecated.
If you want simply to print all values, do
for _, name in ipairs (t) do
print (name)
end
or
print (table.concat (t, ', '))
If you want a random name from t:
local random_name = t [math.random (#t)]

How to table.insert(x) from table.remove(x)

I want to manipulate a lua table by adding and removing elements.
I would use table.remove(x), store (x) into an array and then insert it back with table.insert(x).
a = {}
table.remove(tab, a) -- From tab into a
...
table.insert(tab, a) -- From a into tab
That won't work.
Simply because that is not how they work.
Please refer to table.insert and table.remove of Lua Reference Manual. This one is for Lua 5.1, if you use different version then you should be able to easily find proper one.
You can move elements between tables like this:
a = {}
table.insert(a, table.remove(tab)) -- From tab into a
...
table.insert(tab, table.remove(a)) -- From a into tab
You may need to verify value returned by remove:
local foo = table.remove(tab)
if type(foo) ~= "nil" then
table.insert(a, foo)
end
Note that default position for remove is the last element of the table and default position for insert is after last element of the table. Manipulating pos argument of either insert or remove may allow you to implement different behaviours.
For instance if you would like to get more scrolling-like behaviour (the one you asked for in your previous, now deleted, question):
a = {}
local foo = table.remove(tab, 1)
if type(foo) ~= "nil" then table.insert(a, foo) end -- From tab into a
...
local bar = table.remove(a, 1)
if type(bar) ~= "nil" then table.insert(tab, bar) end -- From a into tab

How can I detect when the Lua scripts access a global variable?

I started to work with a C++/Lua codebase that is somewhat a mess, and when I dump the contents of _G in the middle of the application execution, there are hundreds of variables that I am sure were only initialized somewhere, but are not used anywhere else in the code anymore. To clean this up, I would like to setup a mechanism that will log whenever Lua accesses a global variable.
This was my idea of how to achieve this – I wanted to setup a proxy _G that would only pass all read and write accesses via __index and __newindex along to its own copy of the original _G. However this simple script doesn't work and only outputs:
C:\Programs\lua-5.1.5_Win32_bin\lua5.1: error in error handling
GProx =
{
vars = _G
}
setmetatable(GProx, {
__index = function (t, name)
print("Read> " .. name)
return t.vars[name]
end,
__newindex = function (t, name, val)
print("Write> " .. name .. ' = ' .. val)
t.vars[name] = val
end
})
setfenv(0, GProx)
a = 1 --> Expected to print 'Write> a'
print(a) --> Expected to print 'Read> print', 'Read> a', and '1'
Is this a good approach or is there a better way to do this?
If this is a valid line of thought, then what is the problem with my snippet?
Try this snippet instead, it will work with reads and writes:
do
-- Use local variables
local old_G, new_G = _G, {}
-- Copy values if you want to silence logging
-- about already set fields (eg. predeclared globals).
-- for k, v in pairs(old_G) do new_G[k] = v end
setmetatable(new_G, {
__index = function (t, key)
print("Read> " .. tostring(key))
return old_G[key]
end,
__newindex = function (t, key, val)
print("Write> " .. tostring(key) .. ' = ' .. tostring(val))
old_G[key] = val
end,
})
-- Set it at level 1 (top-level function)
setfenv(1, new_G)
end
Here's a rundown of the changes:
A block is used to have a local reference to the old _G. In your proposed implementation, if a global variable named vars is set, it will override GProx.vars and break the proxy.
key and val should go through tostring before printing, since most values (ie. tables) aren't implicitly converted to strings.
Setting the environment at level 1 is usually enough and will not mess with Lua's internal workings.
You can set a metatable directly on the _G table, as explained in PIL section 14.2, so you are really close. There are also a couple of existing Lua modules on the web that do this (perhaps penlight contains one).

Lua: When and how to write tables to _G

I am learning Lua from a book, and I am NOT a programmer. I am trying to save a table of data to a file using the following functions (that were copied directly from the book), but the function is getting an error when trying to get a string from _G[resTable]. Why?
function readFromFile(filename,resTable)
local hfile = io.open(filename)
if hfile == nil then return end
local results = {} -why is this table here?
local a = 1
for line in hfile:lines() do-- debug shows this loop doesn't run (no lines in hfile?)
_G[resTable[a]] = line
a = a + 1
end
end
function writeToFile(filename, resTable)
local hfile = io.open(filename, "w")
if hfile == nil then return end
local i
for i=1, #resTable do
hfile:write(_G[resTable[i]])--bad argument #1 to 'write' (string expected, got nil)
end
end
'writeToFile" gets an error when trying to :write to _G[resTable[i]]. In the two previous functions listed here, I don't understand why they are referencing _G[resTable[i]] since I don't see any code that is writing to _G.
So here is the order of execution:
local aryTable = {
"Score",
"Lives",
"Health",
}
readFromFile("datafile", aryTable)
writeToFile("datafile", aryTable)
and I get an error:
bad argument #1 to 'write' (string expected, got nil)
stack traceback:
[C]: in function 'write'
test.lua:45: in function 'writeToFile'
test.lua:82: in main chunk
Apparently the author has implemented a way of saving a list of global variables to file and restore them.
The function writeToFile expects a filename and a list of global variables names (resTable). Then it opens a the filename for writing and iterates over the provided names:
for i=1, #resTable do
hfile:write(_G[resTable[i]])
end
in this loop resTable[i] is the i-th name and _G[resTable[i]] is the corresponding value, taken from the table _G, which stores all the globals. If a global with that name is not defined, _G[resTable[i]] will return nil, which is the cause of the failure you experienced. Thus you must provide a resTable that is filled with names of existing globals to avoid this error.
Apart from this, the serialization strategy of the author is really naive, since it handles only variables with string values. In fact by saving the variables to file like that the type information is lost, thus a variable having the value "100" (a string) and another with value 100 (a number) will be stored the same on disk.
The problem is evident analyzing the readFromFile function. After opening the file for reading, it scans it line by line, creating a new variable for each name mentioned in its resTable list:
local a = 1
for line in hfile:lines() do
_G[resTable[a]] = line
a = a + 1
end
the problem is manyfold:
the loop variable line will always have a string value, thus the recreated globals will be all strings, even if they were numbers originally;
it assumes that the variables are recreated in the same order, thus you must provide the same names in resTable you used when you saved the file;
it assumes that the values are stored one per line, but this is a false assumption, since the writeToFile function doesn't write a newline character after each value;
Moreover that local results = {} is useless and in both functions the file handle hfile is not closed. This latter is very bad practice: it could waste system resources and if your script fails part of the supposedly written data could never make its way to disk, since it may be still stuck in some buffer. File handles are automatically closed when the script ends, but only if it ends in a sane way.
Unless you did some error in pasting the code or omitted significant parts of it or the book is building some example incrementally, I dare say it is fairly crappy.
If you want a quick and dirty way to save and retrieve some globals you could use this:
function writeToFile( filename, resTable )
local hfile = io.open(filename, "w")
if hfile == nil then return end
for _, name in ipairs( resTable ) do
local value = _G[name]
if value ~= nil then
hfile:write( name, " = ")
local vtype = type( value )
if vtype == 'string' then
hfile:write( string.format( "%q", value ) )
elseif vtype == 'number' or vtype == 'boolean' then
hfile:write( tostring( value ) )
else
-- do nothing - unsupported type
end
hfile:write( "\n" )
end
end
hfile:close()
end
readFromFile = dofile
It saves the globals as a Lua script and reads them back by executing the script using Lua dofile function. Its main limitation is that it can only save strings, booleans an numbers, but usually this is enough while learning.
You can test it with the following statements:
a = 10
b = "20"
c = "hello"
d = true
print( a, b, c, d )
writeToFile( "datafile", { "a", "b", "c", "d" } )
a, b, c, d = nil
print( a, b, c, d )
readFromFile( "datafile" )
print( a, b, c, d )
If you need more advanced serialization techniques you can refer to Lua WIKI page on table serialization.
Those aren't generalized "read/write any table from/to any file" functions. They apparently expect the name of a global table as an argument, not a [reference to a local] table itself. They look like the kind of one-off solution to a very specific problem that tends to show up in books. :-)
Your functions shouldn't be doing anything with _G. I don't have an API reference handy, but the read loop should be doing something like
resTable[a] = line
and the write loop would be doing
hfile:write(resTable[i])
Throw out that local "results" table too. :-)
This code reads and writes data from a file into global variables whose names are specified in aryTable. Since your file is empty, readFromFile does not actually set the variable values. And then writeToFile fails when trying to get the variable values, because they haven't been set.
Try putting data in the file so that the variables do get set, or set the variable values yourself before writing them to the file (e.g. Score = 10, etc.)

Resources