Lua not overriding # metamethod - lua

Ok so I've been searching for a while and didn't get the answer. I imagine someone has this same problem but I was not able to solve this problem. I'm new to Lua, having some experience with Python but not being a programmer :S.
So I'm doing a metatable to handle complex numbers, following the tutorials here: http://www.dcc.ufrj.br/~fabiom/lua/
so I implement creation, addition, printing and equal comparison:
local mt={}
local function new(r,i)
return setmetatable({real = r or 0, im = i or 0},mt)
end
local function is_complex (v)
return getmetatable(v)==mt
end
local function add (c1,c2)
if not is_complex(c1) then
return new(c1+c2.real,c2.im)
end
if not is_complex(c2) then
return new(c1.real+c2,c1.im)
end
return new(c1.real + c2.real,c1.im + c2.im)
end
local function eq(c1,c2)
return (c1.real==c2.real) and (c1.im==c2.im)
end
local function modulus(c)
return (math.sqrt(c.real^2 + c.im^2))
end
local function tos(c)
return tostring(c.real).."+"..tostring(c.im).."i"
end
mt.new=new
mt.__add=add
mt.__tostring=tos
mt.__eq=eq
mt.__len=modulus
return mt
Then I make a small tests:
complex2=require "complex2"
print (complex2)
c1=complex2.new(3,2)
c2=complex2.new(3,4)
print (c1)
print (c2)
print(#{1,2})
print(#c2)
print(complex2.__len(c2))
print(#complex2.new(4,3))
and I get:
table: 0000000003EADBC0
3+2i
3+4i
2
0
5
0
So, What am I dong wrong? is something with calling the #, when i try to debug in the other cases the program goes to the module into the function but the # operand gets like ignored. Modulus function is working and can be called in the module... I'm sorry for such a long and I'm sure obvious question but I tried everything I could already. Thank you

May be the problem is about Lua version.
http://www.lua.org/manual/5.2/manual.html#2.4
"len": the # operation
works since Lua 5.2
and it works only with tables

So, Thank you, and you are right. I thought I had selected 5.2 but in the interpreter it was running 5.1 :S. Now i did:
complex2=require "complex2"
print (complex2)
c1=complex2.new(3,2)
c2=complex2.new(3,4)
print (c1)
print (c2)
print (c1+c2)
print(#{1,2})
print(#c2)
print(complex2.__len(c2))
print(complex2.__len(complex2.new(3,3)))
print(#complex2.new(4,3))
print(getmetatable(c2))
And i got:
table: 000000000047E1C0
3+2i
3+4i
6+6i
2
5
5
4.2426406871193
5
table: 000000000047E1C0
Everything under control ^^, at least the code was working as supposed xD

Related

How to get the value from SPOP safely and test for value or null

I am currently doing 2 steps in my code and I just realized I can combine both steps in a LUA script.
I am doing:
SPOP on my set
calling a lua script to do other things.
The value from step#1 is being passed and stored in the local variable ele.
My lua script looks like:
local ele = KEYS[1]
local p = KEYS[2]
local u = KEYS[3]
if redis.call("SISMEMBER", u, ele) == 0 then
..
..
return "OK"
else
return "EXISTS"
end
How can I call SPOP from inside my lua script and store it in a variable.
I need to do:
local popped = redis.call("SPOP", "my-set-here")
I'm not sure if that will work, but then I have to check if it is null or has a value I guess. Just want to make sure I am following best practise.
BTW, as a side note, what is the fastest way to create and test lua scripts?
You can check the value of popped for non-nillness with something like:
if popped then
-- do something
end
As for developing Redis Lua scripts, have a look at Zerobrane's integration:
http://notebook.kulchenko.com/zerobrane/redis-lua-debugging-with-zerobrane-studio
https://redislabs.com/blog/zerobrane-studio-plugin-for-redis-lua-scripts/
Disclosure: I was involved in the integration effort ;)

Metamethod lookup through __index?

I've implemented my own class system and I'm having trouble with __tostring; I suspect a similar issue can happen with other metamethods, but I haven't tried.
(Brief detour: each class has a __classDict attribute, holding all methods. It is used as the class instances' __index. At the same time, the __classDict's __index is the superclass' __classDict, so methods in superclasses are authomatically looked up.)
I wanted to have a "default tostring" behavior in all instances. But it didn't work: the "tostring" behavior doesn't "propagate" through subclasses correctly.
I've done this test exemplifying my issue:
mt1 = {__tostring=function(x) return x.name or "no name" end }
mt2 = {}
setmetatable(mt2, {__index=mt1})
x = {name='x'}
y = {name='y'}
setmetatable(x, mt1)
setmetatable(y, mt2)
print(x) -- prints "x"
print(mt2.__tostring(y)) -- prints "y"
print(y) -- prints "table: 0x9e84c18" !!
I'd rather have that last line print "y".
Lua's "to_String" behaviour must be using the equivalent of
rawget(instance.class.__classDict, '__tostring')
instead of doing the equivalent of
instance.class.__classDict.__tostring
I suspect the same happens with all metamethods; rawget-equivalent operations are used.
I guess one thing I could do is copying all the metamethods when I do my subclassing (the equivalent on the above example would be doing mt2.__tostring = mt1.__tostring) but that is kind of inelegant.
Has anyone fought with this kind of issue? What where your solutions?
I suspect the same happens with all metamethods; rawget-equivalent operations are used.
That is correct.
from the lua manual:
... should be read as rawget(getmetatable(obj) or {}, event). That is, the access to a metamethod does not invoke other metamethods, and the access to objects with no metatables does not fail (it simply results in nil).
Generally each class has its own metatable, and you copy all references to functions into it.
That is, do mt2.__tostring = mt1.__tosting
Thanks to daurnimator's comments, I think I found a way to make metamethods "follow" __index as I want them to. It's condensed on this function:
local metamethods = {
'__add', '__sub', '__mul', '__div', '__mod', '__pow', '__unm', '__concat',
'__len', '__eq', '__lt', '__le', '__call', '__gc', '__tostring', '__newindex'
}
function setindirectmetatable(t, mt)
for _,m in ipairs(metamethods) do
rawset(mt, m, rawget(mt,m) or function(...)
local supermt = getmetatable(mt) or {}
local index = supermt.__index
if(type(index)=='function') then return index(t,m)(...) end
if(type(index)=='table') then return index[m](...) end
return nil
end)
end
return setmetatable(t, mt)
end
I hope it is straightforward enough. When a new metatable is set, it initializes it with all metamethods (without replacing existing ones). These metamethods are prepared to "pass on" requests to "parent metatables".
This is the simplest solution I could find. Well, I actually found a solution that used less characters and was a bit faster, but it involved black magic (it involved metatable functions de-referencing themselves inside their own bodies) and it was much less readable than this one.
If anyone finds a shorter, simpler function that does the same, I'll gladly give him the answer.
Usage is simple: replace setmetatable by setindirectmetatable when you want it to "go up":
mt1 = {__tostring=function(x) return x.name or "no name" end }
mt2 = {}
setmetatable(mt2, {__index=mt1})
x = {name='x'}
y = {name='y'}
setmetatable(x, mt1)
setindirectmetatable(y, mt2) -- only change in code
print(x) -- prints "x"
print(mt2.__tostring(y)) -- prints "y"
print(y) -- prints "y"
A little word of warning: setindirectmetatable creates metamethods on mt2. Changing that behavior so a copy is made, and mt2 remains unaltered, should be trivial. But letting them set up by default is actually better for my purposes.
From my experience with Lua 5.1, metamethods are looked up in metatables using rawget(), and that's why you must copy the reference to the function into every class table you create.
See the Inheritance Tutorial on the Lua Users Wiki.

Lua metamethods not being called

I'm kinda new to Lua (not really done much with it yet) and I'm trying to wrap my mind around metatables. I have had them working before but now (after a number of months) I have encountered something really weird.
What should this script print when run?
__mt = {}
__mt.__index = function(table, key)
print("In __index")
return 99
end
test = {}
test.x = 5
setmetatable(test, __mt)
print(test.x)
Personally, I would expect it to print "In __index" (from the metamethod) followed by 99. However, whenever I run it I get 5. Nothing I do can get the index metamethod to run. It just acts like I'm using rawget() instead.
Curiously, adding
print(getmetatable(test).__index(test, "x"))
will do the right thing. The metatable is there, __index() is correct, it just isn't being called.
Is this a bug or am I just doing something stupid? I can't tell.
The metamethod (in old terminology also called fallback) called __index is only called if the key x does not exist in the table, when you access t.x. Try print(t.y) instead!
Added: Yes, using a proxy table.
function doubletable(T)
local store = T or {}
local mt = {}
mt.__index = function (t, k) return store[k] and 2*store[k] end
mt.__newindex = store
return setmetatable({}, mt)
end
t = doubletable({a=1, b=3})
t.c = 7
print(t.a, t.b, t.c)
-- output: 2 6 14

lua - get the list of parameter names of a function, from outside the function

I'm generating some (non-html) documentation for a Lua library that I developed. I will generate the documentation by hand, but I'd appreciate some kind of automation if possible (i.e. generating skeletons for each function so I can fill them in)
I'd like to know if there's a way for lua to know the names of the parameters that a function takes, from outside it.
For example, is there a way to do this in Lua?
function foo(x,y)
... -- any code here
end
print( something ... foo ... something)
-- expected output: "x", "y"
Thanks a lot.
ok,here is the core code:
function getArgs(fun)
local args = {}
local hook = debug.gethook()
local argHook = function( ... )
local info = debug.getinfo(3)
if 'pcall' ~= info.name then return end
for i = 1, math.huge do
local name, value = debug.getlocal(2, i)
if '(*temporary)' == name then
debug.sethook(hook)
error('')
return
end
table.insert(args,name)
end
end
debug.sethook(argHook, "c")
pcall(fun)
return args
end
and you can use like this:
print(getArgs(fun))
Try my bytecode inspector library. In Lua 5.2 you'll be able to use debug.getlocal.
Take a look at debug.getinfo, but you probably need a parser for this task. I don't know of any way to fetch the parameters of a function from within Lua without actually running the function and inspecting its environment table (see debug.debug and debug.getlocal).
function GetArgs(func)
local args = {}
for i = 1, debug.getinfo(func).nparams, 1 do
table.insert(args, debug.getlocal(func, i));
end
return args;
end
function a(bc, de, fg)
end
for k, v in pairs(GetArgs(a)) do
print(k, v)
end
will print
1 bc
2 de
3 fg
Basically we use debug.getinfo to retrieve the nparams attribute (which gives us the information of how many parameters the function takes) and debug.getlocal to access the name of the parameters.
Tested and working with Lua 5.4
Take a look at the luadoc utility. It is sort of like Doxygen, but for Lua. It is intended to allow the documentation to be written in-line with the source code, but it could certainly be used to produce a template of the documentation structure to be fleshed out separately. Of course, the template mechanism will leave you with a maintenance issue down the road...

What's with PCALL or is Wowwiki wrong?

This is a WoW (World of Warcraft) lua script question. Not many of these get asked here but I have no where to turn and Stackoverflow is the programmer oasis for answers.
Question:
Wowwiki states that the 2nd, 3rd, 4th arguments are your calling functions 1st, 2nd, 3rd arguments. I don't find this to be true. I find that the 3rd, 4th, 5th arguments end up being the 1st, 2nd, 3rd arguments.
Link: http://www.wowwiki.com/API_pcall
Function:
function myTest(arg1)
return arg1 .. 10;
end
Problem:
local retOK, ret1 = pcall(myTest,"string value");
when I try the sample I get an error of "trying to perform concatenate on local 'arg1' (a nil value)". If I change the code to:
local retOK, ret1 = pcall(myTest,"string value", "bob");
then I get the output of "bob10". Where does the 2nd argument go and what is it for?
More Testing:
function BobsToolbox:RunTest()
local test1, value1 = pcall(BobsToolbox.Test1, "string value");
SharpDeck:Print("Test1: " .. tostring(test1) .. " Value: " .. tostring(value1));
end
function BobsToolbox:Test1(arg1)
return arg1 .. "10";
end
Results: attempt to concatenate local 'arg1' (a nil value)
function BobsToolbox:RunTest()
local test1, value1 = pcall(Test1, "string value");
SharpDeck:Print("Test1: " .. tostring(test1) .. " Value: " .. tostring(value1));
end
function Test1(arg1)
return arg1 .. "10";
end
Results: string value10
I am new to lua and I can't understand why these are different.
New Question:
The following code works but why?
function BobsToolbox:RunTest()
local test1, value1 = pcall(BobsToolbox.Test1, "string value");
SharpDeck:Print("Test1: " .. tostring(test1) .. " Value: " .. tostring(value1));
end
function BobsToolbox.Test1(arg1)
return arg1 .. "10";
end
What's the difference between the following: ("." vs ":")
function BobsToolbox.Test1(arg1)
function BobsToolbox:Test1(arg1)
Lua Documentation:
http://www.lua.org/pil/16.html
This use of a self parameter is a central point in any object-oriented language. Most OO languages have this mechanism partially hidden from the programmer, so that she does not have to declare this parameter (although she still can use the word self or this inside a method). Lua can also hide this parameter, using the colon operator. We can rewrite the previous method definition as
function Account:withdraw (v)
self.balance = self.balance - v
end
and the method call as
a:withdraw(100.00)
The effect of the colon is to add an extra hidden parameter in a method definition and to add an extra argument in a method call. The colon is only a syntactic facility, although a convenient one; there is nothing really new here. We can define a function with the dot syntax and call it with the colon syntax, or vice-versa, as long as we handle the extra parameter correctly:
Account = { balance=0,
withdraw = function (self, v)
self.balance = self.balance - v
end
}
function Account:deposit (v)
self.balance = self.balance + v
end
Account.deposit(Account, 200.00)
Account:withdraw(100.00)
Possible Conclusion:
With this in mind I assume that when calling a ":" function using "pcall" you must supply the "self" argument.
Related: There are nice live code editors for WoW. I used to use LuaSlinger, but turns out that's no longer developed and the developer recommends Hack instead.
However, what you might be encountering here is that the colon method-call syntax is just syntax sugar, ditto for method definitions, IIRC. Basically, if you do foo:bar("quux!"), where foo is an object, you are in reality just doing foo.bar(foo, "quux!").
Hope that helps!
Well, I don't think WoWWiki is wrong. Here is the code I am using:
function myTest(arg1) return arg1 .. 10; end
local retOK, ret1 = pcall(myTest,"string value");
DEFAULT_CHAT_FRAME:AddMessage(ret1);
local retOK, ret1 = pcall(myTest,"string value", "bob");
DEFAULT_CHAT_FRAME:AddMessage(ret1);
Here is the output I get in my General chat box:
string value10
string value10
How are you trying your sample code? I just pasted my code into an existing mod lua file and made sure that mod was enabled in the addons window before selecting my character and logging in. I made a few changes to the source lua file and typed:
/console reloadui
To try the new changes and have the results output to my screen. I don't have much advice to offer you, because I haven't done much work with WoW addons. Have you tried this code in a blank addon to make sure nothing else is interfering? Have you actually tried the code in game? If you can provide any more information or want me to try anything else, let me know!
Update: Decided to try a few more tests. Here are the tests (with the same function):
local retOK, ret1 = pcall(myTest,"");
DEFAULT_CHAT_FRAME:AddMessage(ret1);
local retOK, ret1 = pcall(myTest, nil, "bob");
DEFAULT_CHAT_FRAME:AddMessage(ret1);
And the results:
10
attempt to concatenate local 'arg1' (a nil value)
It's interesting that the error I see when arg1 is nil is slightly different than the error you see. I'd be interested in knowing how you are testing your code. Or maybe you didn't copy the error down verbatim? I guess you could also try clearing out your WTF folder and disabling the rest of your addons to test this function. If it makes a difference, then you can enable them one a time until you find the problem.

Resources