Declaring global variable inside a function in LUA - 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.

Related

How to run a string and get a table that has all the variables that the code created in lua?

So i'm making my own OS for LIKO-12 and I need to run .lk12 files but i need to have a function to run a string as code and return a table that has all of the variables that code created
Example:
function foo()
return "Hello,"
end
function bar()
return " World!"
end
hello = foo()..bar()
should return a table with 2 functions:
A function called foo that returns "Hello,"
A function called bar that returns " World!"
and a variable called hello that has "Hello, World!"
You should be able to call a function from the code using
vars["foo"]() or vars.foo()
Can anyone help me?
When you create or use a global variable it's actually stored in a table called "the environment", or _ENV (which is not global, it's a local that you get automatically)
So your code:
function foo()
return "Hello,"
end
function bar()
return " World!"
end
hello = foo()..bar()
is really doing:
function _ENV.foo()
return "Hello,"
end
function _ENV.bar()
return " World!"
end
_ENV.hello = _ENV.foo().._ENV.bar()
so if we just set _ENV to a new table, and return it, then this is exactly the table you want. You can do this with load (which is how you run strings) by passing it as the 4th argument. This function will do that:
function loadAndReturnEnv(codeString)
local env = {}
load(codeString, nil, nil, env)() -- error checking skipped for demonstration
return env
end
However, note that all the usual global stuff like string.rep, table.sort, load, print don't exist in this new _ENV. If that's what you want, that's great. But I guess you probably do want them. In that case we can use the metatable __index feature, so that if the code looks for something in its _ENV and it's not there, it looks in our (the caller's) _ENV instead.
function loadAndReturnEnvWithGlobals(codeString)
-- note: env is the environment for the code we're about to run
-- (which will be called _ENV in that code), and _ENV is the environment
-- of the loadAndReturnEnvWithGlobals function (i.e. the "real" environment)
local env = {}
setmetatable(env, {__index=_ENV})
load(codeString, nil, nil, env)() -- error checking skipped for demonstration
setmetatable(env, nil) -- set the table back to normal before we return it
return env
end
But wait... the program can now call print when you run it, but if it defines a function, that function can't call print since we deleted the link back to the real environment after the code returned. So I think the best way to fix that is to leave the environment linked, and just copy the functions etc.
function loadAndReturnEnvWithGlobals2(codeString)
local env = {}
setmetatable(env, {__index=_ENV})
load(codeString, nil, nil, env)() -- error checking skipped for demonstration
local result = {}
for k,v in pairs(env) do result[k]=v end
return result
end
although this copies the values, so if you have any functions that change the variables, you won't see the changed variables.
Which version you use is up to you. They each have pros and cons.

Is there an easy way to unpack two arrays/varargs in Lua?

Lets take a look at this pseudo code example:
-- Wraps a function with given parameters inside a callback
-- Usefull for sending class methods as callbacks.
-- E.g. callback(object.method, object)
local function callback(func, ...)
return function(...)
func(..., ...)
end
end
How would I go about this?
I know there is unpack, but it would get swallowed by the second vararg:
local function callback(func, ...)
local args = { ... }
return function(...)
func(table.unpack(args), ...)
end
end
callback(print, "First", "Second")("Third") --> prints First Third
The only option I found so far, is to concat those together and then unpack it:
local function callback(func, ...)
local args1 = { ... }
return function(...)
local args2 = { ... }
local args = { }
for _, value in ipairs(args1) do
table.insert(args, value)
end
for _, value in ipairs(args2) do
table.insert(args, value)
end
func(table.unpack(args))
end
end
Is this the only solution, or could I do better?
What I would like to have is either a function, that concats two arrays together (which should be faster than those two for loops) and then use table.unpack on it, or make those varargs concat.
From the Lua 5.4 Reference Manual: 3.4 Expressions
If an expression is used as the last (or the only) element of a list
of expressions, then no adjustment is made (unless the expression is
enclosed in parentheses). In all other contexts, Lua adjusts the
result list to one element, either discarding all values except the
first one or adding a single nil if there are no values.
So the only way is to manually combine both lists to a single one befor you use them.
There are several ways to do this.
for i,v in ipairs(list2) do
table.insert(list1, v)
end
for i,v in ipairs(list2) do
list1[#list1+i] = v
end
table.move(list2, 1, #list2, #list1 + 1, list1)
I'm not sure what kind of problem you're actually trying to solve here.
If you want to access your object from its methods use self.
-- Wraps a function with given parameters inside a callback
-- Usefull for sending class methods as callbacks.
-- E.g. callback(object.method, object)
Usually you would do something like this:
local callback = function(params) object:method(params) end
callback(params)
Instead of
callback(print, "First", "Second")("Third")
You could do this
local callback = function (...) print("First", "Second", ...) end
callback("Third")
Edit ( opinonated )
My main goal is to use member functions as callbacks. However, I'd
like to keep it general. The advantage of a callback function is to
omit the function and end keyword. It's shorter and looks closer to
what it would look like, if there would be no need for a self
argument. So this: object.event = function(...) return
self:method(...) end would become to this: object.event =
callback(self.method, self) what would be even nicer, is this (sadly
not possible in lua) object.event = self:method
So instead of
RegisterCallback(function() obj:method() end)
You say it is easier to do this, so you don't have to write function end?
local function callback(func, ...)
local args1 = { ... }
return function(...)
local args2 = { ... }
local args = { }
for _, value in ipairs(args1) do
table.insert(args, value)
end
for _, value in ipairs(args2) do
table.insert(args, value)
end
func(table.unpack(args))
end
end
RegisterCallback(callback(obj.method, obj)())
Your approach is not going to work if any of the arguments is nil to begin with. And there is not a single advantage. You're just typing other words while increasing the chance of running into errors.
You can control the param order to optimize your function.
local function callback(func, ...)
local args = {...}
return function(...)
func(..., table.unpack(args))
end
end
callback(print, "second", "third")("first") -- prints first second third

Globals are bad, does this increase performance in any way?

I'm working in LuaJIT and have all my libraries and whatnot stored inside "foo", like this:
foo = {}; -- The only global variable
foo.print = {};
foo.print.say = function(msg) print(msg) end;
foo.print.say("test")
Now I was wondering, would using metatables and keeping all libraries local help at all? Or would it not matter. What I thought of is this:
foo = {};
local libraries = {};
setmetatable(foo, {
__index = function(t, key)
return libraries[key];
end
});
-- A function to create a new library.
function foo.NewLibrary(name)
libraries[name] = {};
return libraries[name];
end;
local printLib = foo.NewLibrary("print");
printLib.say = function(msg) print(msg) end;
-- Other file:
foo.print.say("test")
I don't really have the tools to benchmark this right now, but would keeping the actual contents of the libraries in a local table increase performance at all? Even the slightest?
I hope I made mysef clear on this, basically all I want to know is: Performance-wise is the second method better?
If someone could link/give a detailed explaination on how global variables are processed in Lua which could explain this that would be great too.
don't really have the tools to benchmark this right now
Sure you do.
local start = os.clock()
for i=1,100000 do -- adjust iterations to taste
-- the thing you want to test
end
print(os.clock() - start)
With performance, you pretty much always want to benchmark.
would keeping the actual contents of the libraries in a local table increase performance at all?
Compared to the first version of the code? Theoretically no.
Your first example (stripping out the unnecessary cruft):
foo = {}
foo.print = {}
function foo.print.say(msg)
print(msg)
end
To get to your print function requires three table lookups:
index _ENV with "foo"
index foo table with "print"
index foo.print table with "say".
Your second example:
local libraries = {}
libraries.print = {}
function libraries.print.say(msg)
print(msg)
end
foo = {}
setmetatable(foo, {
__index = function(t, key)
return libraries[key];
end
});
To get to your print function now requires five table lookups along with other additional work:
index _ENV with "foo"
index foo table with "print"
Lua finds the result is nil, checks to see if foo has a metatable, finds one
index metatable with "__index"
check to see if the result is is is table or function, Lua find it's a function so it calls it with the key
index libraries with "print"
index the print table with "say"
Some of this extra work is done in the C code, so it's going to be faster than if this was all implemented in Lua, but it's definitely going to take more time.
Benchmarking using the loop I showed above, the first version is roughly twice as fast as the second in vanilla Lua. In LuaJIT, both are exactly the same speed. Obviously the difference gets optimized away at runtime in LuaJIT (which is pretty impressive). Just goes to show how important benchmarking is.
Side note: Lua allows you to supply a table for __index, which will result in a lookup equivalent to your code:
setmetatable(foo, { __index = function(t, key) return libraries[key] end } )
So you could just write:
setmetatable(foo, { __index = libraries })
This also happens to be a lot faster.
Here is how I write my modules:
-- foo.lua
local MyLib = {}
function MyLib.foo()
...
end
return MyLib
-- bar.lua
local MyLib = require("foo.lua")
MyLib.foo()
Note that the return MyLib is not in a function. require captures this return value and uses it as the library. This way, there are no globals.

Recreating setfenv() in Lua 5.2

How can I recreate the functionality of setfenv in Lua 5.2? I'm having some trouble understanding exactly how you are supposed to use the new _ENV environment variable.
In Lua 5.1 you can use setfenv to sandbox any function quite easily.
--# Lua 5.1
print('_G', _G) -- address of _G
local foo = function()
print('env', _G) -- address of sandbox _G
bar = 1
end
-- create a simple sandbox
local env = { print = print }
env._G = env
-- set the environment and call the function
setfenv(foo, env)
foo()
-- we should have global in our environment table but not in _G
print(bar, env.bar)
Running this example shows an output:
_G table: 0x62d6b0
env table: 0x635d00
nil 1
I would like to recreate this simple example in Lua 5.2. Below is my attempt, but it does not work like the above example.
--# Lua 5.2
local function setfenv(f, env)
local _ENV = env or {} -- create the _ENV upvalue
return function(...)
print('upvalue', _ENV) -- address of _ENV upvalue
return f(...)
end
end
local foo = function()
print('_ENV', _ENV) -- address of function _ENV
bar = 1
end
-- create a simple sandbox
local env = { print = print }
env._G = env
-- set the environment and call the function
foo_env = setfenv(foo, env)
foo_env()
-- we should have global in our envoirnment table but not in _G
print(bar, env.bar)
Running this example shows the output:
upvalue table: 0x637e90
_ENV table: 0x6305f0
1 nil
I am aware of several other questions on this subject, but they mostly seem to be dealing with loading dynamic code (files or string) which work quite well using the new load function provided in Lua 5.2. Here I am specifically asking for a solution to run arbitrary functions in a sandbox. I would like to do this without using the debug library. According to the Lua documentation we should not have to rely on it.
You cannot change the environment of a function without using the debug library from Lua in Lua 5.2. Once a function has been created, that is the environment it has. The only way to modify this environment is by modifying its first upvalue, which requires the debug library.
The general idea with environments in Lua 5.2 is that the environment should be considered immutable outside of trickery (ie: the debug library). You create a function in an environment; once created there, that's the environment it has. Forever.
This is how environments were often used in Lua 5.1, but it was easy and sanctioned to modify the environment of anything with a casual function call. And if your Lua interpreter removed setfenv (to prevent users from breaking the sandbox), then the user code can't set the environment for their own functions internally. So the outside world gets a sandbox, but the inside world can't have a sandbox within the sandbox.
The Lua 5.2 mechanism makes it harder to modify the environment post function-creation, but it does allow you to set the environment during creation. Which lets you sandbox inside the sandbox.
So what you really want is to just rearrange your code like this:
local foo;
do
local _ENV = { print = print }
function foo()
print('env', _ENV)
bar = 1
end
end
foo is now sandboxed. And now, it's much harder for someone to break the sandbox.
As you can imagine, this has caused some contention among Lua developers.
It's a bit expensive, but if it's that important to you...
Why not use string.dump, and re-load the function into the right environment?
function setfenv(f, env)
return load(string.dump(f), nil, nil, env)
end
function foo()
herp(derp)
end
setfenv(foo, {herp = print, derp = "Hello, world!"})()
To recreate setfenv/getfenv in Lua 5.2 you can do the following:
if not setfenv then -- Lua 5.2
-- based on http://lua-users.org/lists/lua-l/2010-06/msg00314.html
-- this assumes f is a function
local function findenv(f)
local level = 1
repeat
local name, value = debug.getupvalue(f, level)
if name == '_ENV' then return level, value end
level = level + 1
until name == nil
return nil end
getfenv = function (f) return(select(2, findenv(f)) or _G) end
setfenv = function (f, t)
local level = findenv(f)
if level then debug.setupvalue(f, level, t) end
return f end
end
RPFeltz's answer (load(string.dump(f)...)) is a clever one and may work for you, but it doesn't deal with functions that have upvalues (other than _ENV).
There is also compat-env module that implements Lua 5.1 functions in Lua 5.2 and vice versa.
In Lua5.2 a sandboxeable function needs to specify that itself. One simple pattern you can use is have it receive _ENV as an argument
function(_ENV)
...
end
Or wrap it inside something that defines the env
local mk_func(_ENV)
return function()
...
end
end
local f = mk_func({print = print})
However, this explicit use of _ENV is less useful for sandboxing, since you can't always assume the other function will cooperate by having an _ENV variable. In that case, it depends on what you do. If you just want to load code from some other file then functions such as load and loadfile usually receive an optional environment parameter that you can use for sandboxing. Additionally, if the code you are trying to load is in string format you can use string manipulation to add _ENV variables yourself (say, by wrapping a function with an env parameter around it)
local code = 'return function(_ENV) return ' .. their_code .. 'end'
Finally, if you really need dynamic function environment manipulation, you can use the debug library to change the function's internal upvalue for _ENV. While using the debug library is not usually encouraged, I think it is acceptable if all the other alternatives didn't apply (I feel that in this case changing the function's environment is deep voodoo magic already so using the debug library is not much worse)

why can't this Lua function using a self ":" be marked "local" without getting "'(' expected near ':'"

Why can't this Lua function using a self ":" be marked "local" without getting:
'(' expected near ':'
That is, in the code below things work. But why can't I make the "scene:createScene" function local (as I get the above-mentioned error when trying).
I note the listener functions need to be made local else I have struck cross-scene issues at times in storyboard. These can be marked local and work fine.
SceneBase = {}
function SceneBase:new()
local scene = Storyboard.newScene()
local function returnButtonTouch_Listener (event)
-- code here
end
function scene:createScene( event ) -- WHY CAN'T THIS BE LOCAL???
-- code here
end
return scene
end
return SceneBase
That is why can't the function line read:
local function scene:createScene( event )
function scene:createScene(event) ... end
Is syntax sugar for:
scene.createScene = function(self, event) ... end
Which is syntax sugar for:
scene["createScene"] = function(self, event) ... end
You want to do:
local scene["createScene"] = function(self, event) ... end
Which makes no sense.
Another way of putting it: local is a qualifier for a variable which makes it local rather than global. What variable would you be qualifying with local function scene:createScene( event )?
createScene is not a variable, it's a key in the table scene.
Actually, that's a bit misleading. When you say:
foo = 10
Without qualification, foo becomes global, which is to say it's stored in the global state like this:
_G.foo = 10;
Which of course means the same as this:
_G["foo"] = 10;
When you use the keyword local, it doesn't get stored in a table, it ends up stored in a VM register, which is both faster and has more tightly constrained scope.
When you write either of these:
function foo.bar() end
function foo:bar() end
You are explicitly storing the function value in a table (foo). Those statements are exactly equivalent to these, respectively:
foo["bar"] = function() end
foo["bar"] = function(self) end
I note the listener functions need to be made local
What do you mean by that? In Lua, a function is a function is a function. It's just another value, like a string or number. Whether it's stored in a global, table, local, or not stored at all is irrelevant.
local foo = print
_G["zap"] = foo
doh = zap
t = { zip = doh }
t.zip("Hello, World") -- Hello, World
assert(print == foo
and zap == foo
and zap == doh
and t.zip == doh)
Here we pass the print function around. It's all the same function, and as long as we have a reference to it we can call it.
I don't know Corona, but I'm guessing that event handlers are not specified by naming convention of locals. You need to register it as an event handler. For instance, according to this video a Button object has an onEvent field which is set to the handler for that button.

Resources