Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
Inheritance troubles — Gideros Forum

Inheritance troubles

jasimmonsjasimmons Member
edited April 2012 in General questions
I am able to inherit a value "hunger" from a parent class (Animal) and see its value in the child class (Dog), but I cannot seem to change it in the child class through any function not named init. I made a test project (below) and it somehow works fine, but I can't seem to replicate it in my actual project.
--animal.lua:
Animal = Core.class(Sprite)
 
function Animal:init()
	self.hunger = 50
	print("Animal:"..self.hunger)
end
 
function Animal:setHunger(hunger)
	self.hunger = hunger
	print("setHunger(): "..self.hunger)
end
 
function Animal:getHunger()
	print("getHunger(): "..self.hunger)
	return self.hunger
end
 
--dog.lua
Dog = Core.class(Animal)
 
function Dog:init()
	print("Dog:"..self.hunger)
end
 
function Dog:callMe()
	print("callMe(): "..self:getHunger())
end
 
--main.lua:
local d = Dog.new()
d:setHunger(20)
d:callMe()
 
 
 
 
------------------
-- Output:
------------------
Animal:50
Dog:50
setHunger(): 20
getHunger(): 20
callMe(): 20
The output is as expected. Created a Dog object first calls Animal's init() method which prints the first line, then Dog's init() method prints the second line.

Calling "d:setHunger(20)" calls the inherited method from Animal, printing the third line.

Calling "d:callMe()" first goes into Animal's getHunger() method resulting in the fourth line, and then back up in hierarchy to finally print the fifth line. The values are all correct.

Now, in my actual project, I am trying to do the same thing, but the Dog class calling getHunger() is resulting in the "old" value.
------------
breed.lua
------------
Breed = Core.class(Sprite)
 
function Breed:init()
	self.hunger = 20
	local timer = Timer.new(5000, 0)
	timer:addEventListener(Event.TIMER, self.statusUpdate, self)
	timer:start()
end
 
function Breed:needUpdate(event)
	if self.hunger <= 95 then
		self:setHunger(self.hunger + 5)
		print("Hunger set to "..self:getHunger())
	end
end
 
function Breed:statusUpdate(event)
	self.needUpdate(self)
end
 
function Breed:setHunger(hunger)
	self.hunger = hunger
end
 
function Breed:getHunger()
	return self.hunger
end
 
------------
dog.lua
------------
Dog = Core.class(Breed)
 
function Dog:init()
	local timer = Timer.new(5000,0)
	timer:addEventListener(Event.TIMER, self.updateMeters, self)
	timer:start()
 
end
 
function Dog:updateMeters(event)
	local h = self:getHunger()
	print("updateMeters(): "..h)
end
 
------------------
-- Output: (Multiple outputs as timer goes on)
------------------
Hunger set to 25
updateMeters(): 20
 
Hunger set to 30
updateMeters(): 20
 
Hunger set to 35
updateMeters(): 20
 
Hunger set to 40
updateMeters(): 20
I tried to cut out most of the code that was irrelevant.

So, every 5000 ms two timers are going off: one in the Breed class (Breed:NeedUpdate()) which increases self.hunger by 5, and one in the Dog class (Dog:updateMeters()) which just prints self.hunger through the inherited getHunger() method. I am not understanding why the second printed line from updateMeters() is always printing 20, when the value is clearly being changed in the needUpdate() method.

I am sorry for the long-winded question, I just wanted to be thorough! Thanks for any help!
-J

Comments

  • ndossndoss Guru
    edited April 2012
    In the non-working code, if I print self in updateMeters and needUpdate, then they appear to be two different tables -- similarly, I get two different values if I print self in both init functions.

    Testing this out on a simpler example, the value of self is different in the two init calls ...
    A = Core.class()
    function A:init()
       print(self)
    end
     
    B = Core.class(A)
    function B:init()
       print(self)
    end
     
    local b = B.new()
    If this was a language like c++, I'd expect the value of "self" to be the same in both init functions when I create a "B".

    So ... addEventListener in Breed.init adds an event listener that references a different table than the addEventListener in Dog.init ... and since they're different, they each have their own version of "hunger".

    I don't know whether @atilim will consider this a bug, but it's not the behavior I'd expect.

    --ND


  • A short-term ugly workaround ...

    1. Name your "init" functions something else, e.g., "construct"
    2. Add an explicit call to the base class construct method as the first line in your construct functions
    3. After you create a class, call its construct method.

    Example:
    ------------------------------------------------------------
    Breed = Core.class(Sprite)
     
    function Breed:construct() -- renamed from init to construct (1)
    	self.hunger = 20
    	local timer = Timer.new(5000, 0)
    	timer:addEventListener(Event.TIMER, self.statusUpdate, self)
    	timer:start()
    end
     
    function Breed:needUpdate(event)
    	if self.hunger <= 95 then
    		self:setHunger(self.hunger + 5)
    		print("Hunger set to "..self:getHunger())
    	end
    end
     
    function Breed:statusUpdate(event)
    	self.needUpdate(self)
    end
     
    function Breed:setHunger(hunger)
    	self.hunger = hunger
    end
     
    function Breed:getHunger()
    	return self.hunger
    end
     
    ------------------------------------------------------------
    Dog = Core.class(Breed)
     
    function Dog:construct()      -- renamed from init to construct (1)
    	Breed.construct(self) -- explicitly call base class construct (2)
    	local timer = Timer.new(5000,0)
    	timer:addEventListener(Event.TIMER, self.updateMeters, self)
    	timer:start()
     
    end
     
    function Dog:updateMeters(event)
    	local h = self:getHunger()
    	print("updateMeters(): "..h)
    end
     
    ------------------------------------------------------------
    local d = Dog.new()
    d:construct()  -- explicitly call construct (3)
    d:setHunger(20)
  • In the non-working code, if I print self in updateMeters and needUpdate, then they appear to be two different tables -- similarly, I get two different values if I print self in both init functions.

    Testing this out on a simpler example, the value of self is different in the two init calls ...
    A = Core.class()
    function A:init()
       print(self)
    end
     
    B = Core.class(A)
    function B:init()
       print(self)
    end
     
    local b = B.new()
    If this was a language like c++, I'd expect the value of "self" to be the same in both init functions when I create a "B".

    So ... addEventListener in Breed.init adds an event listener that references a different table than the addEventListener in Dog.init ... and since they're different, they each have their own version of "hunger".

    I don't know whether @atilim will consider this a bug, but it's not the behavior I'd expect.

    --ND


    I'd be worried if "self" was identical, as far as I'm aware "self" can be thought of like the "this" reference in C++. As I read it - In the above example A is a table and B is a different table that contains it's own copy of A (or at least a reference to it).

    If memory serves "self" is actually a reference to the table that contains the function being executed and is passed in as a first "hidden" parameter when calling a table function with : as opposed to .

    ie.
    table.function(self,param) is the same as table:function(param)

    in both cases you can reference "self" from within the function and get the same value.

    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
  • jasimmonsjasimmons Member
    edited April 2012
    @ndoss the workaround appears to be working, but it is indeed ugly. I will continue watching this to see if any other solution may arise, but as of right now it seems having to explicitly call a constructor method might be what I need to do.

    Thanks!
    -J

    @techdojo IIRC in C++, using the "this" reference in A to work with parameters should still change those parameters in B (if the object created is a B). This is what seems to be the problem, as changing "hunger" in the Breed class is not changing "hunger" in the Dog class because the Dog class's "hunger" contains a different instance of "hunger", a deep copy from the Breed class rather than the shallow one I expect/need.
Sign In or Register to comment.