OOP Question, Inheritance hierarchy - dmc?

So I’m trying to port something across from Mat Buckland’s excellent (and thoroughly recommended) Game AI by Example. All of the code is written in C++, which hasn’t been an issue until now.

Essentially I’m trying to port the triggering example. The class hierarchy is as follows:

BaseGameEntity

  |

Trigger
  |

Trigger_LimitedLifeTime

  |

Trigger_SoundNotify

As far as I can tell this isn’t multiple inheritance, but merely a hierarchy of inheritance so I’ve just employed the standard lua OOP approach:

main.lua

[lua]

local soundTrigger =  Trigger_SoundNotify.new(1)

[/lua]

[lua]

Trigger_SoundNotify.new = function(source, range)

    local o = Trigger_LimitedLifeTime:new(8)

    o.m_pSoundSource = source

    o:AddCircularTriggerRegion(o:Pos(), o:BRadius())

    setmetatable(o, Trigger_SoundNotify_mt)

    return o

    

end

[/lua]

[lua]

Trigger_LimitedLifeTime.new = function(lifetime)

    local o = Trigger.new(BaseGameEntity:GetNextValidID())

    –

    o.m_iLifetime = lifetime

    

    setmetatable(o, Trigger_LimitedLifeTime_mt)

    return o

end

[/lua]

[lua]

local Trigger = {}

local Trigger_mt = {__index=Trigger}

…class method declarations…

Trigger.new = function(id)

    local o = BaseGameEntity:new(id)

    

    o.m_pRegionOfInfluence = TriggerRegion:new()

    o.m_bRemoveFromGame = false

    o.m_bActive = false

    --GraphNodeIndex is useful to hook into pathfinding, so AI can discover triggers

    o.m_iGraphNodeIndex = -1

    

    setmetatable(o, Trigger_mt)

    return o

return Trigger

[/lua]

[lua]

function BaseGameEntity:new(id)

    local o = o or {}

    

    self.m_dBoundingBoxRadius = 0

    self.m_vPositon = {3,2}

    self.m_vScale = {1,1} --Vector2D

    self.m_iType = default_entity_type

    self.m_bTag = false

    self:SetID(id)

    

    setmetatable(o, self)

    self.__index = self

    

    

    

    return self

end

[/lua]

The problem that I’m facing is that ultimately not all of the various class methods are being returned to Trigger_SoundNotify. I’ve noticed all of those attached to BaseGameEntity are there, however when I try to call a method from Trigger (e.g :AddCircularTriggeRegion) I’m getting an “attempt to call method ‘’ (a nil value) error”

I suspect this is because I’m attempting to set the metatable (__index) repeatedly as I progress down the hierarchy, and I assume you can only do this once; i.e it set when it gets down to the BaseGameEntity class, and subsequent attempts silently fail?

If anybody could confirm this or perhaps shed some further light on the subject then that would be really appreciated.

Just to add a little further detail in case it helps, I’ve noticed that at the top level, i.e the object in main.lua, has all of the relevant inherited properties from each of the inherited classes; however the methods are just not available?!?

hey SegaBoy,

the issue you’re running into stems from the way you have your hierarchy setup. i think the biggest change to get what you have working is on the metatables.
(some of this is written for others who might see this later)

you have only shown the metatable setup on Trigger, but i’m assuming the rest are the same, ie, the metatable refers to the class itself. so the first thing is the only metatable which should refer to itself is the LAST one, BaseGameEntity (even then i’m not sure that’s necessary.)

  1. metatable for static classes

the metatable __index entry refers to the **next** place to do property/method lookup if lookup *fails* on the current object. of course, the __index value typically is set to the object’s parent.

since every other metatable in the chain should refer to its parent, your setup should look something like this:

local BaseGameEntity = {} local Trigger = {} local Trigger\_mt = {\_\_index=BaseGameEntity} Trigger\_LimitedLifeTime = {} Trigger\_LimitedLifeTime\_mt = {\_\_index=Trigger} Trigger\_SoundNotify = {} Trigger\_SoundNotify\_mt = {\_\_index=Trigger\_LimitedLifeTime}

this will enable proper property/method lookup between classes.

  1. metatable for dynamic objects

because a new object doesn’t have any methods on it, **its** metatable should reference its **class**, not the class’s metatable, else that object will skip property/method lookup on its own class.

for example, in Trigger_LimitedLifeTime.new()

[lua]setmetatable(o, {__index=Trigger_LimitedLifeTime})[/lua]

these two seemingly different scenarios are the same, but sometimes the distinction in Lua gets lost because we might think classes and objects are two separate things when in actuality there is no difference between the two. that is, any object can immediately be used as a “class” of another object just by modifying the second object’s metatable.

i want to mention that you don’t have to think too about this distinction if you would just switch to using your new() methods to create the hierarchy instead of using raw objects. for example:

instead of this:

local Trigger\_LimitedLifeTime = {} Trigger\_LimitedLifeTime\_mt = {\_\_index=Trigger}

do this:

[lua]local Trigger_LimitedLifeTime = Trigger.new()[/lua]

(this is an example of “any object can immediately be used as a class of another object” :slight_smile: and as a bonus your metatable has already been set for you. )

other tips:

** the ‘self’ stuff in BaseGameEntity seems redundant because ‘self’ is always BaseGameEntity, so it’s just being reset over and over again. it seems like some of that should/could be your object ‘o’.
if values like m_vPosition are static class values which never change, then they could be placed on BaseGameEntity *outside* of the method new().

** when calling class methods inside of new()/init, e.g., SetID() in BaseGameEntity, AddCircularTriggerRegion() in Trigger_SoundNotify, they could be called after the setmetable() so that the call works in all instances (or the first thing you could do in new() is just set the metatable then do all property/method setup)
so with this simple change, o:SetID(id) would work in BaseGameEntity.new() and would be on the proper object instance.

i hope that helps. let me know you have other questions.

cheers,
dmc

i was just thinking, using ‘dmc’ in the thread title is like my Bat Signal. LOL

Yeah - although glad I had a line to the Bat Phone as well :slight_smile:

I sincerely appreciate both your expertise and time on this matter. I’m currently digesting everything you’ve said and will hit the code soon. Porting this from C++ hasn’t been too much of an issue, however getting the correct setup was really hitting the brick wall - so thanks again.

I’ll keep you informed of the progress, but just wanted to say thanks as soon as I could - massive respect.

Hey dmc, still having some issues here.
 
I’ve changed the way in which classes reference one another - as you pointed out in step 1. I’m a little confused as to how the class e.g:

local Trigger={}

references

local Trigger_mt={__index=BaseGameEntity}.

Before I was mistakingly setting the object/instance to the class mt via:

 
setmetatable(o, Trigger_mt)
 
But now I haven’t got anything that is linking the two tables listed above.
 
i.e my Trigger class is now like this:
 
[lua]
local Trigger = {}
local Trigger_mt = {__index=BaseGameEntity}
 
…class method declarations…
 

function Trigger.new(id)    
    local o = BaseGameEntity:new(id)
    --o:Pos()
    o.m_pRegionOfInfluence = TriggerRegion:new()
    o.m_bRemoveFromGame = false
    o.m_bActive = false
    --GraphNodeIndex is useful to hook into pathfinding, so AI can discover triggers
    o.m_iGraphNodeIndex = -1
    
    setmetatable(o, {__index=Trigger})
  

    o:SetID(1) – **CRASH***

    return o
end
 
return Trigger
[/lua]

It’s crashing when I try to call SetID on the object in the above constructor function - that method should have been attached to the object during the previous stage - BaseGameEntity.

I’m thinking that rather then creating a new table (Trigger_mt) I need to set the __index property of Trigger?

The other thing that is bugging me is that the object is passed through the hierarchical chain - and I’m just a little concerned that I’m constantly resetting the metatable of this object as it’s passed up the chain. i.e the object that’s created in Trigger.new is derived from BaseGameEntity - where it’s metatable was set, and then we’re setting the metatable again in Trigger.new(). 

Is there any concern with the metatable of the object being “reset” during each stage of the hierarchy? Or am I reading this wrong?

Once again, as always, I appreciate all of your time and effort on this…

hi SegaBoy,

there is no issue about the object passing through the hierarchical chain as long as you’re careful. though i don’t do that in dmc_objects – there i setup the hierarchy with the new() as i mentioned before and then let the initialization methods handle putting properties/calling methods on the new objects. since this is done after the hierarchy is setup, devs don’t have to worry about methods not being available, etc.

you can reset a metatable as many times as you wish, however, you have to be careful with that as well if there are other properties in the metatable – eg, http://www.lua.org/manual/5.2/manual.html#2.4. though in this instance i think we’re ok. :slight_smile:

is it possible to send me your code ? it’s hard to get a full picture seeing just snippets. send me a message on the Bat Phone. LOL

cheers, dmc

hey again,

i found this old forum thread where i wrote up a simple code example of multi-level OO inheritance, maybe it’ll help.

https://developer.coronalabs.com/forum/2013/02/02/classes-and-inheritance

cheers,
dmc

Hey dmc, I’ll send over the files - to be honest they’re all pretty much stubs at this stage, as I just want to ensure the inheritance is working correctly before worrying about implementation.

I have, as always, been studying dmc_objects - I tried to implement that library, but also wanted to ensure I knew what was happening under the hood, rather then relying on a third-party library and not necessarily knowing what/why happens.

That said I did notice this, where a chap is explaining to Omid (who produced a handy guide on OOP in Corona). He critiques Omid’s original implementation quite heavily, which resulted in Omid redesigning his implementation and guide. I set it up with the class() library that he provided and all seems to work.

http://www.reddit.com/r/learnprogramming/comments/17300n/ooplua_tutorials_for_corona_mobile_dev/

Can I just ask you one more thing: the approach you mentioned above about using new() to setup the hierarchy - I’m just a little concerned that this could lead to a bloated object? Am I right in saying this? Would linking metatables with a __newindex property reduce this? I realise that at the moment I didn’t have anything to concern myself, after all each of the classes are just a handful of table properties.

I’ll send the files over and see what you think.

As always dmc, it’s always a pleasure - appreciate the time and effort you’re putting into this. 

Actually not too happy about the class() implementation as it seems I need to track the ‘super’ variables that it creates; i.e if I’m calling a method that has been declared on the object at the base of the hierarchy chain I need to call:

self.m_Triggers[i].super.super:isToBeRemoved()

(self.m_Triggers is an array of created Triggers)

That’s a bit of a pain to have to keep track - am I wrong in assuming that metatables would have handled this - I just call the method and if it’s not found on the object at the current level, it would pass up the chain?

One step forwards; two steps back :slight_smile:

Inside the class you would just call Super.<FUNCTION>

Provided you already did:

local Super = require (‘base_class’)

You should not be trying to call supers of objects parents.

For example, if you create

Object

RedObject inherited from Object

RoundRedObject inherited from RedObject

RoundRedObject should not be calling super trying to get to Object.  You would call super to get RedObject’s function, that then calls super to get Objects.  If you need to get to Object’s function by bypassing RedObject, then you likely inherited from the wrong parent.

Super should not be called outside of the Class Definition either.

hi SegaBoy,

i checked out the link from reddit. unfortunately, the posted URLs to the original OO tutorials no longer work. however, based on what ErikTboneJackson mentioned, Omid’s original implementation seemed pretty messy and fragile. what Omid has now is better, but seems like a pretty standard method of OO in Lua – it’s really basic. that last point is what i find when looking at most of the more recent implementations/tutorials – they all get the basics down, but don’t tend to move much past that.

the implementation in dmc_objects came from reading many of the same OO articles on the main Lua website (et al) that everyone eventually finds. i saw that they all share the common thread (using the metatable) and that the differences are fairly minimal (eg, what to use as method names, minor features, etc). so i chose elements from each solution that i liked to create what i have. i didn’t change a lot because i don’t believe in re-inventing the wheel if someone has already figured it out for me :slight_smile: plus, at the time, Corona/Lua were new to me so my Lua OO was weak ; a Jedi i was not.

**my** specific OO implementation differs in that i did add other features beyond the basic OO stuff – some of them geared towards Corona development itself. for example, by default i use a Corona display group to back the Lua object which automatically gives 1. a visual tie into the display hierarchy as well as 2. the ability to dispatch events from the object. i also tweaked table/object property lookup so that i could have the capability of using getters/setters. i think that these features are important for a robust, full-fledged OO solution in Corona and, as much as possible, allow you to use the objects as as you would any native Corona object.

as far as your question about bloated-ness

i wouldn’t worry about it too much for several reasons.

  1. the way OO works in Lua (and Javascript for that matter) require you to create objects to form the hierarchy, so it’s already less efficient. that’s just the nature of the solution which the language gives us.

  2. as you mentioned, typically you only have a handful of properties on each object so memory requirements are really low. plus we’re only talking about 3 objects (for example) to form a hierarchy, but those 3 objects might serve as templates (parent classes) for 10-100s of objects. to me that’s a pretty good ROI.

  3. having those properties on the parent objects allows a hierarchical lookup to succeed. for instance, maybe you have a default class velocity, or number of lives, etc. if you remove (nil) a property on a object, the lookup will go up the hierarchy searching for it. that is what OO is all about, right ? :slight_smile:

that said, i should mention that i **have** recently made a modification in dmc_objects, for reasons related to your question, which separates out different stages of object creation. this update allows me to determine the difference between 1. the objects which you actually are using in the app as opposed to 2. those which are created for the hierarchy, then tweak the initializations for each. however, i personally decided to keep property creation on objects because of #3 above. the driver behind the change was more about the visual elements, not object properties.

finally, using __newindex would be a very deep, complex solution to what i think is a minor issue (as i have stated). i used __newindex and its related posse to power dmc_autostore and dmc_objects,… and let me tell you, you’ll run into deep voodoo while walking down that path. :slight_smile:

cheers,
dmc

ps, i appreciate your appreciation. :slight_smile:

Just to add a little further detail in case it helps, I’ve noticed that at the top level, i.e the object in main.lua, has all of the relevant inherited properties from each of the inherited classes; however the methods are just not available?!?

hey SegaBoy,

the issue you’re running into stems from the way you have your hierarchy setup. i think the biggest change to get what you have working is on the metatables.
(some of this is written for others who might see this later)

you have only shown the metatable setup on Trigger, but i’m assuming the rest are the same, ie, the metatable refers to the class itself. so the first thing is the only metatable which should refer to itself is the LAST one, BaseGameEntity (even then i’m not sure that’s necessary.)

  1. metatable for static classes

the metatable __index entry refers to the **next** place to do property/method lookup if lookup *fails* on the current object. of course, the __index value typically is set to the object’s parent.

since every other metatable in the chain should refer to its parent, your setup should look something like this:

local BaseGameEntity = {} local Trigger = {} local Trigger\_mt = {\_\_index=BaseGameEntity} Trigger\_LimitedLifeTime = {} Trigger\_LimitedLifeTime\_mt = {\_\_index=Trigger} Trigger\_SoundNotify = {} Trigger\_SoundNotify\_mt = {\_\_index=Trigger\_LimitedLifeTime}

this will enable proper property/method lookup between classes.

  1. metatable for dynamic objects

because a new object doesn’t have any methods on it, **its** metatable should reference its **class**, not the class’s metatable, else that object will skip property/method lookup on its own class.

for example, in Trigger_LimitedLifeTime.new()

[lua]setmetatable(o, {__index=Trigger_LimitedLifeTime})[/lua]

these two seemingly different scenarios are the same, but sometimes the distinction in Lua gets lost because we might think classes and objects are two separate things when in actuality there is no difference between the two. that is, any object can immediately be used as a “class” of another object just by modifying the second object’s metatable.

i want to mention that you don’t have to think too about this distinction if you would just switch to using your new() methods to create the hierarchy instead of using raw objects. for example:

instead of this:

local Trigger\_LimitedLifeTime = {} Trigger\_LimitedLifeTime\_mt = {\_\_index=Trigger}

do this:

[lua]local Trigger_LimitedLifeTime = Trigger.new()[/lua]

(this is an example of “any object can immediately be used as a class of another object” :slight_smile: and as a bonus your metatable has already been set for you. )

other tips:

** the ‘self’ stuff in BaseGameEntity seems redundant because ‘self’ is always BaseGameEntity, so it’s just being reset over and over again. it seems like some of that should/could be your object ‘o’.
if values like m_vPosition are static class values which never change, then they could be placed on BaseGameEntity *outside* of the method new().

** when calling class methods inside of new()/init, e.g., SetID() in BaseGameEntity, AddCircularTriggerRegion() in Trigger_SoundNotify, they could be called after the setmetable() so that the call works in all instances (or the first thing you could do in new() is just set the metatable then do all property/method setup)
so with this simple change, o:SetID(id) would work in BaseGameEntity.new() and would be on the proper object instance.

i hope that helps. let me know you have other questions.

cheers,
dmc

i was just thinking, using ‘dmc’ in the thread title is like my Bat Signal. LOL

Yeah - although glad I had a line to the Bat Phone as well :slight_smile:

I sincerely appreciate both your expertise and time on this matter. I’m currently digesting everything you’ve said and will hit the code soon. Porting this from C++ hasn’t been too much of an issue, however getting the correct setup was really hitting the brick wall - so thanks again.

I’ll keep you informed of the progress, but just wanted to say thanks as soon as I could - massive respect.

Hey dmc, still having some issues here.
 
I’ve changed the way in which classes reference one another - as you pointed out in step 1. I’m a little confused as to how the class e.g:

local Trigger={}

references

local Trigger_mt={__index=BaseGameEntity}.

Before I was mistakingly setting the object/instance to the class mt via:

 
setmetatable(o, Trigger_mt)
 
But now I haven’t got anything that is linking the two tables listed above.
 
i.e my Trigger class is now like this:
 
[lua]
local Trigger = {}
local Trigger_mt = {__index=BaseGameEntity}
 
…class method declarations…
 

function Trigger.new(id)    
    local o = BaseGameEntity:new(id)
    --o:Pos()
    o.m_pRegionOfInfluence = TriggerRegion:new()
    o.m_bRemoveFromGame = false
    o.m_bActive = false
    --GraphNodeIndex is useful to hook into pathfinding, so AI can discover triggers
    o.m_iGraphNodeIndex = -1
    
    setmetatable(o, {__index=Trigger})
  

    o:SetID(1) – **CRASH***

    return o
end
 
return Trigger
[/lua]

It’s crashing when I try to call SetID on the object in the above constructor function - that method should have been attached to the object during the previous stage - BaseGameEntity.

I’m thinking that rather then creating a new table (Trigger_mt) I need to set the __index property of Trigger?

The other thing that is bugging me is that the object is passed through the hierarchical chain - and I’m just a little concerned that I’m constantly resetting the metatable of this object as it’s passed up the chain. i.e the object that’s created in Trigger.new is derived from BaseGameEntity - where it’s metatable was set, and then we’re setting the metatable again in Trigger.new(). 

Is there any concern with the metatable of the object being “reset” during each stage of the hierarchy? Or am I reading this wrong?

Once again, as always, I appreciate all of your time and effort on this…

hi SegaBoy,

there is no issue about the object passing through the hierarchical chain as long as you’re careful. though i don’t do that in dmc_objects – there i setup the hierarchy with the new() as i mentioned before and then let the initialization methods handle putting properties/calling methods on the new objects. since this is done after the hierarchy is setup, devs don’t have to worry about methods not being available, etc.

you can reset a metatable as many times as you wish, however, you have to be careful with that as well if there are other properties in the metatable – eg, http://www.lua.org/manual/5.2/manual.html#2.4. though in this instance i think we’re ok. :slight_smile:

is it possible to send me your code ? it’s hard to get a full picture seeing just snippets. send me a message on the Bat Phone. LOL

cheers, dmc

hey again,

i found this old forum thread where i wrote up a simple code example of multi-level OO inheritance, maybe it’ll help.

https://developer.coronalabs.com/forum/2013/02/02/classes-and-inheritance

cheers,
dmc

Hey dmc, I’ll send over the files - to be honest they’re all pretty much stubs at this stage, as I just want to ensure the inheritance is working correctly before worrying about implementation.

I have, as always, been studying dmc_objects - I tried to implement that library, but also wanted to ensure I knew what was happening under the hood, rather then relying on a third-party library and not necessarily knowing what/why happens.

That said I did notice this, where a chap is explaining to Omid (who produced a handy guide on OOP in Corona). He critiques Omid’s original implementation quite heavily, which resulted in Omid redesigning his implementation and guide. I set it up with the class() library that he provided and all seems to work.

http://www.reddit.com/r/learnprogramming/comments/17300n/ooplua_tutorials_for_corona_mobile_dev/

Can I just ask you one more thing: the approach you mentioned above about using new() to setup the hierarchy - I’m just a little concerned that this could lead to a bloated object? Am I right in saying this? Would linking metatables with a __newindex property reduce this? I realise that at the moment I didn’t have anything to concern myself, after all each of the classes are just a handful of table properties.

I’ll send the files over and see what you think.

As always dmc, it’s always a pleasure - appreciate the time and effort you’re putting into this.