Object inheritance

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Object inheritance

Post by ivan »

I'll put it to you this way, if your examples managed to confuse both myself and pgimeno - it doesn't speak good for the code.
Basically you're doing a simple __index fallback to another table.
It won't work for more than one level of inheritance, so you can't make a subclass of "subclass".
User avatar
4vZEROv
Party member
Posts: 126
Joined: Wed Jan 02, 2019 8:44 pm

Re: Object inheritance

Post by 4vZEROv »

What I use :

class.lua

Code: Select all

local Class = {}
function Class:new() end
function Class:extend() local obj = {} obj.__call, obj.__index, obj.super = self.__call, obj, self return setmetatable(obj, self) end
function Class:__index(v) return Class[v] end
function Class:__call(...) local obj = setmetatable({}, self) obj:new(...) return obj end
return Class
vehicle.lua

Code: Select all

Vehicle = Class:extend()
function Vehicle:new(nb_of_wheels, name)
	self.nb_of_wheels = nb_of_wheels
	self.name = name
end
function Vehicle:update(dt) print(self.name, self.nb_of_wheels) end
function Vehicle:draw() love.graphics.rectangle("line", 10, 10, 10, 10) end
car.lua

Code: Select all

Car= Vehicle:extend()
function Car:new(name) self.super.new(self, 4, name) end
function Car:update(dt) self.super.update(self, dt) end
function Car:draw() self.super.draw() end
main.lua

Code: Select all

function love.load()
	require("class")
	require("vehicle")
	require("car")
	
	my_car = Car("Ferrari")
end
function love.update(dt) my_car:update(dt) end
function love.draw() my_car:draw() end
User avatar
pgimeno
Party member
Posts: 3548
Joined: Sun Oct 18, 2015 2:58 pm

Re: Object inheritance

Post by pgimeno »

To clarify, I was confused by Schwender.exe's code, not by TheHUG's code, especially regarding initialization of members. It made me think that Player was an instance, not a class.
TheHUG
Citizen
Posts: 61
Joined: Sun Apr 01, 2018 4:21 pm

Re: Object inheritance

Post by TheHUG »

ivan wrote: Mon Jul 08, 2019 1:44 pm I'll put it to you this way, if your examples managed to confuse both myself and pgimeno - it doesn't speak good for the code.
Basically you're doing a simple __index fallback to another table.
It won't work for more than one level of inheritance, so you can't make a subclass of "subclass".
Of course you can, anything created with subobject:new will have subobject as it's metatable,and subobject __index attribute will be itself. You can then use that object as a subclass, defining new methods for it, which anything made with subsubobject:new will inherit.

It was the first result back when I googled 'classes in lua' :https://www.lua.org/pil/16.1.html . it's seems to be a fairly standard way to do it, and I still prefer it since it's simple and explicit.
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Object inheritance

Post by ivan »

Oops sorry pgimeno was talking about somebody else. And no Lua doesn't have classes or instances.
I don't follow your explanation so you have to show me a code example with more than one layer of inheritance. :)
User avatar
zorg
Party member
Posts: 3441
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Object inheritance

Post by zorg »

Does this qualify for multiple inheritance? I typed it up and tested it in 5 mins:

Code: Select all

-- "class" A
local A = {} -- "methods/members" for "class instances"
A.bar = function() print "A!" end -- example of above
local mtA = {__index = A} -- one metatable to make instances access these methods
local newA = function() return setmetatable({}, mtA) end -- set instance to access class methods/members if it itself doesn't contain such a key.

-- "class" B
local B = {}
B.baz = function() print "B!" end
local mtB = {__index = B}
setmetatable(B, mtA) -- set class B's method/member table to have class A's method/member table as a fallback.
local newB = function() return setmetatable({}, mtB) end

local foo
foo = newA()
foo:bar()

foo = newB()
foo:baz()
foo:bar() -- method "bar" in class "A" called through fallback index metamethod defined on class "B"'s own methodlist
-- This can be extended to more than one "round", e.g. an instance of class C falling back to class B falling back to class A's
-- class methods or members.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Object inheritance

Post by ivan »

Looks fine and yes this would work thanks to the following line:

Code: Select all

setmetatable(B, mtA) -- set class B's method/member table to have class A's method/member table as a fallback.
So you can have as many layers of inheritance as you want as long as the metatables fallback sequentially to the parent object.
I would call this "layers of inheritance" since "multiple inheritance" usually refers to something different. :)
User avatar
zorg
Party member
Posts: 3441
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Object inheritance

Post by zorg »

Yeah, the diamond of death thing; i guess even that could be possible, depending on whether you can set __index to be a function and check multiple "parent" tables defined by the class itself.. but then you'd probably also want a way to programatically set which other "classes" one should search for potentially missing members/methods, the order of the definitions mattering and such. (or you could just hard-code it)
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Object inheritance

Post by ivan »

Never had any use for multiple inheritance myself. But simple inheritance like in your code example can be very useful.
TheHUG
Citizen
Posts: 61
Joined: Sun Apr 01, 2018 4:21 pm

Re: Object inheritance

Post by TheHUG »

An example of more levels of inheritance would be:

Code: Select all

Object = {}
function Object:new(x, y)
     local o = {x=x, y=y}
     setmetatable(o, self)
     self.__index = self
     return o
end

Collider = Object:new() -- Collider inherits Object's :new method

function Collider:collide(...)
	...
end

a_collider = Collider:new() -- an instance of Collider

BouncingCollider = Collider:new(new_default_an_arg) --  creates BouncingCollider and sets its metatable to Collider, inheriting :new and :collide

--override :collide
function BouncingCollider:collide(...)
	--bounce and then
	getmetatable(self).collide(self, ...)
end

a_bouncing_collider = BouncingCollider:new() -- works

internally when BouncingCollider:new() is called it finds that BouncingCollider.new == nil, so it looks in getmetatable(BouncingCollider).__index (i.e. Collider) for .new. Since that is also nil, it will check getmetatable(Collider).__index (i.e. Object) and find the .new function there.

There are several variations you can do, including some where the boundary between instance and class is clearer and/or enforceable, though I don't really see the need. That's one advantage of being explicit - it preserves customizability so you can code with your preference.

One variation I used for a while was to leave out self.__index = self in :new and specifically set the metatable and __index for each subclass, such that :new would only work when invoked on the class, not on an instance, but I ended up liking this better, they're all just tables after all.
Post Reply

Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 51 guests