Meta metatables? - lua

I'm trying to make an __index function in my table which can process ALL of the field it receives.. What I want to do is that if I call the table in the following way
mytable.str1.str2.str3
I should be able to return the table
{"str1", "str2", "str3"}
Note that str1,str2,str3 are undefined, they are just strings. I am not trying to create subtables str1, str2, I just want __index to see everything beyond the first period.
Unfortunately what I have seems that __index only captures str1, and complains that "attempt to index field 'str1' (a nil value)"
Anyone know how this can be done?

I'm not sure why you'd want to do this, but here's how you do it. The comments explain the trick, but basically you need a second metatable to handle the table that's returned from the first call to the __index metamethod.
If this isn't clear, let me know and I can explain in more detail.
-- This metatable appends the new key to itself, then returns itself
stringtablemeta = {}
function stringtablemeta.__index(self, key)
table.insert(self, key)
return self
end
-- In response to the question in the comments:
function stringtablemeta.__tostring(self)
local str = ""
for i, v in ipairs(self) do
if i > 1 then str = str .. "-" end
str = str .. v
end
return str
end
-- This metatable creates a new table, with stringmetatable as its metatable
mytablemeta = {}
function mytablemeta.__index(self, key)
local temp = { key }
setmetatable(temp, stringtablemeta)
return temp
end
-- set mytable to have mymetatable as it's metatable. This makes it so when
-- you index into it, it will call the mytablemeta.__index method.
--
-- That will return a talb with a single string, the key that was passed
-- in. that table will have it's own metatable, the stringmetatable, which
-- will cause it to append keys that are called on it with its own __index
-- metamethod
mytable = {}
setmetatable(mytable, mytablemeta)
test = mytable.str1.str2.str3
for k, v in pairs(test) do
print(k, v)
end

It can't. Not without having a metatable on each of those tables.
mytable is a table. str1 is a different table. So you can do the same thing by doing this:
local temp = mytable.str1
temp.str2.str3
And as far as Lua is concerned, these are equivalent. Therefore, the only way to know what was done at each stage is to give all of them a special metatable. How you concatenate the different values into a table is something you'll have to investigate on your own.

As Nicol said, you cannot do that directly in Lua. However, by returning specially crafted tables, you can achieve a similar result to what you want. Take a look at AutomagicTables at the Lua-users Wiki for inspiration.

Related

Lua overloads the operator to create a fake array

I want to create a 0-memory lua array that actually jumps to my custom function when I use operators like # [] on it
Any ideas on how to do this?
I want the user using this fake array to not perceive it as fake, it is worse than a normal array in terms of access speed, but has better memory performance
Lua implements something called metamethods (documentation)
Metamethods are functions which exist subsequently to a table and fire on certain operations such as indexing the array, reading missing indices, gathering the length of the array, or even math operations such as + - * /
-- Start by creating your array, and another for demonstration purposes
local object = {}
local demo = {1, 2, 3, 4}
-- Create a your metamethods contained in a table
local metamethods = {
__index = function(self, index) return demo[index] end;
__newindex = function(self, index, value) demo[index] = value end;
}
-- Lets print out what is in the object table for demonstration
print(object[1]) -- nil
print(object[2]) -- nil
print(object[3]) -- nil
print(object[4]) -- nil
-- Use the setmetatable(table a, table b) function to set the metamethods
-- stored in 'b' to 'a'
setmetatable(object, metamethods);
-- Lets print out what is in the object table for demonstration
print(object[1]) -- 1
print(object[2]) -- 2
print(object[3]) -- 3
print(object[4]) -- 4
Why does the above code work? When a metatable is set with the index __index (metamethods.__index), if the attached table's (object) is indexed and the key isn't present (nil), then it will call the specified function. In the __index function, it returns the demo's table with the index passed straight to it. So its as if: when you do object[1], you actually do demo[1] but with a metamethod's help of course. This effectively creates a proxy of sorts.
One cool and quick usage of setmetatable() is that it returns the value you pass as the first parameter (a table).
local object1 = setmetatable({}, { __index = function(self, i) return 1 end })
print(object1["a"]) -- 1
print(object2[321]) -- 1

Write-once table in lua?

I'd like to have a write-once table in Lua (specifically LuaJIT 2.0.3), so that:
local tbl = write_once_tbl()
tbl["a"] = 'foo'
tbl["b"] = 'bar'
tbl["a"] = 'baz' -- asserts false
Ideally, this would otherwise function like a regular table (pairs() and ipairs() work).
__newindex is basically the opposite of what I'd want for implementing this easily, and I am unaware of any techniques for making a proxy table pattern work with pairs() and ipairs().
You need to use a proxy table, that is, an empty table that catches all access to the actual table:
function write_once_tbl()
local T={}
return setmetatable({},{
__index=T,
__newindex=
function (t,k,v)
if T[k]==nil then
T[k]=v
else
error("table is write-once")
end
end,
__pairs= function (t) return pairs(T) end,
__ipairs= function (t) return ipairs(T) end,
})
end
Note that __pairs and __ipairs only work from Lua 5.2 onwards.

lua - how to initialize a table using a string

I have the following string:
mystring = "a=test;b=12345"
I would like to know how to initialize a table in one shot, assign it the value of the string. The string originates from another external application, and if possible I want to avoid having to split it up. Something like this:
mytable = {mystring:gsub(";",",")}
Is it possible to do something like this? I know how to do it in multiple steps... but just wondering if it's possible to do it all at once.
Here's what I've tried and the respective output:
> mystring = "a=123;b=2345"
> myarray = {mystring:gsub(";",",")}
> for key,value in pairs(myarray) do print(key,value) end
1 a=123,b=2345
2 1
>
whereas I was hoping to end up with an array / table where like this:
key value
a 123
b 2345
-- Lua 5.2+ required
function string_to_table (str)
local result = {}
load(str, '', 't', setmetatable({}, {
__index = function(t,k) return k end,
__newindex = result
}))()
return result
end
mytable = string_to_table("a=test;b=12345;c=a") -- {a="test", b=12345, c="a"}
Try this, which lets Lua do the hard work:
function parse(s)
local t={}
load(s,"",nil,t)()
return t
end
mytable=parse("a=123;b=2345")
for k,v in pairs(mytable) do print(k,v) end
Note that this executes the code in the given string, which may be dangerous if it comes from an untrusted source. On the other hand, the damage is limited because the code is executed in an empty environment and so cannot affect existing variables. Malicious code may contain infinite loops or consume all memory, though.
mytable = {}
for key, value in string.gmatch("a=123;b=456", "(%w+)=(%w+)") do
mytable[key] = value
end
print(mytable.a, mytable.b)
Returns:
123
456
as expected. This only works, of course, with alphanumeric and no punctuation.

Changing metatable in Lua breaks colon operator

While learning Lua, I borrowed some code from here
to use string indexing, which is exactly this:
getmetatable("").__index = function(str, i) return string.sub(str, i, i) end
After that, I wrote a function to reverse a string as practice.
function reverse_string(str)
local s = ""
for i = string.len(str), 1, -1 do s = s .. str[i] end
return s
end
That works fine, until I change the string.len(str) to str:len(), then I get this error:
reverse.lua:9: bad argument #2 to 'sub' (number expected, got string)
Debugging print()'s tell me that the __index function is being called on str:len(), and that the i argument is becoming the string "len". I know that str:len() works without the metatable there, but as soon as I add it this happens, why?
From Lua 5.2 Refernce Manual:String Manipulation
The string library provides all its functions inside the table string. It also sets a metatable for strings where the __index field points to the string table. Therefore, you can use the string functions in object-oriented style. For instance, string.byte(s,i) can be written as s:byte(i).
So, the object oriented style like str:len() comes from the default metamethod __index, which you have modified.
The index function is passed the table, and the key that is being indexed. so 'str' should be the string and 'i' should be key in your case. Because "len" is not in the meta table it calls __index and passes the string as the first argument and the key ("len") as the second argument. it looks as though you need to check to the type of 'i' to see what to do for better handling of strings
getmetatable("").__index = function(str, key)
if type(key) == "string" then
return string[key]
else
return string.sub(str, key, key)
end
end
str = "hello, world!"
print(str:len())
print(str[5])
see here for more info

In Lua, is there a function that given a function, it returns its name as a string?

Sorry if this is too obvious, but I am a total newcomer to lua, and I can't find it in the reference.
Is there a NAME_OF_FUNCTION function in Lua, that given a function gives me its name so that I can index a table with it? Reason I want this is that I want to do something like this:
local M = {}
local function export(...)
for x in ...
M[NAME_OF_FUNCTION(x)] = x
end
end
local function fun1(...)
...
end
local function fun2(...)
...
end
.
.
.
export(fun1, fun2, ...)
return M
There simply is no such function. I guess there is no such function, as functions are first class citizens. So a function is just a value like any other, referenced to by variable. Hence the NAME_OF_FUNCTION function wouldn't be very useful, as the same function can have many variable pointing to it, or none.
You could emulate one for global functions, or functions in a table by looping through the table (arbitrary or _G), checking if the value equals x. If so you have found the function name.
a=function() print"fun a" end
b=function() print"fun b" end
t={
a=a,
c=b
}
function NameOfFunctionIn(fun,t) --returns the name of a function pointed to by fun in table t
for k,v in pairs(t) do
if v==fun then return k end
end
end
print(NameOfFunctionIn(a,t)) -- prints a, in t
print(NameOfFunctionIn(b,t)) -- prints c
print(NameOfFunctionIn(b,_G)) -- prints b, because b in the global table is b. Kind of a NOOP here really.
Another approach would be to wrap functions in a table, and have a metatable set up that calls the function, like this:
fun1={
fun=function(self,...)
print("Hello from "..self.name)
print("Arguments received:")
for k,v in pairs{...} do print(k,v) end
end,
name="fun1"
}
fun_mt={
__call=function(t,...)
t.fun(t,...)
end,
__tostring=function(t)
return t.name
end
}
setmetatable(fun1,fun_mt)
fun1('foo')
print(fun1) -- or print(tostring(fun1))
This will be a bit slower than using bare functions because of the metatable lookup. And it will not prevent anyone from changing the name of the function in the state, changing the name of the function in the table containing it, changing the function, etc etc, so it's not tamper proof. You could also strip the tables of just by indexing like fun1.fun which might be good if you export it as a module, but you loose the naming and other tricks you could put into the metatable.
Technically this is possible, here's an implementation of the export() function:
function export(...)
local env = getfenv(2);
local funcs = {...};
for i=1, select("#", ...) do
local func = funcs[i];
for local_index = 1, math.huge do
local local_name, local_value = debug.getlocal(2, local_index);
if not local_name then
break;
end
if local_value == func then
env[local_name] = local_value;
break;
end
end
end
return env;
end
It uses the debug API, would require some changes for Lua 5.2, and finally I don't necessarily endorse it as a good way to write modules, I'm just answering the question quite literally.
Try this:
http://pgl.yoyo.org/luai/i/tostring
tostring( x ) should hopefully be what you are looking for
If I am not wrong (and I probably will, because I actually never programmed in Lua, just read a bunch of papers and articles), internally there is already a table with function names (like locals and globals in Python), so you should be able to perform a reverse-lookup to see what key matches a function reference.
Anyway, just speculating.
But the fact is that looking at your code, you already know the name of the functions, so you are free to construct the table. If you want to be less error prone, it would be easier to use the name of the function to get the function reference (with eval or something like that) than the other way around.

Resources