Why doesn't the __call metamethod work in this Lua class? - lua

in the code below, i set up a __call metafunction which, in theory, should allow me to call the table as a function and invoke the constructor, instead of using test.new()
test = {}
function test:new()
self = {}
setmetatable(self, self)
--- private properties
local str = "hello world"
-- public properties
self.__index = self
self.__call = function (cls, ...) print("Constructor called!") return cls.new(...) end
self.__tostring = function() return("__tostring: "..str) end
self.tostring = function() return("self:tstring(): "..str) end
return self
end
local t = test:new()
print(t) -- __tostring overload works
print(tostring(t)) -- again, __tostring working as expected
print(t:tostring()) -- a public call, which works
t = test() -- the __call metamethod should invoke the constructor test:new()
output:
> __tostring: hello world
> __tostring: hello world
> self.tostring(): hello world
> error: attempt to call global `test` (a table value) (x1)
(i'm using metatable(self, self) because i read somewhere it produces less overhead when creating new instances of the class. also it's quite clean-looking. it may also be where i'm getting unstuck).

You're setting the __call metamethod on the wrong table - the self table - rather than the test table. The fix is trivial:
test.__call = function(cls, ...) print("Constructor called!") return cls.new(...) end
setmetatable(test, test)
After this, test(...) will be equivalent to test.new(...).
That said, your current code needs a refactoring / rewrite; you overwrite the implicit self parameter in test:new, build the metatable on each constructor call, and don't even use test as a metatable! I suggest moving methods like tostring to test and setting the metatable of self to a metatable that has __index = test. I'd also suggest separating metatables and tables in general. I'd get rid of upvalue-based private variables for now as they require you to use closures, which practically gets rid of the metatable benefit of not having to duplicate the functions per object. This is how I'd simplify your code:
local test = setmetatable({}, {__call = function (cls, ...) return cls.new(...) end})
local test_metatable = {__index = test}
function test.new()
local self = setmetatable({}, test_metatable)
self._str = "hello world" -- private by convention
return self
end
function test_metatable:__tostring()
return "__tostring: " .. self._str
end
function test:tostring()
return "self:tstring(): " .. self._str
end
If you like, you can merge test and test_metatable; I prefer to keep them separated however.

Related

Lua: Workaround for boolean conversion of a class variable when enclosed in parentheses

In the below code, can anyone explain why does t1:print() works but (t1):print fails. I am attempting to make something like (t1 * 3):print() work without using an intermediate variable.
function classTestTable(members)
members = members or {}
local mt = {
__metatable = members;
__index = members;
}
function mt.print(self)
print("something")
end
return mt
end
TestTable = {}
TestTable_mt = ClassTestTable(TestTable)
function TestTable:new()
return setmetatable({targ1 = 1}, TestTable_mt )
end
TestTable t1 = TestTable:new()
t1:print() -- works fine.
(t1):print() -- fails with error "attempt to call a boolean value"
Lua expressions can extend over multiple lines.
print
(3)
Will print 3
So
t1:print()
(t1):print()
actually is equivalent to
t1:print()(t1):print()
or
local a = t1:print()
local b = a(t1)
b:print()
So you're calling the return value of t1:print()
To avoid that follow Egors advice and separate both statements with a semicolon.
t1:print();(t1):print()

OOP Help - How do I get this code block to recognize one of its arguments?

I'm learning Lua and how to implement OOP. Trying out a test example of an object seems to return one of the argument of the object as 'null' despite being assigned one.
function Character(Name, Level, Class) --Constructor
return {GetName = T.GetName, GetLevel = T.GetLevel, GetClass = T.GetClass}
end
end
-- Snippets
Player = Character("Bob", 1, "Novice")
When I try printing Player.GetName() it returns null instead of Bob. Where have I gone wrong?
Here is the full code.
OOP in Lua takes a bit more than what you have done there, you will want to make use of metatables and upvalues.
-- How you could define your character structure.
local Character = {}
function Character.GetName(self)
return self.name
end
function Character.new(Name, Level, Class)
local _meta = {}
local _private = {}
_private.name = Name
_private.level = Level
_private.class = Class
_meta.__index = function(t, k) -- This allows access to _private
return rawget(_private, k) or rawget(Character, k)
end
_meta.__newindex = function(t, k, v) -- This prevents the value from being shaded
if rawget(_private, k) or rawget(Character, k) then
error("this field is protected")
else
rawset(t, k, v)
end
end
return setmetatable({}, _meta) --return an empty table with our meta methods implemented
end
This creates a local table _private when you create a new instance of a Character. That local table is an upvalue to the _meta.__index and it cannot be accessed outside the scope of the Character.new function. _private can be accessed when __index is called because it is an upvalue.
-- How to use the character structure
player = Character.new("Bob", 10, "Novice")
npc = Character.new("Alice", 11, "Novice")
print(player:GetName())
I use player:GetName(), but in all honesty you can just do player.name as well.
Resources for more on this topic:
http://tutorialspoint.com/lua/lua_metatables.htm
http://lua-users.org/wiki/ObjectOrientationTutorial

lua: how can i get the raw string in __tostring metamethod?

code below:
local t = {}
setmetatable(t, {__tostring = function(self) return 'MyTable is: '..tostring(self) end})
print(t)
running the code will cause error: "C stack overflow". Because in __tostring metamethod, tostring(self) will invoke the __tostring metamethod, that's a dead loop.
Is there a way to get the raw string of the value "t"?
To do what you're trying to do from Lua, you basically have to unset the metatable from the main table, then call tostring on it, then set the metatable back. Like this:
setmetatable(t, {__tostring = function(self)
local temp = getmetatable(self)
setmetatable(self, nil)
local ret = 'MyTable is: ' .. tostring(self)
setmetatable(self, temp)
return ret
end,
})
Also, note that the __tostring metafunction is supposed to return the string, not merely print it.

How does setmetatable() work and why the metatable is needed in the linked list in lua

I'm learning how Lua's metatables work in OOP, and I am confused of the code I read from the object orientation tutorial on lua-users wiki. Could someone helps to explain the following questions? Thanks.
Question 1:
The wiki's explanation: Here we add a metatable to the class table that has the __call metamethod, which is triggered when a value is called like a function. We make it call the class's constructor, so you don't need the .new when creating instances.
(1)How does the __call get called in the example so the constructor is called?
(2)Does "cls" refer to "MyClass?"
setmetatable(MyClass, {
__call = function (cls, ...)
return cls.new(...)
end,
})
Question 2:
What does {} from the following code refer to?
function MyClass.new(init)
local self = setmetatable({}, MyClass)
self.value = init
return self
end
**Here is the Complete code:
local MyClass = {}
MyClass.__index = MyClass
setmetatable(MyClass, {
__call = function (cls, ...)
return cls.new(...)
end,
})
function MyClass.new(init)
local self = setmetatable({}, MyClass)
self.value = init
return self
end
function MyClass:set_value(newval)
self.value = newval
end
function MyClass:get_value()
return self.value
end
local instance = MyClass(5)
-- do stuff with instance...
Question 1:
setmetatable(MyClass, {
__call = function (cls, ...)
return cls.new(...)
end,
})
This sets MyClass's metatable to a table which defines the __call metamethod. Because of that, you can 'call' MyClass (newObj = MyClass(<args>)). In the metamethod, cls refers to the table that is called, in this case it refers to MyClass.
Question 2:
function MyClass.new(init)
local self = setmetatable({}, MyClass)
self.value = init
return self
end
{} is syntax for a table literal, which creates a new table (an empty one in this case). The MyClass table is set as the metatable for a new table. Then this new table is assigned to self.
More about metatables can be found here: https://www.google.com/?gws_rd=ssl#q=lua+metatables as suggested by Anderson Green in the comments.

How does `table.insert` work with custom tables in Lua

I wonder how does table.insert work in lua?!
I am asking this because I have tried to use it on a custom table with __newindex metamethod
but it seems not to call it. Is there a way to make my custom table functionality to work with table.insert?!
From my humble knowledge about the language I would say it uses something like rawset or something maybe I donno.
Sample I worked on:
do
tabl = {1,2,3}
local _tabl = tabl
tabl = {}
local mt = { __newindex = function(t,k,v) print"changing" ;_tabl[k] = v end, __index = _tabl}
setmetatable(tabl,mt)
end
tabl[4] = 4; --prints "changing"
table.insert(tabl,5) -- prints nothing!!
There's no such metamethod, table.insert just inserts a new value to a specified table.
local myTable = {}
table.insert(myTable, "somestring")
-- so now myTable has one value, myTable = { "somestring" }
It works like:
local myTable = {}
myTable[#myTable + 1] = "somestring"
__newindex metamethod affects only assignment operator "=", table.insert is just a separate function not related with metatables, you can modify the behaviour of this function if you want:
_tableinsert = table.insert
function table.insert(t, v)
-- here your actions, before real function will be used
_tableinsert(t, v)
end
I think that would be possible to make your own metamethod __tableinsert this way.
table.insert does, in fact, use rawset. See the lua 5.1 source here.
As indicated if you do the assignment yourself you should be able to get the behavior you want.

Resources