I wrote a function in Lua for Conky to provide a list of top processes to be displayed with a delay of 3 sections (intervals) before refreshing. This requires capturing the refresh data to be stored and displayed on each following interval before the next refresh.
All works well with the code below but I am puzzled that I cannot define global tables t1, t2, t3, and t4 within the function block of conky_top_control(). I've had to create them outside the block but for appearance sake, I wanted to keep table creation inside the function code block. I've tried listing them with no "local" define in front of the tables. And I cannot find the right syntax to use the environmental declaration _G to the table creations either, if that is even correct usage.
So, the question is: how do you define a table inside a function as global? Specifically as it pertains to tables t1, t2, t3, and t4 defined below?
Here is the code in Lua, which is called from Conky ${lua top_control):
function conky_timer(interval)
return tonumber(conky_parse("${updates}") % interval+1)
end
t1,t2,t3,t4={},{},{},{}
function conky_top_control()
if conky_timer(3)==1 then
for i=1,5 do
t1[i]=conky_parse('${top name '..i..'}')
t2[i]=conky_parse('${top pid '..i..'}')
t3[i]=conky_parse('${top cpu '..i..'}')
t4[i]=conky_parse('${top mem '..i..'}')
end
end
return conky_parse(t1[1]..'${goto 129}'..t2[1]..'${goto 174}'..t3[1]..'${goto 219}'..t4[1]..'\n'..t1[2]..'${goto 129}'..t2[2]..'${goto 174}'..t3[2]..'${goto 219}'..t4[2]..'\n'..t1[3]..'${goto 129}'..t2[3]..'${goto 174}'..t3[3]..'${goto 219}'..t4[3]..'\n'..t1[4]..'${goto 129}'..t2[4]..'${goto 174}'..t3[4]..'${goto 219}'..t4[4]..'\n'..t1[5]..'${goto 129}'..t2[5]..'${goto 174}'..t3[5]..'${goto 219}'..t4[5])
end
Actually, you don't need globals. In general, they are a poor practice that causes difficult errors in your code and other code.
Try it this way:
local t1,t2,t3,t4={},{},{},{} -- state variables for conky_top_control
function conky_top_control()
-- use t1,t2,t3,t4
end
Since the locals t1,t2,t3,t4 are in the scope where conky_top_control is defined, they are captured as references. Their values will persist across calls to conky_top_control, which can read and modify them.
Now, appearance considerations give way to a satisfying design.
You can move the global variable initialization into the function by just adding a conditional that checks whether the globals were previously initialized.
function conky_top_control()
if not t1 then
t1,t2,t3,t4={},{},{},{}
end
-- rest of function
end
This way the globals will only be set to blank tables the first call to the function.
I am puzzled that I cannot define global tables t1, t2, t3, and t4 within the function block of conky_top_control()
There is no way/need to define global variables. Things stop working when you move the assignment to the function because your variables then get re-assigned every time that function is called.
I cannot find the right syntax to use the environmental declaration _G to the table creations either, if that is even correct usage.
You can use _G.t1 syntax to reference t1 table from the (global) environment. See this reference for details.
In Lua every variable is global unless it is declared local.
If you get errors defining global variables inside a function then perhaps Conky has set up some restrictions.
Related
I have a personal project and a pure lua writed object module which provides metatable with methods filter map etc to a table , I don't want to require and setmetatable for every line local foo={}
Global Constructor
I have a personal project and a pure lua writed object module which provides metatable with methods filter, map etc to a table , I don't want to require and setmetatable for every line local foo={}.
You can avoid the need for require by just ensuring that your "object module" is always loaded first, setting global variables. I consider require to be cleaner however as it makes clear the dependencies of your files (and does not pollute the global environment).
To avoid the need for setmetatable, you can write yourself a constructor function:
local metatable = ...
function Table(t) -- NOTE: global function
return setmetatable(t, metatable)
end
then, in another file which you ensure only runs after this file was executed:
local foo = Table{}
You might want to shorten Table to T if you use this very often.
Setting a metatable for all (new) tables
Do you really want this?
First of all: You probably do not want local t = {} to set a metatable on t. This would mess with linters like Luacheck while also making your code hard to follow for everyone familiar with Lua but unfamiliar with this hack.
Setting a metatable with __index also interferes with the usage of tables as dictionaries / hash maps; users now need to use rawget / rawset to circumvent your metatable. Consider the following snippet:
local dict = { filter = "bar" }
print(dict.filter) -- "bar", as expected
print(dict.map) -- filter function - unexpected
print(rawget(dict, "map")) -- nil, as expected
It will also harm performance of every table access. Do you really want this just for some syntactic sugar?
Furthermore, if you heavily set metamethods (such as the arithmetic metamethods) even if it doesn't really make sense, you again get unexpected behavior by allowing passing tables where numbers are expected etc. Lua's partial strictness when dealing with incompatible types is what distinguishes it from JS.
How to do it
how to automatically set default metatable to every newly created table?
This is not possible in general; the proper way to set metatables is to explicitly use constructors.
debug.setmetatable, primitives & functions
Using debug.setmetatable, you can set "shared"/"common" metatables for all primitive types (boolean, number, string) as well as functions. You can not set a shared metatable for objects this way however.
Hooking global/environmental variable access
This is what koyaanisqatsi's snippet does: It catches global variable access and sets the metatable on all new table global variables. This is insufficient as it forces you to use global/environmental variables, which is both bad for performance and code quality. It will in particular not work at all for local variables such as the local foo = {} in your example. It will also not work for temporary variables in expressions (consider ({...}):filter(...)).
Debug Hooks
The following approach would be more reliable than hooking environmental variable access:
Set a debug hook that runs after every instruction.
Iterate over locals & upvalues and set the metatable for each table; perhaps remember old/new locals/upvalues/tables.
Eventually consider doing this recursively, for structures such as {{}}.
Obviously this would be awfully slow. It is very likely that there still exist many "edge cases" this doesn't catch (what about table creation in C, for instance?).
Forking Lua
The only proper solution would be to add such a feature - a default metatable for all tables - to the language by forking it and implementing this right in the function where Lua creates new tables. This is the only way this could be implemented adequately - that is, with adequate performance & reliability.
For this you can set/use the Metamethod __newindex that triggers at defining something New.
For "every newly table" the right Place is: _G
_G = setmetatable(_G,
{__index = table,
__newindex = function(tab, key, value)
print('[__newindex]:', tab, key, value)
if type(value) == 'table' then
rawset(tab, key, setmetatable(value, getmetatable(tab)))
else
rawset(tab, key, value)
end
return
end
})
--[[ Example of use:
> a = {}
table: 0xf7ed2380 a table: 0xf7ed9738
> a:insert('Hi There!')
> print(a:concat())
Hi There!
> a[#a + 1] = {}
[__newindex]: table: 0xf7ed9738 2 table: 0xf7edbe40
]]
What will not work under LuaJIT (Lua 5.1)?
__gc will not be triggered at all
table.insert({'I (Key [1]) am a string in a table'}) will not triggered by __newindex
I have a piece of lua code that is called from a C program that passes it a table.
The table contains a table which has a field that LUA reports is of type "function".
I want to invoke that function and get its return value, but the following just causes a crash (????stack corruption????):
function myFunc (tableIn)
local interface = tableIn["interface"] -- type(interface) is table
local targetFunc = interface["targetFunc"] -- type(targetFunc) is function
local value = targetFunc()
...
end
I'm therefore assuming that I need to pass one or more parameters to targetFunc(...) but I have no idea what targetFunc() is expecting and I don't have access to the documentation or code for targetFunc - which I'm also guessing is written in C.
I've seen the thread lua - get the list of parameter names of a function, from outside the function and run the code as suggested - but that just seems to return "*temporary".
The question: is there some "standard" way of calling a C routine from LUA - or is there any other programmatic way (even a one-off) of finding out how many and what type of parameters targetFunc(...) needs.
Even an answer of "no - you need to see the code / doc for targetFunc(...)" would help!
Thanks in advance...
When I have functions which take many arguments it's sometimes useful to pass a single table as an argument instead of many local variables.
function example_1(arg_one, arg_two, arg_three)
end
becomes
function example_2(arg_table)
arg_table.arg_one, arg_table.arg_two, arg_table.arg_three
end
The problem is, when calling the function somewhere else in code, it's hard to remember what arg_table needs to consist of. There are plenty of code-completion plugins for many editors which will help you with remembering the arguments for the example_1 function, but not for example_2.
Is there any way to write the example_2 function with a table parameter in a way that is still a table, but also shows the necessary parameters for the function inside the ()?
something like this (which does not work):
function example_2(arg_table = {arg_one, arg_two, arg_three})
end
Write your formal parameter list and documentation with separate parameters, as usual. Then document that if the first (and only) actual argument is a table, the effective arguments will be taken from the table using the formal parameter names as string keys.
You can keep the function parameters as they are and pass a table for convenience.
For example:
function example(arg_one, arg_two, arg_three)
-- do stuff
end
local tbl = {"value for arg_one", "value for arg_two", "value for arg_three"}
example(table.unpack(tbl))
Note that this doesnt work for tables with named keys.
I want to use a Lua API which has specific callback functions when events occur, e.g. when an TCP package arrives. At first the function have to be registered but by the functions name as a string, see the sample code below
function __init__()
local dstport = 4681
local dstIP = "192.168.1.42"
-- register the callback function
register_tcp_handler('tcp_package_handler', dstIP, dstPort)
end
-- callback function
function tcp_package_handler(srcIP, srcPort, dstIP, dstPort, payload)
-- check the payload, or reset watchdog
end
It would be nice to have other variables in callback function provided by the callee, e.g. watchdog-timer or other objects.
The most simple way I could think of is to make the extravariables global, but it is the least elegant way I reckon. Closures would be helpful if I could pass the function directly, but i can not. I have to use the functions name as a string.
Considering this mechanics, is there a more elegant way to privide variables to the callback function without making them global?
EDIT: Using closures like this
function closure_tcp_package_handler(srcIP, srcPort, dstIP, dstPort, payload, packagecounter, timerobject)
function tcp_package_handler(srcIP, srcPort, dstIP, dstPort, payload)
-- do some stuff, change packagecounter, timerobject
end
return 'tcp_package_handler'
end
and use this function twice to register, e.g. with packagecounter1, timerobject1 and packagecounter2, timerobject2, only the last pair of parameters will be changed.
You're dealing with a callback infrastructure. In which case, your code is not the one invoking the handler. As such, there's no way to hide those parameters; if you can change them, so can someone else with access to the module providing the handler.
That doesn't mean that they have to be global, of course. You could make them members of a table. You could even provide setter functions to set the parameters, if you want to make sure that they only get certain parameters.
The simple form of this is as follows:
local handler_params = {}
function tcp_package_handler(srcIP, srcPort, dstIP, dstPort)
-- check `handler_params.payload`
end
--Make `handler_params` available for outside modification
How you do that last part is entirely up to you. You could have made it a global, but if this is in a module somewhere, it'd be better to make it a member of that module's table. And again, if you want to have some control over who gets to poke at it and how, you can use setter functions:
function tcp_handler_set_payload(payload)
handler_params.payload = payload
end
What is the difference between the following Lua scripts in terms of function scope. How would it effect the need to require 'calculator' in some other Lua script. And how would it be referenced in say a LuaState.getGlobal(function_name). What would be its proper function name? Also any comment on advantages/disadvantages of the declaration approaches.
A) Calculator.lua
function foo(n)
return n+1;
end
B) Calculator.lua
calc= {}
function calc.foo(n)
return n+1;
end
C) Calculator.lua
function foo(n)
return n+1;
end
function calculator()
calc = {}
calc.foo=foo
return calc
end
I don't know what it is you mean exactly by the "scope" of these scripts. Exactly what these scripts do depends on how they're being executed. You could give them a different environment, thus changing what they think of as "globals".
So, I will explain what each of these scripts does based on the assumption that you are loading them with dofile, dostring, or something of the like. That is, you're applying them to the global environment.
A)
This creates a single global variable, foo, which is a function.
B)
This creates a single global variable, calc, which is a table. This table has a single entry, with the key foo, who's value is a function.
C)
This creates two global variables. foo is a function. calculator is also a function. Each call to calculator will cause the global variable calc to be overwritten. The new value of calc will be a table that has a single entry, with the key foo, who's value is a copy of what is stored in the global variable foo.
It's hard to say what the "advantages" of method C are, since it makes no sense. It creates two functions instead of one, and that second function keeps creating new tables.
B is just a "namespace" scoped version of A. The general expectation with Lua modules is that including them will usually create some table that will contain all of the functions in that module, to avoid name conflicts with existing global state. In that regard, B may be better. But it depends mostly on what this script will be used for.
Personally, for simple shell scripts and utility scripts, I don't bother with proper module scoping. When I do make a proper module, I generally use the module Lua function.
Not exactly an answer, but a comment on semantics - in Lua, you do not declare a function, you create it. The function becomes available at the time the function(...) ... end is executed. How it is available depends on where you store it.
You have to remember that:
function myfun(foo) print('foo') end
is just syntactic sugar for:
myfun = function(foo) print('foo') end
If you do not do anything special, myfun becomes a global variable stored in the global state (accessible through _G). If you say local myfun before actually calling function myfun() end, the function (actually a closure) will be stored in the local variable and available only to the scope of the local variable.
If you use require, it just finds the file and loads it once. It doesn't do anything fancy with the module like hiding globals, etc. So if you write function foo() end in your module Calculator.lua, then calling require 'Calculator' will create a function in global foo, which you can access using LuaState.getGlobal("foo"). If you create a table of functions (step B), you have to use 2 steps:
L.getGlobal("calc") -- pushes the "calc" global (a table) to the stack
L.getField(-1, "foo") -- gets the "foo" field from the table