I created a metatable where __add and __sub take a table and an number. How does Lua determine which to use? Consider the two situations below with table T (with metatable described)
local n=-10
local V=T+n
and
local n=-10
local V=T-n
Which gets called?
I have experimented with various combinations but fail to see a pattern.
Created a metatable where __add and __sub take a table and an number.
__add and __sub take two operands. One of them will necessarily be a table or userdata which has your metatable, the other can be anything at all. Lua doesn't care about the type of the other operand.
If either operand has a metatable with a handler for the particular operator (+ -> __add, - => __sub), that handler will be called.
In your example, Lua not only doesn't care if n is positive or negative, it doesn't care if it's a number. The - in -n has nothing to do with the __sub metamethod -- that's the unary minus operator, whereas __sub handles the binary minus operator.
How does Lua determine which to use?
The + operator is an "__add" event. When you sayd op1 + op2, Lua checks op1 for a metatable with an __add handler. If it finds one, it calls it. Otherwise it checks op2.
It is the sign of the two operands operator in the source code which is determinant.
It does not care whether the operand value are negative or positive numbers. It does not even care whether your n is actually a number or a value of another type.
local n=-10
local V=T+n -- __add called
local n=-10
local V=T-n -- __sub called
Related
Lua will write the code of a function out as bytes using string.dump, but warns that this does not work if there are any upvalues. Various snippets online describe hacking around this with debug. It looks like 'closed over variables' are called 'upvalues', which seems clear enough. Code is not data etc.
I'd like to serialise functions and don't need them to have any upvalues. The serialised function can take a table as an argument that gets serialised separately.
How do I detect attempts to pass closures to string.dump, before calling the broken result later?
Current thought is debug.getupvalue at index 1 and treat nil as meaning function, as opposed to closure, but I'd rather not call into the debug interface if there's an alternative.
Thanks!
Even with debug library it's very difficult to say whether a function has a non-trivial upvalue.
"Non-trivial" means "upvalue except _ENV".
When debug info is stripped from your program, all upvalues look almost the same :-)
local function test()
local function f1()
-- usual function without upvalues (except _ENV for accessing globals)
print("Hello")
end
local upv = {}
local function f2()
-- this function does have an upvalue
upv[#upv+1] = ""
end
-- display first upvalues
print(debug.getupvalue (f1, 1))
print(debug.getupvalue (f2, 1))
end
test()
local test_stripped = load(string.dump(test, true))
test_stripped()
Output:
_ENV table: 00000242bf521a80 -- test f1
upv table: 00000242bf529490 -- test f2
(no name) table: 00000242bf521a80 -- test_stripped f1
(no name) table: 00000242bf528e90 -- test_stripped f2
The first two lines of the output are printed by test(), the last two lines by test_stripped().
As you see, inside test_stripped functions f1 and f2 are almost undistinguishable.
I was doing some anti metamethod hooks and I was curious on what metamethod is called in the code below between the parentheses
local test = "random string"
if (test == "random string") then --// What metamethod if any is being called here?
print("equals")
end
I've done some research and took a look at the __eq metamethod, but that is only called when comparing two tables which isn't what I'm tryna do.
If there isn't any metamethod being called then how would I protect the if condition?
-- Update --
What if I put every string inside of a table for example:
local _Table1 = {"Test1", "Test2"}
local _Table2 = {"Test1", "Test2"}
for Index, Value in next, _Table1 do
if Value == _Table2[Index] then
print("Tables Match!")
elseif Value ~= _Table2[Index]
print("Tables Don't Match!")
end
end
I'm not doing any string converting here, but I'm showing what I could try and do for a simple anti tamper.
The only operator in the parenthesized expression is ==. Thus the only metamethod in question is __eq. The Lua Reference Manual states the following on __eq:
__eq: the equal (==) operation. Behavior similar to the addition operation, except that Lua will try a metamethod only when the values being compared are either both tables or both full userdata and they are not primitively equal.
Your values are strings and thus no metamethod will be called - even if you were to modify the string metatable to alter __eq.
Strings in Lua are always interned, so this comparison will always run in constant time. Since it is a primitive comparison, it can't possibly throw an error. No metamethod is called.
There is nothing to protect from: No possible performance issue / DoS vulnerability, no possible fancy side effects or code execution, no possible error.
(Highly theoretically: If a debug hook running every n instructions is registered, it might fire as the comparison is executed. You can hardly "protect" against a debug hook though.)
But it makes sense, for example a check against the Length.
A check against the Content seems also possible but need more lines.
And i like one liner in Lua Standalone to show...
> _VERSION
Lua 5.4
> eqtab = setmetatable({"Test1", "Test2"}, {__eq = function(left, right) print('SIMON SAYS:') return(#left == #right) end})
> eqtab == {1}
SIMON SAYS:
false
> eqtab == {}
SIMON SAYS:
false
> eqtab == {"Test1", "Test2"}
SIMON SAYS:
true
> eqtab == {1, 2}
SIMON SAYS:
true
From the Lua 5.3 doc:
__index: The indexing access table[key]. ... The metamethod is looked up in table.
It says the same thing for __newindex, but not for any other metamethod.
If this were true (which it's not), it would be a major departure from previous versions of Lua. The following code outputs nil, as I would expect, but it's inconsistent with the doc.
#!/usr/bin/env lua5.3
local proto = {a = 54}
local t0 = {__index = proto}
print(t0.a)
To be clear: If the doc was correct, I would expect t0 in the above code to only require an __index field without an actual metatable for t0.a to be 54. So does anyone know what's going on with the doc?
You are incorrectly interpreting the meaning of the term. To say that it is "metamethod Y is looked up in X" does not mean that it searches the X table for an entry named Y. It means that it gets the metatable for X and looks up an entry named Y, as if by rawget(getmetatable(X) or {}, "Y"), as specified in the docs.
This terminology is repeatedly used in the metamethod descriptions. For example:
First, Lua will check the first operand (even if it is valid). If that operand does not define a metamethod for __add, then Lua will check the second operand.
It's not asking if the first (or second) operand have a method __add; it asks if they have a metamethod __add.
As you can see from the __add example, you have to specify which of the operands it tries to get metamethods from, and in which order. For table[key], the point of the text is that doesn't try to get the metamethod from key, only from table. That may seem a tad bit obvious, but completeness is better than incompleteness.
What is the difference between tables and metatables in Corona? What are the types of metatables? How and where can I use them? What is the main purpose of using tables and metatables?
Tables in Lua are the main data type you can use to create dynamic, structured data. Other languages have arrays, lists, dictionaries (key-value storage), in Lua you only have tables. The only operations you can do with a basic table is indexing and storing a value using the tab[key] syntax, i.e.:
local tab = {}
tab['key1'] = 'Hello' -- storing a value using a string key
tab.key2 = 'World' -- this is syntax sugar, equivalent to previous
print(tab.key1, tab['key2']) -- indexing, the syntax is interchangable
You cannot do anything else with basic tables, for example adding them:
local v1={x=0,y=0}
local v2={x=1,y=1}
print(v1+v2)
--> stdin:1: attempt to perform arithmetic on local 'v1' (a table value)
A metatable allows you to modify the behavior of tables, to specify what should be done when tables are added, multiplied, concatenated (..), etc. A metatable is just a table, which contains functions with special keys, also called metamethods. You can assign a metatable to a table using setmetatable(). For example:
local Vector = {} -- this will be the metatable for vectors
function Vector.__add(v1, v2) -- what to do when vectors are added
-- create a new table and assign it a Vector metatable
return setmetatable({x=v1.x+v2.x, y=v1.y+v2.y}, Vector)
end
function Vector.__tostring(v) -- how a vector should be displayed
-- this is used by tostring() and print()
return '{x=' .. v.x .. ',y=' .. v.y .. '}'
end
local v1 = setmetatable({x=1, y=2}, Vector)
local v2 = setmetatable({x=3, y=4}, Vector)
-- vectors are added and the resulting vector is printed
print(v1 + v2) --> {x=4,y=6}
If you want to understand metatables better, you should definitely read the Programming in Lua chapter on metatables.
Lua (which is the language that Corona is based on) uses metatables for different purposes.
The relevant entry in the manual is Section 2.8.
A nice tutorial can be found here or here.
A metatable is just a table like any other, but is set as metatable on another table (which I'll call a base table further on, to make a difference between the 2 tables).
A metatable can contain anything, but the special keys (starting with a double underscore) are the interesting ones. The values set to this keys in this table will be called on special occasions. Which occasion depends on which key. The most interesting are:
__index: Will be used whenever a key in the base table is looked up, but does not exist. This can either contain table, in which the key will be looked up instead, or a function, which will be passed the original table and the key. This can be used for implementing methods on tables (OOP style), for redirection, fall through cases, setting defaults, etc etc
__newindex: Will be used whenever a new key is to be assigned in a table (which was previously nil). If it's a table, the key will be assigned in that table. If it's a function, that function will be passed the original table, key and value. This can be used for controlling access to a table, preprocessing data, redirection of assignments.
__call: enables you to set a function to be called if you use eg. table().
__add,__sub,__mul,__div,__mod are used to implement binary operations,
__unm is used to implement unary operations,
__concat is used for implementing concatenation (the .. operator)
__len is used for implementing the length operator (#)
__eq,__lt,__le are used for implementing comparisons
A small thing to know when using __index & co.: in those methods, you should use rawget and rawset in order to prevent calling the metamethod each time again, causing a loop.
As a small example:
t={1,2,3} -- basetable
mt={} -- metatable
mt.__index=function(t,k)
print("__index event from "..tostring(t).." key "..k)
return "currently unavailable"
end
mt.__newindex=function(t,k,v)
print("__newindex event from "..tostring(t).." key: "..k.." value: "..v)
if type(k)=="string" then
rawset(t,k,v:reverse())
else
rawset(t,k,v)
end
end
mt.__call=function(t,...)
print("call to table "..tostring(t).." with arguments: ".. table.concat({...},','))
print("All elements of the table:")
for k,v in pairs(t) do print(k,v) end
end
setmetatable(t,mt)
t[4]="foo" -- this will run the __newindex method
print(t[5]) -- this will run the __index method
t("foo","bar")
-- Multiple fall through example:
t={}
mt={}
mt2={}
setmetatable(t,mt) -- metatable on base table
setmetatable(mt,mt2) -- second layer of metatable
mt.__index=function(t,k) print('key '..k..' not found in '..namelookup[t]) return getmetatable(t)[k] end -- tries looking nonexistant indexes up in mt.
mt2.__index=mt.__index -- function was written portably, reuse it.
t[1]='A'
mt[2]='B'
mt2[3]='C'
namelookup={[t]="t",[mt]="mt",[mt2]="mt2"}
print(t[1],t[2],t[3],t[4])
Now these are but silly examples, you can do much more complex stuff. Take a look at the examples, take a look at the relevant chapters in Programming in Lua, and experiment. And try not to get confused ;)
In Lua, there seem to be two ways of appending an element to an array:
table.insert(t, i)
and
t[#t+1] = i
Which should I use, and why?
Which to use is a matter of preference and circumstance: as the # length operator was introduced in version 5.1, t[#t+1] = i will not work in Lua 5.0, whereas table.insert has been present since 5.0 and will work in both. On the other hand, t[#t+1] = i uses exclusively language-level operators, wheras table.insert involves a function (which has a slight amount of overhead to look up and call and depends on the table module in the environment).
In the second edition of Programming in Lua (an update of the Lua 5.0-oriented first edition), Roberto Ierusalimschy (the designer of Lua) states that he prefers t[#t+1] = i, as it's more visible.
Also, depending on your use case, the answer may be "neither". See the manual entry on the behavior of the length operator:
If the array has "holes" (that is, nil values between other non-nil values), then #t can be any of the indices that directly precedes a nil value (that is, it may consider any such nil value as the end of the array).
As such, if you're dealing with an array with holes, using either one (table.insert uses the length operator) may "append" your value to a lower index in the array than you want. How you define the size of your array in this scenario is up to you, and, again, depends on preference and circumstance: you can use table.maxn (disappearing in 5.2 but trivial to write), you can keep an n field in the table and update it when necessary, you can wrap the table in a metatable, or you could use another solution that better fits your situation (in a loop, a local tsize in the scope immediately outside the loop will often suffice).
The following is slightly on the amusing side but possibly with a grain of aesthetics. Even though there are obvious reasons that mytable:operation() is not supplied like mystring:operation(), one can easily roll one's own variant, and get a third notation if desired.
Table = {}
Table.__index = table
function Table.new()
local t = {}
setmetatable(t, Table)
return t
end
mytable = Table.new()
mytable:insert('Hello')
mytable:insert('World')
for _, s in ipairs(mytable) do
print(s)
end
insert can insert arbitrarily (as its name states), it only defaults to #t + 1, where as t[#t + 1] = i will always append to the (end of the) table. see section 5.5 in the lua manual.
'#' operator only use indexed key table.
t = {1, 2 ,3 ,4, 5, x=1, y=2}
at above code
print(#t) --> print 5 not 7
'#' operator whenever not using.
If you want to '#' operator, then check it to table elements type.
Insert function can using any type use.But element count to work slow than '#'