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

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.

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.

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()

Scoping classes created in Lua using Luabind

I am aware that Lua classes can be created using the OO system that Luabind exposes to Lua:
http://www.rasterbar.com/products/luabind/docs.html#defining-classes-in-lua
class 'lua_testclass'
function lua_testclass:__init(name)
self.name = name
end
function lua_testclass:print()
print(self.name)
end
a = lua_testclass('example')
a:print()
However I am unable to figure out how to scope the class within another namespace so I can do the following:
a = MyScope.lua_testclass('example')
a:print()
Anyone has a idea. I do not want my classes to pollute the global namespace in Lua.
Luabind's class function will always pollute the global table. However, you can clean up after it:
function newclass(name)
oldglobal = _G[name]
class(name)
cls = _G[name]
_G[name] = oldglobal
return cls
end
Then you would use it like this:
MyScope.lua_testclass = newclass 'lua_testclass'
Analogous to local mod = require 'mod' you have to spell the name of the class twice, but you could easily build another function on top of this that could be used like setclass(MyScope, 'lua_testclass'), automatically putting the class into MyScope:
function setclass(scope, name) scope[name] = newclass(name) end
Disclaimer: All this code is entirely untested.
I did mine a little differently, but it's generally the same concept. Mine doesn't create the class, but rather just moves it. I also implemented it on the C++ side.
To implement what I did in Lua, you would write:
function moveClass(name)
oldGlobal = _G[name]
_G[name] = nil
return oldGlobal
end
To implement it in C++, you would write:
luabind::module(lua) [
luabind::def("moveClass", +[](lua_State * lua, std::string name) {
// In the case the class does not exist, this will just
// remove nil and return nil. That essentially does nothing.
luabind::object oldGlobal = luabind::globals(lua)[name];
luabind::globals(lua)[name] = luabind::nil;
return oldGlobal;
})
];
So now if you were to use that to move a class you created, you would do this:
class 'MyClass'
myTable = {}
myTable.MyClass = moveClass 'MyClass'
As an extra note, if you want the moveClass function to give an error in the case that the class you are trying to move does not exist, use luabind::type(oldGlobal) == LUA_TNIL to determine if the class existed or not.
Example:
luabind::module(lua) [
luabind::def("moveClass", +[](lua_State * lua, std::string name) {
luabind::object oldGlobal = luabind::globals(lua)[name];
if (luabind::type(oldGlobal) == LUA_TNIL) {
throw std::runtime_error("Class does not exist.");
}
luabind::globals(lua)[name] = luabind::nil;
return oldGlobal;
})
];

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.

When instantiating multiple instances of a lua object when to use require vs new?

I'm trying to wrap my head around the different ways to define and instantiate objects in Lua.
For the current problem, I'm imagining objects as simple as a C-struct (no methods necessary).
Between these two methods below, does it matter which way we choose? Is there a practical difference? I'd like to go for "least code" because I like "less code" best. It seems like the first method has a problem somewhere. Maybe it's just me. What happens if we instantiate 10,000 mary's? Does it matter?
First Way:
mary.lua
return {
name = "mary"
}
main.lua
local r = require("mary")
local s = require("mary")
local t = require("mary")
local u = require("mary")
Second Way:
mary.lua
local _O = {}
function _O:new()
o = {}
setmetatable(o, self)
self.__index = self
o:_create()
return o
end
function _O:_create()
self.name = "mary"
end
return _O
main.lua
local o = require("mary")
local r = o:new()
local s = o:new()
local t = o:new()
local u = o:new()
require is a function for loading modules, not instantiating classes.
require("mary") only loads mary.lua once, then stores the return value inside of package.loaded["mary"]. All subsequent results of require("mary") return the object at package.loaded["mary"], and do not create new instances. This makes require unsuitable for class instantiating.
local r = require("mary")
local s = require("mary")
print(rawequal(r, s)) -- Prints true
r.name = "samantha"
print(s.name) -- Prints samantha
Your second way actually creates new class instances. However, instead of a new class method, it's more familiar to call the class table itself, using the __call metamethod. I've written a one-file OOP framework that handles that and single inheritance; feel free to use it directly or as a reference.

Resources