LUA Metatable and Classes - lua

local Class = {}
Class.__index = Class
--default implementation
function Class:new() print("Bye!!") end
--create a new Class type from our base class
function Class:derive(type)
local cls = {}
cls.type = type
cls.__index = cls
cls.super = self
setmetatable(cls, self)
return cls
end
function Class:__call(...)
local inst = setmetatable({},self)
inst:new(...)
return inst
end
function Class:get_type()
return self.type
end
--Create 'Player' class
local Player = Class:derive("Player")
function Player:new(name)
print("hello " .. name)
end
--Create player instance
plyr1 = Player('Dylan')
This is a Class module in lua
I have a few questions.
What is the point of Class:new() if it does not do anything?
At Line 19 inst:new(...) in the Class:__call(...) function,why does the program search for the :new function in the Player table and not the original Class table?
Meaning,
When Player("Dylan") is called, "Hello Dylan" is printed instead of "Bye!!"
Isn't the meta table of inst table set to self, which references Class?
Code is taken from youtube video https://www.youtube.com/watch?v=O15GoH7SDn0 about Lua Classes

When doing Player("Bob") we will enter the __call metamethod defined in the metatable of Player which is Class:__call
function Class:__call(...)
local inst = setmetatable({},self)
inst:new(...)
return inst
end
In this call self refers to the Player table not the Class table, so when we create inst we are setting it's metatable to Player which has an __index value that points to itself, so inst:new will be equal to Player.new(inst, name)

Related

Set the meta table for the lua meta table

The __index of the table originally set a meta table, and the actual access is to the function under this meta table.
setmetatable(flatTbl, {__index = metaTbl}
I want to access the function of the same name of the meta table when the field of the table is not accessible, but I have used two methods without success
function FlatBufferTools:SetMeta(flatTbl)
setmetatable(flatTbl, {
__index = function(tbl, key)
metaTbl = getmetatable(tbl).__index
return metaTbl[key](metaTbl)
end
})
end
function FlatBufferTools:SetMeta2(flatTbl)
metaTbl = getmetatable(flatTbl).__index
setmetatable(metaTbl, {
__index = function(tbl, key)
return tbl[key](tbl)
end
})
end
The first method is to reset the __index of the table, but the metaTbl that i get is a function
The second method is to set __index to the table's meta table(metaTbl), but the setmetatable function skips it
I checked the metaTbl and there is no __metatable
I want to access the function of the same name of the meta table when
the field of the table is not accessible
local meta = { myFunc = function () print("metatable here") end }
meta.__index = meta
local a = setmetatable({}, meta)
a.myFunc()
a.myFunc is nil so you'll call meta.myFunc
You know the table library?
Lets make the table functions "not accessible" to a and raise them to methods...
> a = setmetatable({}, {__index = table})
> for i = 1, 10 do a:insert(i) end
> print(a:concat('\n'))
1
2
3
4
5
6
7
8
9
10
>

How to inherit method?

I want to inherit method "GetName" or other methods from "Create" for "CreateInherited" and i want save unique methods from "CreateInherited" (like "GetInheritName"), but i dont know how.
My test code:
local MainTbl = {}
function MainTbl:Create(name)
local tbl = {}
tbl.name = name or 'Null'
function tbl:GetName()
return self.name
end
setmetatable(tbl, self)
self.__index = self
return tbl
end
function MainTbl:CreateInherited(name)
local tbl = {}
tbl.name = name or 'Null'
function tbl:GetInheritName()
return self.name
end
setmetatable(tbl, self)
self.__index = self
return tbl
end
local Man = MainTbl:Create('Man')
local Woman = MainTbl:CreateInherited('Woman')
print(Man:GetName())
print(Woman:GetName())
print(Woman:GetInheritName())
If I understand you, you're trying to put two different constructors into a single class. Notice that Create does most of the work that CreateInherited needs to do, so you can save yourself a lot of repeated code by calling Create inside CreateInherited. Instead of starting with an empty table, you can start with a fully formed instance from Create and add a method to it.
function MainTbl:CreateInherited(name)
local tbl = self:Create(name)
function tbl:GetInheritName()
return self.name
end
return tbl
end

How to use OO in Lua Corona Labs? Unable to index error

Im using the Corona SDK for developing games and created a new project that comes with a main.lua file, but wanted to add other seperate files such as player.lua so i could do object oriented.
My goal is to create a player from the main and i did some research on how that can be done.
a link to lua tutorial
Here is my code for those files:
player.lua :
Player = {}
Player.new = function(name, id)
local self = {}
name = name or "player"
id = id or 0
self.getName = function() return name end
self.getId = function() return id end
end
return self
main.lua :
local Player = require("scripts.player")
player1 = Player.new("Player1", 1)
print(player1.getName())
Im expecting a print in the console. The error says 'unable to index local Player (a boolean value) stack traceback' in main.lua
You do not return your player lib in player.lua. so when you call
local Player = require("scripts.player")
You shade the global variable Player created in player.lua with the result of require which is true.
References on Require: https://www.lua.org/manual/5.3/manual.html#6.3
You have 2 choices to correct this issue.
Option 1)
change player.lua
local Player = {}
Player.new = function(name, id)
local self = {}
name = name or "player"
id = id or 0
self.getName = function() return name end
self.getId = function() return id end
return self
end
return Player
OR Option 2) Change main.lua
require("scripts.player")
player1 = Player.new("Player1", 1)
print(player1.getName())
Option one follows more modern Lua module conventions, but either option will resolve your issue.
The first issue is that you have the return statement for constructor in the wrong place. It should be inside the constructor rather than outside:
Player = {}
Player.new = function(name, id)
local self = {}
name = name or "player"
id = id or 0
self.getName = function() return name end
self.getId = function() return id end
return self
end
Indenting your code consistently will help you see such problems straight away. I suggest always having end indented at the same level as the opening of the block (no matter if it is a function, for, do or whatever else).
After solving this issue you have the problem mentioned by Nifim - you need to take care of shadowing the Player. The simplest solution would be to add a return statement in the end of the player.lua:
Player = {}
-- `Player.new` and so on...
return Player
You could also make the Player local if you want. It is not needed but it might be desired.
Or you could remove the assignment from the main.lua:
require("scripts.player")
local player = Player.new()

How to change a table's metatable but inherit it's own methods in Lua

in Lua we do OO programming like this:
MyClass = {}
function MyClass:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
obj.hello = "hello world"
return obj
end
function MyClass:sayHi()
print(self.hello)
end
function main()
local obj = MyClass:new()
obj:sayHi()
end
When working with more compelx stuff, usually I take advantage of Lua's metamethods to proxy function calls and do whatever I need with it, like arguments parsing etc, by adding this:
MyClassMeta = {}
function MyClassMeta.__index(obj, funcName)
return function (self, ...)
//do some stuff
print("you called " .. funcName .. " with args", ...)
end
end
and changing the line:
setmetatable(obj, self)
to:
setmetatable(obj, MyClassMeta)
every function I call with an instance of MyClass will execute the code implemented in MyClassMeta.__index metamethod.
What I want to do now is inherit the MyClass existing methods, and execute MyClassMeta.__index only for functions that are not part of MyClass.
In the above example, the code will always execute the MyClassMeta.__index metamethod even when calling MyClass:sayHi():
function main()
local obj = MyClass:new()
obj:sayHi("hello")
end
you called sayHi with args hello
When you set __index to a table, it will look for properties on that table and return them if they don't exist on the instance. Since sayHi exists on the MyClass table it is used.
self.__index = self
When you set __index to a function, it can return anything for properties that don't exist on the instance. You can check if the key exists on the MyClass table and return it, then do something else if it doesn't:
MyClass = {}
MyMetatable = {
__index = function(obj, key)
if MyClass[key] ~= nil then return MyClass[key] end
return function(self, ...)
print("you called "..tostring(key))
print(" self.hello is '"..tostring(self.hello).."'")
print(" with args", ...)
end
end
}
function MyClass:new()
local obj = {}
setmetatable(obj, MyMetatable)
obj.hello = "hello world"
return obj
end
function MyClass:sayHi()
print(self.hello)
end
function main()
local obj = MyClass:new()
obj:sayHi()
end
local obj = MyClass:new()
obj:sayHi("hello")
obj:somethingElse(1, 2, 3)
Version with Egor's comment
MyClass = {}
setmetatable(MyClass, {
-- if it's not found on MyClass, return a function
__index = function(self, funcName)
return function(self, ...)
print("you called "..funcName.." with args", ...)
end
end
})
function MyClass:new()
local obj = {}
-- if it's not found on obj, try self (MyClass)
setmetatable(obj, { __index = self })
obj.hello = "hello world"
return obj
end
function MyClass:sayHi()
print(self.hello)
end
local obj = MyClass:new()
obj:sayHi()
obj:somethingElse(1, 2, 3)
When creating an object this sets the __index of the new object's metatable to MyClass, and MyClass's metatable's index to the function that is a fallback. So if the property isn't on your object or on MyClass, it will use the fallback.

why doesnt my lua class implementation work?

I have implemented OOP in my lua environment but it doesnt seem to be working.
I think it has something to do with how i am handling the __index and my improper use of require and module but I'm not 100% sure.
Here is the code:
Class = function( prototype )
local derived = {}
local derivedMT = {
--When indexing into a derived class, check the base class as well
__index = prototype,
--When invoking a class, return an instance
__call = function( proto, ... )
local instance = {}
local instanceMT = {
--When indexing into an instance, check the class hierarchy as well.
__index = derived,
--Calling instances is a no-no!
__call = function()
print( "WARNING! Attempt to invoke an instance of a class!" )
print( debug.traceback() )
return instance
end,
}
setmetatable( instance, instanceMT )
if ( instance.__constructor ) then
instance:__constructor( ... )
end
return instance
end,
}
setmetatable( derived, derivedMT )
return derived
end
And here is how I use it, the nil reference is a call to a base class function, that is the error/problem im having is it seems like the base class isn't being referenced.
require "Core.Camera.FreeCamera"
local T = Core.Camera.FreeCamera.FreeCamera(0,0,-35)
c = T:getObjectType() -- nil reference
print(c .." --Type" )
and here is Camera.lua the base class
local _G = _G
module(...)
local M = _G.Class()
Camera = M
function M:__constructor(x,y,z) --This never gets called.
--self.Active = false
--self.x = x
--self.y = y
--self.z = z
--self.ID = EngineManager:getCamera()
print("InCameraInstance_-_-_-_-_-__-_--_-__-_-_-_--_-_-_-_-_--_-_-_--_--__-_---_--_---__-")
end
function M:getObjectType()
return "camera"
end
And Finally Free Camera which attempts to inherit Camera.
local require = require
local _G = _G
module(...)
require "Core.Camera.Camera"
local M = _G.Class( _G.Core.Camera.Camera ) --Gross, lame might be the culprit
FreeCamera = M
function M:__constructor(x,y,z) ---WHOOPS, this does get called... the other one doesnt
self.Active = false
self.x = x
self.y = y
self.z = z
self.ID = _G.EngineManager:getCamera()
--_G.print("got Id of:" .. self.ID)
self:setCameraPosition(x, y, z, self.ID)
_G.print("<<<Camera in lua>>>")
end
I'm running out of ideas. any help would be appreciated.
Instead of:
local M = _G.Class( _G.Core.Camera.Camera )
you need to have:
local M = _G.Class( _G.Core.Camera.Camera.Camera )
First Camera is the directory name, second is the module name, third is the class name.
Edit: You can clean it up a bit like this:
local CameraModule = require "Core.Camera.Camera"
local M = _G.Class( CameraModule.Camera )

Resources