Im trying to save myself some headache in a project im working on by splitting it up in files but I cannot seem to be able to cross call functions. (inside file_a, call a function in file_b which calls a function in file_a)
Is this just not possible or am i doing it wrong?
file_a:
local imported = require("fileb")
m = imported:new(BC)
m.print_message("hi")
file_b:
class_b = {
BC = {}
}
function class_b:print_message(message)
print(message)
end
function class_b:new (BC)
local AB = {}
AB = BC or {}
AB._index = class_b
setmetatable(AB, self)
return AB
end
return class_b
When i run this i get: attempt to call nil value (field 'print_message')
My project is much larger and complex at the moment but i extracted the logic i am working with right now to show what im trying to do to be able to cross call the functions.
You're calling m["print_message"] which is a nil value. Calling nil values is not allowed as it doesn't make any sense to call a nil value.
If you index a field that does not exist in a table, Lua will check wether the table has a metatable and if that metatable has a field __index.
While that field exists in m it does not exist in its metatable class_b.
class_b.__index must refer to class_b to make this work.
Related
I want to be able to run a lambda and get at its argument values (the values of a and b below).
I can achieve this by explicitly having the lambda return a binding, and then getting the values out of this binding:
fun = lambda { |a, b = Time.now| binding }
fun_binding = fun.call(123)
puts "A is #{fun_binding.local_variable_get(:a)} and B is #{fun_binding.local_variable_get(:b)}"
# Outputs e.g.: A is 123 and B is 2022-04-29 20:14:07 +0200
Is it possible to get these values without running any code inside the lambda body? I.e. could I do
fun = lambda { |a, b = Time.now| }
fun.call(123)
and still get the a and b values somehow from outside the lambda?
(To provide context – I'm asking because I've experimented with this as a shortcut syntax for instantiating objects with ivars/readers automatically assigned. At this stage I'm more curious about what's possible than what is advisable – so I invite any solutions, advisable or not…)
I'm not sure how advisable this is, but you can use the TracePoint class with the :b_call event to grab the lambda's binding:
fun = lambda { |a, b = Time.now| }
fun_binding = nil
TracePoint.trace(:b_call) do |tp|
fun_binding = tp.binding
tp.disable
end
fun.call(123)
puts "A is #{fun_binding.local_variable_get(:a)} and B is #{fun_binding.local_variable_get(:b)}"
This is not a perfect solution and will fail if one of the argument default values enters a block on it's own (since the default values are evaluated before the :b_call event is fired for the lambda), but that could be fixed with some additional filtering inside the TracePoint handler.
I have a list with special users and normal users. Special users have their own special func, while normal users use a standard func.
I came up with this code design, but I feel that this is not optimal (performance wise).
So my question is: How would I get the best performance when calling inner functions like the example below?
if something then
CallFunc(var)
end
Special/normal user logic
function CallFunc(var)
if table[name] then
table[name](var)
else
Standard_Func(var)
end
end
local table = {
["name1"] = function(var) Spec_Func1(var) end,
["name2"] = function(var) Spec_Func2(var) end,
["name3"] = function(var) Spec_Func3(var) end,
...
--40 more different names and different funcs
}
Special user funcs
function Spec_Func1(var)
--lots of code
end
function Spec_Func2(var)
--lots of code
end
...
--more funcs
EDIT:
see #hjpotter92's answer:
I cant find the user in the table.
local function_lookups = {
name1 = Spec_Func1, --this doesnt let me find the user
--name1 = 1 --this does let me find the user (test)
}
if function_lookups[name] then --this fails to find the user
--do something
end
You do not need another anonymous function. Simply use the lookup table as follows:
local function_lookups = {
name1 = Spec_Func1,
name2 = Spec_Func2,
name3 = Spec_Func3,
...
--40 more different names and different funcs
}
Do not use the variable name table. It is a library available in Lua itself, and you are overwriting it.
You don't need a special function at all! You can use a generic function that's behaviour depends on the caller! Lemme explain with a piece of code:
local Special = {Peter=function(caller)end} --Put the special users' functions in here
function Action(caller)
if Special[caller] then
Special[caller](caller)
else
print("Normal Action!")
end
end
So whenever a user does a certain action, you can fire this function and pass a caller argument, the function then does the work behind the scenes determining if the caller is special, and if so what to do.
This makes your code clean. It also makes it easier to add more than 2 user statuses!
Sample code : https://play.golang.org/p/vl3xwMWf5G
In the above code, I am making sure that, we don't unnecessarily call the LocateBasket function. It is called only once during the GetBasketCall. But if there is any attribute change (For eg. The quantity was changed to 30) then I wanted to make sure that when user calls GetBasket it internally calls LocateBasket function too.
In my example I had only one function, but if there is multiple attributes which needs to be re-calculated (based on attribute change) when their corresponding Getter Functions are getting called, what is the best approach to do the same.
Write a setter method, and either immediately LocateBasket or flag it for GetBasket to do:
func (ball *Ball) SetQuantity(n int){
ball.Quantity = n
// ball.LocateBasket(ball)
ball.dirty = true
}
func (ball *Ball) GetBasket() BasketType {
if ball.dirty{
ball.LocateBasket(ball)
ball.dirty = false
return ball.basket
}
For a field inside a deeply nested table, for example, text.title.1.font. Even if you use
if text.title.1.font then ... end
it would result in an error like "attempt to index global 'text' (a nil value)" if any level of the table does not actually exists. Of course one may tried to check for the existence of each level of the table, but it seems rather cumbersome. I am wondering is there a safe and prettier way to handle this, such that when referencing such an object, nil would be the value instead of triggering an error?
The way to do this that doesn't invite lots of bugs is to explicitly tell Lua which fields of which tables should be tables by default. You can do this with metatables. The following is an example, but it should really be customized according to how you want your tables to be structured.
-- This metatable is intended to catch bugs by keeping default tables empty.
local default_mt = {
__newindex =
function()
error(
'This is a default table. You have to make nested tables the old-fashioned way.')
end
}
local number_mt = {
__index =
function(self, key)
if type(key) == 'number' then
return setmetatable({}, default_mt)
end
end
}
local default_number_mt = {
__index = number_mt.__index,
__newindex = default_mt.__newindex
}
local title_mt = {__index = {title = setmetatable({}, default_number_mt)}}
local text = setmetatable({}, title_mt)
print(text.title[1].font)
Egor's suggestion debug.setmetatable(nil, {__index = function()end}) is the easiest to apply. Keep in mind that it's not lexically scoped, so, once it's on, it will be "on" until turned off, which may have unintended consequences in other parts of your code. See this thread for the discussion and some alternatives.
Also note that text.title.1.font should probably be text.title[1].font or text.title['1'].font (and these two are not the same).
Another, a bit more verbose, but still usable alternative is:
if (((text or {}).title or {})[1] or {}).font then ... end
In Lua I've created a pretty printer for my tables/objects. However, when a function is displayed, it's shown as a pointer.
I've been reading up on Lua Introspection but when I introspect the function with debug.getinfo() it won't return the function's name. This seems to be due to a scoping issue but I'm not sure how to get around it.
What is the best way to get a function's name using its pointer? (I understand functions are first class citizens in Lua and that they can be created anonymously, that's fine)
when you create the functions, register them in a table, using them as keys.
local tableNames = {}
function registerFunction(f, name)
tableNames[f] = name
end
function getFunctionName(f)
return tableNames[f]
end
...
function foo()
..
end
registerFunction(foo, "foo")
...
getFunctionName(foo) -- will return "foo"
For some reason, it seems to work only with number parameters (that is, active functions on the stack).
The script
function a() return debug.getinfo(1,'n') end
function prettyinfo(info)
for k,v in pairs(info) do print(k,v) end
end
prettyinfo(a())
prints
name a
namewhat global
but if I change the last line to
prettyinfo(debug.getinfo(a, 'n'))
it gives me only an empty string:
namewhat