I've read about metatables (here and here) and I asked myself if I could create a function call proxy by just adding a function with the same name to the __index table, so that it calls the index function (mine), and then I can call the normal one.
This is what I've tried:
local test = {}
test.static = function () print("static") end -- The normal function
local old = getmetatable(test) or {} -- Get the orginal meta
old.__index = {
static = function () print("hook") end -- Adding my function
}
test.static() -- call just for test ( prints -> static )
test = setmetatable( test , old ) -- setting the metatable
test.static() -- call just for test ( prints -> static, but should be hook )
Try giving the official source a read, specifically §2.4 – Metatables and Metamethods, and the description of the __index methamethod, which reads:
__index: The indexing access table[key]. This event happens when table is not a table or when key is not present in table. The metamethod is looked up in table.
Despite the name, the metamethod for this event can be either a function or a table. If it is a function, it is called with table and key as arguments, and the result of the call (adjusted to one value) is the result of the operation. If it is a table, the final result is the result of indexing this table with key. (This indexing is regular, not raw, and therefore can trigger another metamethod.)
We can see that your thinking is backwards. Properties on a table referenced by the __index metamethod are only looked up if the original table does not contain the key.
If you want to 'hook' a function you'll need to overwrite it, possibly saving the original to restore it later. If you want to tack functionally on to an existing function, you can write a neat little hooking function, which simply creates a closure around the functions, calling them in turn.
local function hook (original_fn, new_fn)
return function (...)
original_fn(...)
new_fn(...)
end
end
local test = {}
test.foo = function () print('hello') end
test.foo = hook(test.foo, function () print('world!') end)
test.foo() --> prints 'hello' then 'world!'
Alternatively, you can switch between metatables, assuming the original table never overrides the keys of interest, to get different results:
local my_table, default, extra = {}, {}, {}
function default.foo () print('hello') end
function extra.foo() print('world!') end
local function set_mt (t, mt)
mt.__index = mt
return setmetatable(t, mt)
end
set_mt(my_table, default)
my_table.foo() --> prints 'hello'
set_mt(my_table, extra)
my_table.foo() --> prints 'world!'
Related
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.
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
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.
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.
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.