Lua metatables and metamethod - How to call a different member function - oop

I have the following Class
local PROGRESS = {}
PROGRESS.__index = function(self,key)
if key~="__group" and self.__group[key] then
return self.__group[key]
else
return rawget(self,key)
end
end
What this does is when You access table[key] it performs a lookup in table.__group (which is an object of another class) and returns table.__group[key] ,if it is not nil.
Now I am trying to do the same for member functions.
i.e If I call table:key() a lookup must be performed in table.__group and if the function is present, then table.__group:key() should be called.
How do I accomplish this?
I tried to do this.
local PROGRESS = {}
PROGRESS.__index = function(self,key)
if key~="__group" and self.__group[key] then
local val = self.__group[key]
if type(val) == "function" then
self.__group:val()
return function() end
end
return self.__group[key]
else
return rawget(self,key)
end
end
But there are 2 things wrong here.
I am unable to retrieve the original function's arguments
Event if I just ACCESS table[key].function without calling it, the function will be called
And I've got the feeling that I am trying to complicate things and the solution is way simpler.
Any help is appreciated.
UPDATE
#Mud
The problem with the original code is that the object passed as 'self' to the member function is an object of the new class. Not of the old class.
Consider this code
GROUP_CLASS = {}
GROUP_CLASS.__index = GROUP_CLASS
function GROUP_CLASS:showSum (a,b) print(self);print(a + b) end
group_object = setmetatable({},GROUP_CLASS)
group_object:showSum(1,2)
local PROGRESS_CLASS = {}
PROGRESS_CLASS.__index = function(self,key,value)
if key~="__group" and self.__group[key] then
return self.__group[key]
else
return rawget(self,key)
end
end
progress_object = setmetatable( {__group = group_object} , PROGRESS_CLASS)
progress_object:showSum(3,3)
--progress_object is passed as first argument to showSum. But i need group_object to be passed
In the above code, When progress_object:showSum(3,3) is called,
is it possible to pass group_object (or in other words progress_object.__group) as self instead of progress_object.
Hope that makes sense.

Response to updated post:
progress_object is passed as first argument to showSum. But i need group_object to be passed
If you're going to ignore the state of the object a method is called on, and substitute the state of some other object, why is it even a method on that object? That's like overriding the addition operator to do multiplication, a recipe for confusion.
In other words, you want this:
progress_object:method("foo")
To resolve, via bizarre internal machinery, into this:
group_object:method("foo")
Why not skip a step and just make the latter call?
If you must, you could achieve this by returning a proxy for the method which replaces self with __group
local PROGRESS_CLASS = {}
PROGRESS_CLASS.__index = function(self,key)
local groupval = self.__group[key]
if key == '__group' or not groupval then
return rawget(self,key)
elseif type(groupval) ~= 'function' then
return groupval
else
return function(...)
if self == ... then -- method call
-- replace self argument with __group
return groupval(self.__group,select(2,...))
else
return groupval(...)
end
end
end
end
Response to original post:
How I am trying to do the same for member functions. i.e If I call table:key() a lookup must be performed in table.__group and if the function is present, then table.__group:key() should be called.
How do I accomplish this?
Do nothing. Your original code handles this.
Lua doesn't know what a "member function" is. A member is a member (i.e. an element in a table), and whether the value of that member is a function is irrelevant.
Remember:
obj:method(a,b,c) is exactly equivalent to obj.method(obj,a,b,c)
obj.method is exactly equivalent to obj["method"].
Your code already resolves obj["method"] into obj.__group["method"]
So you're done.
For instance, say we have:
group = {}
function group:showSum (a,b) print(a + b) end
function group:showProduct(a,b) print(a * b) end
Using your first code, we can write:
foo = setmetatable({__group = group}, PROGRESS)
foo:showSum(3,3) -- 6
foo:showProduct(3,3) -- 9
That's it.
Now, as long as we're here, let's look at what your second function is doing:
local val = self.__group[key]
if type(val) == "function" then
self.__group:val()
return function() end
end
First you grab the function value from __group. At this point you're done. Simply return that value, and the caller is going to call that value (i.e. (...)). Instead, you call __group["val"] which is likely a totally different function from __group[key] (unless key=="val"), then you pass the caller a function which does nothing.

Related

Entities inheriting Entity.lua variables, but not its methods

I am building a game with enemies and players, all of which are set up to have various states for animation and behavior. The parent class for both is Entity.lua, from which they inherit variables and methods. However, while both enemies and players are inheriting the variables, for some reason the enemies do not inherit the methods. So, if I try to call snakey:changeState('search'), for example, it gives me an error message "Attempt to call method 'changeState' (a nil value)".
I have used the same sequence for creating entities in several games in the past and never had this problem. In fact, if I create the Player in the same way, file and location as the enemies, I receive no error messages.
Here is the code where I create the entities.
local snakey
snakey = Snakey {
platform = platform,
player1 = self.player1,
player2 = self.player2,
stateMachine = StateMachine {
['search'] = function() return SnakeySearchState(snakey) end,
['chasing'] = function() return SnakeyChasingState(snakey) end,
['idle'] = function() return SnakeyIdleState(snakey) end
}
}
-- snakey:changeState('search')
-- snakey.stateMachine:change('search', params)
table.insert(self.entities, snakey)
The two coded out lines are where I noticed the problem. The first line gives and error and the second does work, but is not satisfactory because it is a work-around.
Here is the code for Entity.lua: I don't include details of the functions for brevity, but all are working properly for when player calls them.
Entity = Class{}
function Entity:init(def)
-- position
self.x = def.x
self.y = def.y
self.gravity = 6
-- many more variables
end
function Entity:changeState(state, params)
self.stateMachine:change(state)
end
function Entity:update(dt)
self.stateMachine:update(dt)
end
function Entity:collides(entity)
-- do something
end
function Entity:onDamage()
-- do something
end
function Entity:render()
- renders sprite
end
Player code (in brief)
Player = Class{__includes = Entity}
function Player:init(def)
Entity.init(self, def)
-- more variables
end
function Player:update(dt)
Entity.update(self, dt)
end
function Player:render()
Entity.render(self)
end
And perhaps the trouble spot, one one enemy's script
Snakey = Class{__includes = Entity}
function Snakey:init(def)
Entity.init(self, def)
-- yet more variables
end
function Snakey:update(dt)
Entity.update(self, dt)
-- entity behavior (works fine, so omitted)
end
function Snakey:render()
Entity.render(self)
end
Thank you very much for your help. I'm feeling quite frustrated because this sequence has worked in the past and I would really like to know why it's not calling those Entity methods.
Adding the class library
--Copyright (c) 2010-2013 Matthias Richter
local function include_helper(to, from, seen)
if from == nil then
return to
elseif type(from) ~= 'table' then
return from
elseif seen[from] then
return seen[from]
end
seen[from] = to
for k,v in pairs(from) do
k = include_helper({}, k, seen) -- keys might also be tables
if to[k] == nil then
to[k] = include_helper({}, v, seen)
end
end
return to
end
-- deeply copies `other' into `class'. keys in `other' that are already
-- defined in `class' are omitted
local function include(class, other)
return include_helper(class, other, {})
end
-- returns a deep copy of `other'
local function clone(other)
return setmetatable(include({}, other), getmetatable(other))
end
local function new(class)
-- mixins
class = class or {} -- class can be nil
local inc = class.__includes or {}
if getmetatable(inc) then inc = {inc} end
for _, other in ipairs(inc) do
if type(other) == "string" then
other = _G[other]
end
include(class, other)
end
-- class implementation
class.__index = class
class.init = class.init or class[1] or function() end
class.include = class.include or include
class.clone = class.clone or clone
-- constructor call
return setmetatable(class, {__call = function(c, ...)
local o = setmetatable({}, c)
o:init(...)
return o
end})
end
-- interface for cross class-system compatibility (see https://github.com/bartbes/Class-Commons).
if class_commons ~= false and not common then
common = {}
function common.class(name, prototype, parent)
return new{__includes = {prototype, parent}}
end
function common.instance(class, ...)
return class(...)
end
end
-- the module
return setmetatable({new = new, include = include, clone = clone},
{__call = function(_,...) return new(...) end})
It turns out that sequence matters. In trying to create the minimum reproducible code I could not reproduce the error. After some searching (and a little frustration), I noticed that in Dependencies.lua I was requiring the enemies BEFORE Entity.lua, but Player.lua was required after. I would have thought this wouldn't matter, since everything was imported into the program on frame 1 and I was creating entities on something like frame 1000, but alas. Anyway, problem solved! Always require parent classes before child classes... Lesson learned. :)

Question about when to use lua colon syntax

local Public = {}
function Public.new(ent)
local State = {}
function State:update(player)
ent:setLinearVelocity(0,0)
end
function State:start(player)
ent.fixedRotation = true
self.attackTimer = _G.m.addTimer(200, function()
ent:setState('attacking', player)
end)
end
function State:exit(player)
ent.fixedRotation = false
timer.cancel(self.attackTimer)
end
return State
end
return Public
I'm using a linter and its complaining that I'm using the colon unnecessarily for my update and exit methods. The reason I do this is to keep all my methods uniform. Sometimes I need self and sometimes I don't.
But in general is there any advantage to using colon on these at all? It seems like if i have something like State:start then I could just reference State directly. I could do State.attackTimer vs self.attackTimer..
Why would you ever really need the colon? If you have access to the table that holds the method then you have access to self.. right?
The : syntax is a great tool when you are making a class using a table and a metatable.
Your code above, rather then creating a class, creates an encapsulated set of functions. which have access to State as an upvalue.
I will use this class from Lua Users - SimpleLuaClasses as an example:
Account = {}
Account.__index = Account
function Account:create(balance)
local acnt = {} -- our new object
setmetatable(acnt,Account) -- make Account handle lookup
acnt.balance = balance -- initialize our object
return acnt
end
function Account:withdraw(amount)
self.balance = self.balance - amount
end
-- create and use an Account
acc = Account:create(1000)
acc:withdraw(100)
Here we have an instance(acc) of the Account class. To adjust or modify the values in this specific instance of Account we can not refer to Account.balance inside of Account:withdraw. We need a reference to the table where the data is stored, and that is where passing that table using : comes in.
acc:withdraw(100) is just syntactic sugar for acc.withdraw(acc, 100) passing in our table as the first param self. When you define Account:withdraw(amount) there is an implicate first variable self the definition could be written as Account.withdraw(self, amount)

Lua reference table inside metatable

I have a pretty mind-bending setup right now. I have a regular function that returns a table with functions in it under keys "string" and "number":
function defGeneric()
local function funcNumber(a)
return 2*a^2
end
local function funcString(a)
return a.." - test"
end
local returnTable={}
returnTable["number"]=funcNumber
returnTable["string"]=funcString
return returnTable
end
And that works fine. But what I want to do now is make the table that this function returns callable. To illustrate, let's say we have v=defGeneric(). Specifically:
If v is called with a string str, return the result of v["string"](str)
If v is called with a number n, return the result of v["number"](n)
This is obviously a job for metatables, so I can (in my function) add the code to set a metatable:
local metaTable = {
__call = function (...) -- "call" event handler
return
end
}
setmetatable(returnTable,metaTable)
But I don't know what I would put after that return statement. I don't think I can reference returnTable, because this table will be called like so:
v=defGeneric()
v("test")
And I need to reference v's "string" function (there certainly could be multiple defGeneric() tables in one program).
I think the answer here might be some self trick but I can't wrap my head around how. How do I reference a metatable's table from the metatable?
The first argument passed to the __call function is the table it is being called on, the table returned from the function in this case. You can use type(a) to get the type of the argument as a string, so you could do something like this:
function defGeneric()
local result = {
['number'] = function(a) return 2*a^2 end,
['string'] = function(a) return a.." - test" end
}
setmetatable(result, {
__call = function(t,a)
local f = t[type(a)]
if f == nil then return "No handler for type "..type(a) end
-- alternate:
-- if f == nil and t['string'] ~= nil then return t['string'](tostring(a)) end
return f(a)
end
})
return result
end
local def = defGeneric()
print("string: "..tostring(def('sample string')))
print("number: "..tostring(def(5)))
print("table: "..tostring(def({})))
print("boolean: "..tostring(def(1 > 5)))
output
string: sample string - test
number: 50.0
table: No handler for type table
boolean: No handler for type boolean
alternate output
string: sample string - test
number: 50.0
table: table: 0x18537e0 - test
boolean: false - test

OOP in Lua - Creating a class?

I'm aware there are a few questions about implementing OOP in Lua on this site, however, this one is a bit different (at least compared to what I found).
I'm trying to create a class called "human", and make it so objects created with the "new" constructor of "human", inherit everything inside human except it's constructor. However, I also don't want to be able to use methods inside of human, on human. So whatever's inside the human class, is only passed to created objects. Here's an example:
-- "Human" class
human = {}
function human.new(name)
local new = {} -- New object
-- Metatable associated with the new object
local newMeta =
{
__index = function(t, k)
local v = human[k] -- Get the value from human
print("Key: ", k)
if type(v) == "function" then -- Takes care of methods
return function(_, ...)
return v(new, ...)
end
else
return v -- Otherwise return the value as it is
end
end
}
-- Defaults
new.Name = name
new.Age = 1
return setmetatable(new, newMeta)
end
-- Methods
function human:printName()
print(self.Name)
end
function human:setAge(new)
self.Age = new
end
-- Create new human called "bob"
-- This works as expected
local bob = human.new("Bob")
print(bob.Name) -- prints 'Bob'
bob:printName() -- prints 'Bob'
bob:setAge(10) -- sets the age to 10
print(bob.Age) -- prints '10'
-- But I don't want something like this allowed:
local other = bob.new("Mike") -- I don't want the constructor passed
-- I'd also like to prevent this from being allowed, for "human" is a class, not an object.
human:printName()
So creating the object with human.new("Bob") works fine, but it also passes the constructor, and I can still use the object methods on the class. I'm very new to the concept of OOP, so I'm sorry if this was a horrible question. But if anyone could help, I'd appreciate that.
I have run into the same issue before. You need two tables. One for object methods and one for class methods. Set the metatable of constructed objects to the object method table. For example:
local Class = {}
local Object = {}
Object.__index = Object
function Class.new()
return setmetatable({}, Object)
end
setmetatable(Class, {__call = Class.new})
function Object.do()
...
end
return Class
And use it
Class = require('Class')
local obj = Class.new() -- this is valid
obj.do() -- this is valid
obj.new() -- this is invalid
Class.do() -- this is invalid

Creating multiple instances of a class object. Data of all objects are equal to the last object created

I have created a small class called Piece, which looks as follows
local Piece = {}
local Piece_mt = { __index = Piece}
function Piece.New(name, img, startPosX, startPosY)
newPiece = {}
newPiece.name = name;
newPiece.img = display.newImage(img, startPosX, startPosY);
print(newPiece.name);
local function OnHit ( event )
if event.phase == "ended" then
print(newPiece.name);
end
end
newPiece.img:addEventListener("touch", OnHit);
return setmetatable( newPiece, Piece_mt )
end
return Piece
When I create the class, it prints the name, and it is correct (matches what I passed as a parameter). However, when I later click the image, and the name is printed as a result of the triggered OnHit function, the name printed is always the name of the last object I created, regardless of which of the objects I click on. Could someone explane why this happens and how to fix it?
function Piece.New(name, img, startPosX, startPosY)
newPiece = {}
-- process
return setmetatable( newPiece, Piece_mt )
end
Here, you are assigning the global variable newPiece and return it every time. Instead, try use a local variable:
function Piece.New(name, img, startPosX, startPosY)
local newPiece = {}
-- process
return setmetatable( newPiece, Piece_mt )
end