How does setmetatable() work and why the metatable is needed in the linked list in lua - 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.

Related

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

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.

Lua Get/Set Metatable

local ents = {
GetLocalPlayer = function()
local tbl = {
localplayer = {"Ava", "1", {213,234,234}},
GetIndex = function(self)
return self.localplayer[2]
end,
}
setmetatable(tbl, getmetatable(tbl.localplayer))
return tbl
end
}
local function main()
print(ents.GetLocalPlayer()[2])
end
main() print returns nil. If I was to do ents.GetLocalPlayer():GetIndex() however, it returns 1.
The idea is to have the default return value to be localplayer if I don't do things such as GetIndex()
A table has no default metatable, which is why your getmetatable call returns nil. In order to do anything, the second argument to setmetatable must be a table that has at least one metamethod. (__index is the most common metamethod.)
The solution is to change getmetatable(tbl.localplayer) to {__index = tbl.localplayer}.

__tostring in custom classes Lua

The following code should print 'hello', however it is printing the memory location of the table (i.e. 'table: 052E67D0'). Please, explain what I'm missing here.
TestClass = {}
function TestClass:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
function TestClass:__tostring()
return "hello"
end
local t = TestClass.new{}
print(t)
Update
Tried doing this instead:
TestClass = {}
function TestClass:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
self.__tostring = function() return "hello" end
return o
end
local t = TestClass.new{}
print(t)
which worked. This seems weird because, to me, self in constructor and TestClass: refer to the same table.
Your TestClass:new takes two arguments and you call it with just one when you create t.
Change:
local t = TestClass.new{}
to:
local t = TestClass:new{}
Thanks to that self in this TestClass:new call is now reference to TestClass rather than to empty table which was (most likely) meant to be the new instance of the class.
In case of doubts please refer to Lua Reference Manual §3.4.10 or this stackoverflow question.

Simpler way to call a function in a same table?

I wonder if there's a simpler way to call a function or set variable of the same table instead of writing the table name.
For example, Is there a simpler way to call MyClass.test() function from MyClass.setup() in my below example code?
local MyClass = {}
function MyClass.test()
print("Hello")
end
function MyClass.setup()
MyClass.test()
end
MyClass.setup()
If you use : to call the functions instead of ., Lua implicitly inserts a reference to the table itself as the first argument (similar to the this pointer is some object-oriented languages). Then you can say self:test() to get rid of the name dependence.
local MyClass = {
test = function(self)
print("Hello")
end,
setup = function(self)
self:test()
end
}
MyClass:setup()
You can set the module table to be the environment:
local print = print
local _ENV = {}
function test()
print("Hello")
end
function setup()
test()
end
setup()
return _ENV
local MyClass = {}
function MyClass:Setup()
print('hello...Setup')
end
function MyClass:Test()
self:Setup() -- self -> MyClass
print('hello...Test')
end
MyClass:Test()
-- or inherit
local newClass = MyClass
newClass:Test()
local MyClass = {}
function MyClass.test()
print("Hello")
end
function MyClass.setup(self)
self.test()
end
MyClass.setup(MyClass)
the key is self. In the state, MyClass is a obj and create by {}, so you can use MyClass.Test(MyClass) to incoming parameters MyClass to use the func test() of MyClass obj.
ps:
Syntax sugar provided by Lua:
MyClass.setup(MyClass) ==> MyClass:setup()

Inherit a metatable (class) and use its required constructor parameter

I found this tutorial: http://lua-users.org/wiki/InheritanceTutorial
I've got a metatable called Creature. Creature requires an argument in its constructor.
The required argument is a string that represents the name.
local creature = Creature(name)
Creature has a lot of other methods, like getDescription().
Creature's getDescription () returns a string: "This is a creature".
Creature's getName () returns a string: the name
I want to create a new metatable (class) called Player and have it inherit the Creature metatable (class)
The Player class will override only the getDescription () method.
The Player class will also inherit the Creature's getName () method.
Player's getDescription () returns a string: "This is a player".
I want to be able to do the following:
local creature = Creature("Bob")
print(creature:getDescription())
print(creature:getName())
local player = Player("Joey")
print(player:getDescription())
print(player:getName())
Should print:
This is a creature
Bob
This is a player
Joey
Basically, my issue is that the Creature class requires an argument to identify someone, a name. Its getName () function uses the value in the argument and prints it. If I am going to use Player to inherit all of the functions of Creature (and override if necessary), how do I change the code to make sure Player gets the argument it needs?
Code taken from the tutorial:
-- Create a new class that inherits from a base class
--
function inheritsFrom( baseClass )
-- The following lines are equivalent to the SimpleClass example:
-- Create the table and metatable representing the class.
local new_class = {}
local class_mt = { __index = new_class }
-- Note that this function uses class_mt as an upvalue, so every instance
-- of the class will share the same metatable.
--
function new_class:create()
local newinst = {}
setmetatable( newinst, class_mt )
return newinst
end
-- The following is the key to implementing inheritance:
-- The __index member of the new class's metatable references the
-- base class. This implies that all methods of the base class will
-- be exposed to the sub-class, and that the sub-class can override
-- any of these methods.
--
if baseClass then
setmetatable( new_class, { __index = baseClass } )
end
return new_class
end
I want to be able to do the following:
local creature = Creature("Bob")
print(creature:getDescription())
print(creature:getName())
-- ...
Supporting that kind of class usage syntax is certainly possible in lua -- it's just a question of how to use the language mechanisms and tools to achieve that. Some important issues to decide on:
How will object construction happen? What function should the class call to initialize the instance?
What will object instantiation look like from client-code?
How is method overriding accomplished?
From the wiki tutorial, new_class:create() creates a new instance but doesn't call any construction function. So in your OOP system, you'll have to decide on a constructor-like function that the client-code provides and the class creation will call. For example:
function new_class:create(...)
local instance = setmetatable( {}, class_mt )
if new_class.__init__ then new_class.__init__(instance, ...) end
return instance
end
Here I just use __init__ as the constructor name, similar to python, but really any name will work as long as the using code and class creation code agrees.
In order to support object creation syntax like Creature("Bob"), Player("Joey"), you can either make them actual function calls or use the __call metamethod. Using the latter is a simple assignment to __call:
function inheritsFrom( baseClass )
local new_class = setmetatable( {}, { __index = baseClass } )
-- ...
getmetatable(new_class).__call = new_class.create
return new_class
end
For the last question, you override an existing method by just assigning it a new function in the derived class. eg. So to override getDescription in Player you can do:
function Player:getDescription()
return "Is a player"
end
Putting everything together, inheritsFrom
function inheritsFrom( baseClass )
local new_class = setmetatable( {}, { __index = baseClass } )
local class_mt = { __index = new_class }
function new_class:create(...)
local instance = setmetatable( {}, class_mt )
if new_class.__init__ then new_class.__init__(instance, ...) end
return instance
end
getmetatable(new_class).__call = new_class.create
return new_class
end
Defining class Creature + one creature instance:
local Creature = inheritsFrom
{
__init__ = function(self, name) self.name = name end;
getDescription = function(self) return "Is a creature" end;
getName = function(self) return self.name end;
}
local bob = Creature "Bob"
print(bob:getDescription())
print(bob:getName())
Subclassing Player from Creature and overriding getDescription:
local Player = inheritsFrom(Creature)
function Player:getDescription()
return "Is a player"
end
local joey = Player "Joey"
print(joey:getDescription())
print(joey:getName())
As a final remark, the lua Penlight library already implements a class system similar to what I described above but much more featureful and complete. Unless this is an exercise, consider using that instead of reinventing another lua OOP system.

Resources