Lua: Table inside Object - lua

I'm using OO abstraction in LUA and I'm having trouble with tables inside objects.
I'm using this approach to implement OO support: Object Orientation Tutorial
If i define a table inside the Class like this:
fields = {}
It will be shared by all instances of that Class. Something like an static attribute in Java. However, I'd like to get a normal class attribute instead.
That sounds odd to me, since it works correctly for non-table items like this:
attribute = 0
That is not going to be shared between all instances.
Here is a complete example:
local function C1Constructor( self )
print( "C1: Constructor... " )
self.fields = {}
end
local function C1SetName( self, name )
self.name = name
end
local function C1Add( self, name, value )
print( "C1: (" .. self.name .. ") :: Add: " .. name .. " = " .. value )
self.fields[ name ] = value
end
local function C1Show( self )
print( "C1: (" .. self.name .. ") :: Show:" )
for name, value in pairs( self.fields ) do
print(" " .. name .. " = " .. value )
end
end
C1 = {
name = "",
constructor = C1Constructor,
setName = C1SetName,
add = C1Add,
show = C1Show,
fields = {},
}
function C1.new( o )
o = o or { }
setmetatable( o, { __index = C1 } )
o:constructor()
return o;
end
c1a = C1.new()
c1b = C1.new()
c1a:setName( "Obj A" )
c1b:setName( "Obj B" )
c1a:show()
c1b:show()
c1a:add( "k1", "v1" )
c1b:add( "k2", "v2" )
c1a:show()
c1b:show()
I know how to fix it. But I don't know what is happening behind the scenes:
local function C1Constructor( self )
print( "C1: Constructor... " )
self.fields = {}
end
What am I missing here?
Thanks very much,

Tables are referenced values. When you store a table in a variable you are storing a reference to the table and not a primitive value (like for a string or a number). As such if you create one table and share it among instances you only have one table. When you create a table per-instance (e.g. in your constructor) then you have one table per-instance and not a shared table.

Related

How to implement a trace function for table to functions?

I have a table like this
local ftable = {
getPinId = app.getPinId
}
ftable is passed to another function which exports it as a RPC interface.
This works but now I want to add function call tracing to a log file.
The simple approach is
local ftable = {
getPinId = function(...) print("getPinId") app.getPinId(...) end
}
But, this is not particularly nice.
I'd like to put something like:
local trace = function(func, ...)
return function(...) print(func) func(...) end
end
local ftable = {
getPinId = trace(app.getPinId)
}
But this doesn't produce quite the desired result. The parameters are not being passed through.
One other option is to use a metatable like this:
local ftable = {}
setmetatable(ftable, {
__index = function(_, k)
printf("Call: app.%s\n", k) return app[k] end
})
Which works. But I'd also like to be able to print the parameters that are passed if possible.
Any suggestions?
I'm exclusively using luajit if that makes any difference.
Wrapping a function call is easy in Lua:
local function wrap( f )
local function after( ... )
-- code to execute *after* function call to f
print( "return values:", ... )
return ...
end
return function( ... )
-- code to execute *before* function call to f
print( "arguments:", ... )
return after( f( ... ) )
end
end
local function f( a, b, c )
return a+b, c-a
end
local f_wrapped = wrap( f )
f_wrapped( 1, 2, 3 )
Output is:
arguments: 1 2 3
return values: 3 2
One problem for logging/tracing is that Lua values (including functions) don't have names themselves. The debug library tries to find suitable names for functions by inspecting how they are called or where they are stored, but if you want to make sure, you'll have to supply a name yourself. However, if your functions are stored in (nested) tables (as indicated in a comment), you could write a function that iterates the nested tables, and wraps all functions it finds using the table keys as names:
local function trace( name, value )
local t = type( value )
if t == "function" then -- do the wrapping
local function after( ... )
print( name.." returns:", ... )
return ...
end
return function( ... )
print( "calling "..name..":", ... )
return after( value( ... ) )
end
elseif t == "table" then -- recurse into subtables
local copy = nil
for k,v in pairs( value ) do
local nv = trace( name.."."..tostring( k ), v )
if nv ~= v then
copy = copy or setmetatable( {}, { __index = value } )
copy[ k ] = nv
end
end
return copy or value
else -- other values are ignored (returned as is)
return value
end
end
local ftable = {
getPinId = function( ... ) return "x", ... end,
nested = {
getPinId = function( ... ) return "y", ... end
}
}
local ftableTraced = trace( "ftable", ftable )
ftableTraced.getPinId( 1, 2, 3 )
ftableTraced.nested.getPinId( 2, 3, 4 )
Output is:
calling ftable.getPinId: 1 2 3
ftable.getPinId returns: x 1 2 3
calling ftable.nested.getPinId: 2 3 4
ftable.nested.getPinId returns: y 2 3 4
Some things to be aware of:
Table keys can be arbitrary Lua values, not just short strings entirely consisting of printable characters.
Tables can contain cyclic references. If they do, the naive implementation above will die with a stack overflow.
Use the __call metamethod instead:
M = { __call =
function (t,...) print("calling ",t.name,...) return t.func(...) end
}
trace = function(func,name)
return setmetatable({func=func,name=name},M)
end
function f(...)
print("in f",...)
end
g=trace(f,"f")
g(10,20,30)

Setting __index of current environment in Roblox

In Roblox Studio, I have a ModuleScript object that implements an analogous class to the one shown in chapter 16 of the 1st edition of Programming In Lua, as shown below:
local h4x0r = { }
local function setCurrentEnvironment( t, env )
if ( not getmetatable( t ) ) then
setmetatable( t, { __index = getfenv( 0 ) } )
end
setfenv( 0, t )
end
do
setCurrentEnvironment( h4x0r );
do
h4x0r.Account = { };
setCurrentEnvironment( h4x0r.Account );
__index = h4x0r.Account;
function withdraw( self, v )
self.balance = self.balance - v;
return self.balance;
end
function deposit( self, v )
self.balance = self.balance + v;
return self.balance;
end
function new( )
return setmetatable( { balance = 0 }, h4x0r.Account )
end
setCurrentEnvironment( h4x0r );
end
end
return h4x0r
I then attempted to use the following script to access the Account class, assuming that all of the members of the 2nd do-end block would be assigned to h4x0r.Account:
h4x0r = require( game.Workspace.h4x0r );
Account = h4x0r.Account;
account = Account.new( );
print( account:withdraw( 100 ) );
The above script fails with the error Workspace.Script:5: attempt to call method 'withdraw' (a nil value), so it must be an issue regarding the line where I set the __index field of h4x0r.Account.
Can someone please explain to me where I went wrong?
Try using getfenv(2) and setfenv(2, t) instead of getfenv(0) and setfenv(0, t). You essentially want to change the environment of the encapsulating function, which would be stack level 2.
0 is a special argument that would instead get or set the environment of the thread, which is used as a default environment in some cases, but that does not affect the individual closures that have already been instantiated in the thread, hence it doesn't work in this case.

Unable to figure out lua table inheritence

Hope someone can make sense of what I'm attempting to figure out, Just don't seem to understand Lua enough to achieve this.
--[[
tbl.a.test("moo") returns "Table A moo appears"
tbl.b.test("moo") returns "moo appears"
]]
tbl = {
a = { ID = "Table A" },
b = {
test = function(...) print(... .. " appears") end,
},
}
tbl.a__index = function(self, ...) tbl.b[self](tbl.a.ID .. ...) end
What I'm attempting to do is I could create several tables a, c, d, e and not have to copy test to each one. When tbl.a.test, tbl.c.test, tbl.d.test is used, It'll retrieve the tbl.a.ID var, then call tbl.b.test(ID, "moo")
So far all I'm finding out is it's not able to find .test on anything other than tbl.b
** EDIT **
Thank's to support so far the code is now;
tbl = {
a = { ID = "Table A " },
b = { test = function(...) local id, rest = ... print(id .. ": " .. rest) end },
}
setmetatable(tbl.a, {__index=function(self, k, ...) local rest = ... return tbl.b[k](tbl.a.ID, rest) end})
However, the ... is not being progressed for some odd reason :|
You're missing a period between tbl.a and __index.
__index needs to be on a's metatable, not the table itself.
You don't return anything from your __index function
self in the __index function is the table being indexed, not the key (which is the second argument)
This should work:
setmetatable(tbl.a, {__index=function(self, k) return tbl.b[k](tbl.a.ID) end})
--------------------------------------------------------------------------------
-- [Sub]Class creation
--------------------------------------------------------------------------------
function newclass(new_obj,old_obj)
old_obj = old_obj or {} --use passed-in object (if any)
new_obj = new_obj or {}
assert(type(new_obj) == 'table','New Object/Class is not a table')
assert(type(old_obj) == 'table','Old Object/Class is not a table')
old_obj.__index = old_obj --store __index in parent object (optimization)
return setmetatable(new_obj,old_obj) --create 'new_obj' inheriting 'old_obj'
end
--------------------------------------------------------------------------------
prototype = {
test = function(self,s) print('Table ' .. self.id .. ' ' .. s .. ' appears') end
}
tbl = {}
tbl.a = newclass({id = 'A'},prototype)
tbl.b = newclass({id = 'B'},prototype)
tbl.a:test('moo')
tbl.b:test('moo')
The distinction between class and object in Lua is only theoretical. In practice they are implemented exactly the same way.
Anytime you need to do inheritance, you can use my general-purpose newclass() function to either create a new class/object, or inherit from an existing one.
Any common code & data you would like to have passed on should go into the 'prototype' table (whatever you'd like to call it for each case).
Also, you seem to forget to use the method calling syntax (that uses a colon instead of a dot) when calling methods. Without it, the self parameter is not automatically recognized.

Lua: table element doesn't want to be copied while the value exists

The key I'm assigning value to still remains nil after return!
p = {};
for i = 0, 1000 do
p[ i ] = {};
end
t = {}
t.__index = t
function new()
local o = setmetatable({}, t)
for key, value in pairs( p[1] ) do
print(key .. " : " .. value)
o[ key ] = value
end
t.__newindex = function(t, key, value)
if type(value) ~= "function" then
p[1][key] = value
end
end
return o
end
local something = new()
something.xxx = 666
print(p[1].xxx)
local something = new()
print(something.xxx)
print(p[1].xxx)
The problem is with it:
for key, value in pairs( p[1] ) do
print(key .. " : " .. value)
o[ key ] = value
end
It shows xxx : 666 but o[ key ] still remains nil when I return it. The output is
666
xxx : 666
nil
666
EDIT: yeah, I realized just now. I'll give you upvotes later because of net bandwidth(cant run stac with javascript) Thanks, now it works. Created new object based on previous one.
o[ key ] = value will invoke the newindex metamethod. But since you haven't set it yet, it will do the default action. Which is setting the value into o's table at the index key.
Of course, the index metamethod won't care about what is in o's table; it's looking at itself. So your later o[key] will look in the metatable for the key, which it won't find.
It's unclear what this code is trying to accomplish.
You set the __index metamethod for the new table o to t, which contains only __index and __newindex, none of which equal xxx, so the lookup fails and returns nil. You probably wanted to say t.__index = p instead.

Referencing functions enclosing table in Lua

I have a helper function which returns a table like this:
function of_type(expected_type)
return {
expected = expected_type,
matches = function(value) return type(value) == expected_type end,
describe = "type " .. expected_type
}
end
Now this was fine with other matchers but here I would like to store the type(value) to a field in the same table when the matches function is called. Something like this:
function of_type(expected_type)
return {
expected = expected_type,
mismatch = nil, -- set it to nil on initialization
matches = function(value)
mismatch = type(value) -- how to do this?
return type(value) == expected_type
end,
describe = "type " .. expected_type
}
end
Is this possible?
Yes, but you need to split it into steps:
function of_type(expected_type)
local tbl = {
expected = expected_type,
mismatch = nil, -- set it to nil on initialization
describe = "type " .. expected_type
}
tbl.matches = function(value)
tbl.mismatch = type(value)
return type(value) == tbl.expected
end
return tbl
end
-- testing it
local foo = of_type("string")
print(foo.matches(1), foo.matches("1"))
This should output false true as you would expect.
Essentially, tbl.matches will store the reference to tbl (it's called "upvalue") and will be able to modify all the fields in that table (including the reference to itself it in).
Another way to do this would be the following (notice the changes in the tbl.matches function). Instead of capturing it as an upvalue, you can use tbl:method semantic and pass tbl as implicit self parameter:
function of_type(expected_type)
local tbl = {
expected = expected_type,
mismatch = nil, -- set it to nil on initialization
describe = "type " .. expected_type
}
function tbl:matches(value)
self.mismatch = type(value) -- how to do this?
return type(value) == self.expected
end
return tbl
end
local foo = of_type("string")
print(foo:matches(1), foo:matches("1"))
This will print the same result. Notice that you are using foo:matches notation to make foo to be passed as the first parameter (references as self in the method). This is the same as using foo.matches(foo, 1).
You don't. Well, not without storing a copy of the table, or passing the function the table as a parameter. Until all of the statements of the table constructor have been processed, the table doesn't exist yet. And since you never stored it anywhere (in this function), your function can't name it in order to find it.
So you should give it a name, even just for a moment:
function of_type(expected_type)
local temp = nil
temp = {
expected = expected_type,
mismatch = nil, -- set it to nil on initialization
matches = function(value)
temp.mismatch = type(value) -- Like this
return type(value) == expected_type
end,
describe = "type " .. expected_type
}
return temp
end
This works because Lua will store temp as an upvalue. Thus, the function you're creating will see changes in temp, such as when you set it to a table value. And since it's a local variable, it won't be visible outside of this function.

Resources