lua: how to bind my function to the io library - lua

I wrote a lua function write_json which translates lua table into json text.
Is it possible to bind the function with the io library, so that I can use it like this:
mytable = {name="Jack", age="22", score=[12,33,55,66]}
f = io.open("score.json", "wb")
f:write_json(mytable) -- call my function here.
f:close()

You need access to the __index table of the metatable for file objects and put your new methods there:
local metatable = getmetatable( io.stdout )
local indextable = metatable.__index
indextable.write_json = function( file, tab )
-- ...
end
There is another way: The C API function luaL_newmetatable stores the metatable for file objects in the registry under the key "FILE*", so the following will also work (but requires the debug library):
local metatable = debug.getregistry()["FILE*"]
local indextable = metatable.__index
-- ...
There is yet another (more hackish) way: All Lua versions I tested (PUC-Rio Lua 5.1, 5.2, 5.3, and LuaJIT) set the __index field of the metatable to the metatable itself, so you can get at the __index table like that:
local indextable = io.stdout.__index
The best way is probably the first one.

The type of the object returned by io.open is userdata, which I believe cannot be monkey-patched due to its unique nature.

Related

passing parameters of a function to another function when using metatable

im learning lua and got to metatable part, in this example
local tb = {}
local meta = {}
function tb.new(s)
local super = {}
super.s = s
setmetatable(super,meta)
return super
end
function tb.add(s1,s2)
return s1.s..s2.s
end
meta.__add = tb.add
f= tb.new("W")
t= tb.new("E")
print(f+t)
in this part
function tb.add(s1,s2)
return s1.s..s2.s
end
how are values from super.s passed to tb.add function. i noticed that the variable s in s1.s and s2.s in return s1.s..s2.s seems to be the link between tb.new and tb.add.
and in this part
f= tb.new("W")
t= tb.new("E")
print(f+t)
when compiler gets to f+t i think this function is called first f= tb.new("W") in this function right now there is no s2 so it should be nil how did return s1.s..s2.s happen?
Your code is missing a table tb. Otherwise it results in an error for indexing a nil value.
Your code creates two table values with a field s = "E" and s = "W" respectively. Both have share the same metatable meta that implements the __add metamethod.
From the Lua 5.4 Reference Manuual 2.4 - Metatables and Metamethods
Every value in Lua can have a metatable. This metatable is an ordinary
Lua table that defines the behavior of the original value under
certain events. You can change several aspects of the behavior of a
value by setting specific fields in its metatable. For instance, when
a non-numeric value is the operand of an addition, Lua checks for a
function in the field __add of the value's metatable. If it finds one,
Lua calls this function to perform the addition.
So for f+t will call meta._add(f, t) which returns f.s..t.s
tb.new basically just creates a new table and returns it, so in this code:
f= tb.new("W")
t= tb.new("E")
... f and t are newly created tables, each with an s field and both with the same metatable.
The interpreter automatically calls meta.__add when it sees f+t and passes both operands as arguments. tb.add has two parameters and assumes both of them have an s field which is a string, so that all checks out perfectly. If t was not a table with an s field, then you would be in trouble.
In other words, tb.new does not pass s to tb.add. tb.new stores s inside the new table, and tb.add retrieves that value from its parameters.
As commented by #lhf and answered by #Piglet the code is not working as is.
So here is a corrected version that uses/show new Lua 5.4 warn().
It can be used for debugging because #on and #off let you control the output.
Here it is...
if warn==nil then warn=print else warn('#on') end
local meta={}
local tb={}
function tb.new(s)
warn('tb.new("'..s..'")')
local super={}
super.s=s
setmetatable(super,meta)
return super
end
function tb.add(s1,s2)
warn('__add('..tostring(s1)..', '..tostring(s2)..')')
return s1.s..s2.s
end
meta.__add=tb.add
f=tb.new("W")
t=tb.new("E")
print('Print: ',f+t)
Output is...
Lua warning: tb.new("W")
Lua warning: tb.new("E")
Lua warning: __add(table: 0x5661f510, table: 0x5661f580)
Print: WE
( On earlier Lua versions than 5.4 print() is used instead of warn() )

How to modify a metatable which was created by C API?

I want to add some methods or properties to a lua object witch metadata was created by C API. I can't add property in normal way, for example:
local foo = libc.new()
foo.bar = "hello"
it say:
Failed to run script: attempt to index a libc_meta value (local 'foo')
So I think maybe need to modify metatable, so I change my code:
local foo = libc.new()
local mt = getmetatable(foo)
foo[bar] = "hello"
setmetable(foo, mt)
Unfortunately, it still doesn't work.
Failed to run script: bad argument #1 to 'setmetatable' (table expected, got libc_meta)
So how can I add methods or properties to this 'foo'?
BTW, c code is here:
static int libc_new(lua_State *L) {
...
lua_lib_space *libc = lua_newuserdata(L, sizeof(*libc));
libc->L = L;
libc->cb_enter = cb_enter;
libc->cb_leave = cb_leave;
luaL_getmetatable(L, "libc_meta");
lua_setmetatable(L, -2);
lib_space *lib = lib_new(enter, leave, libc);
libc->space = lib;
return 1;
}
Userdata is meant to be created by C, manipulated by C code, and for only the purposes that C code intends. Lua can talk to it, but only in the ways that C allows it to. As such, while userdata can have a metatable (and those metamethods are the only way Lua can "directly" interact with the userdata), only C functions (and the debug library, but that's cheating) can directly manipulate that metatable. Lua is an embedded language, and C has primacy; if some C library wants to shut you out, it can.
What you can do is take the userdata and stick it in a table of your own, then give that table a metatable. In your __index metamethod, if you have an override or new method for a particular key, then you forward the accesses to that. Otherwise, it will just access the stored userdata.
However, if what you get in the userdata is a function, then you may have a problem. See, most userdata functions take the userdata as a parameter; indeed, most are meant to be called via "ud:func_name(params). But if thatud` is actually the table wrapper, then the first parameter passed to the function will be the wrapper, not the userdata itself.
That poses a problem. When the thing you get from the userdata is a function, you would need to return a wrapper for that function which goes through the parameters and converts any references to the wrapper table into the actual userdata.

Lua 5.3 variable argument function in metatable

I have a function as follows:
local function mytest(...)
local args={...}
if(#args==1 and type(args[1])=="table") then
local x, mean=nil, nil
for k,v in pairs(args[1]) do
k=string.lower(k)
if(k=="x") then x=v
elseif(k=="mean") mean=v
--the rest is omitted for brevity
end
Then right after the function (in the same script file), I have the following lines:
mytable.test={}
mytable.test.mt={}
mytable.test.mt.__call=mytest
setmetatable(mytable.test, mytable.test.mt)
where mytable is some table in the global space.
When I call the function as follows:
mytable.test{x=Vec, mean=3}
where Vec is of type userdata, it seems that the type of Vec and type of mean are not correctly passed to the local function. As a matter of fact, both seems to be passed as table. However, if I introduce in the following way:
mytable.test=mytest
then everything works as expected. Not sure what point I am missing.
Based on the comment from Luther, the following change solves the problem:
mytable.test={}
mytable.test.mt={}
setmetatable(mytable.test, mytable.test.mt)
function mytable.test.mt:__call(...)
return mytest(...)
end

Metatable is not indexed, even though setmetatable is used

According to the Lua manual, setmetatable still works the same as in Lua 5.0. Yet for some reason, when I try this code in Lua 5.1.5 and 5.3.1, it appears that the metatable is not accessed:
ClassTable = {}
ClassTable.getString = function(self)
return self.x .. ""
end
inst = {}
setmetatable(inst, ClassTable)
inst.x = 7
--doens't work
assert(getmetatable(inst) == ClassTable)
print(inst:getString())
The first case works, however in the second case the I get the error which suggests that the metatable is not being used:
./lua: /test.lua:12: attempt to call method 'getString' (a nil value)
stack traceback:
test.lua:12: in main chunk
[C]: ?
This also has nothing to do with the method call operator ":" as even getting the value of the method doesn't go to the metatable.
print(inst.getString)
nil
To make the table inst access the metatable you need to use the metamethod __index.
So you can correct the code by adding this line at the top below ClassTable.getString definition:
ClassTable.__index = ClassTable
Despite the name, the __index metamethod does
not need to be a function: It can be a table, instead. When it is a
function, Lua calls it with the table and the absent key as its
arguments. When it is a table, Lua redoes the access in that table.
http://www.lua.org/pil/13.4.1.html

is there any keyword like const or anything else which does the same job with it in lua?

Is there a const keyword in lua ? Or any other similar thing? Because i want to define my variables as const and prevent change of the value of the variables.
Thanks in advance.
I know this question is seven years old, but Lua 5.4
finally brings const to the developers!
local a <const> = 42
a = 100500
Will produce an error:
lua: tmp.lua:2: attempt to assign to const variable 'a'
Docs: https://www.lua.org/manual/5.4/manual.html#3.3.7.
Lua does not support constants automatically, but you can add that functionality. For example by putting your constants in a table, and making the table read-only using metatable.
Here is how to do it: http://andrejs-cainikovs.blogspot.se/2009/05/lua-constants.html
The complication is that the names of your constants will not be merely "A" and "B", but something like "CONSTANTS.A" and "CONSTANTS.B". You can decide to put all your constants in one table, or to group them logically into multiple tables; for example "MATH.E" and "MATH.PI" for mathematical constants, etc.
As already noted there is no const in Lua.
You can use this little workaround to 'protect' globally defined variables (compared to protected tables):
local protected = {}
function protect(key, value)
if _G[key] then
protected[key] = _G[key]
_G[key] = nil
else
protected[key] = value
end
end
local meta = {
__index = protected,
__newindex = function(tbl, key, value)
if protected[key] then
error("attempting to overwrite constant " .. tostring(key) .. " to " .. tostring(value), 2)
end
rawset(tbl, key, value)
end
}
setmetatable(_G, meta)
-- sample usage
GLOBAL_A = 10
protect("GLOBAL_A")
GLOBAL_A = 5
print(GLOBAL_A)
There is no const keyword in Lua or similar construct.
The easiest solution is to write a big caution in a comment, telling that it is forbidden to write to this variable...
It is however technically possible to forbid writing (or reading) to a global variable by providing a metatable to the global environment _G (or _ENV in Lua 5.2).
Something like this:
local readonly_vars = { foo=1, bar=1, baz=1 }
setmetatable(_G, {__newindex=function(t, k, v)
assert(not readonly_vars[k], 'read only variable!')
rawset(t, k, v)
end})
Then if you try to assign something to foo, an error is thrown.

Resources