Method and properties inheritance in Lua

Hi,

I have implemented a Platform class that is a display group with extra properties. In the constructor, I create the display group and add a few display objects to it, and also define a few properties.

It works as expected, except that some properties of Display Group are not working properly (for instance .numChildren)

Here is the code:

[lua]

local Platform = {}
Platform.__index = Platform

setmetatable(Platform, {
__index = display.newGroup(),
__call = function (cls, …)
return cls.new(…)
end,
})

function Platform.new(x,y, width, height, type )
local self = display.newGroup()
local test = display.newCircle(30,10,10,10,10)
self:insert(test)

print(“num Children:”, self.numChildren)

setmetatable(self, Platform )

print(“num Children:”, self.numChildren)

return self
end

[/lua]

In my main, when I instantiate a Platform, the output in the console is 

num Children: 1

num Children: 0

I don’t understand why I lose the property after setting the metatable. I was new to Lua when I started Corona a few weeks ago and I’ve read tutorials about how to write classes or pseudo-classes, but I would be very grateful if someone could explain in details why the numChildren is lost.

Thanks

endy

Hi, Endy!

First of all I would recommend you to avoid using reserved words like “self” as your local variables names as it may cause hardly discoverable mistakes in future.

Considering your question it looks like Corona’s display objects have complicated logic inside of themselves. And they already have metatables assigned to them.

By doing

[lua]setmetatable(self, Platform )[/lua]

where " self " is a display group you actually overwrite its metatable. Thus you get an unexpectable behaviour.

It is easy to check. Try this code

[lua]local myTab = { a = ‘1’ }

local myTab2 = { a = ‘2’ }

print(myTab.a)

setmetatable(myTab, myTab2)

print(myTab.a)[/lua]

You will see expected result:

1

1

But if you try this code

[lua]local myDG = display.newGroup()

myDG.myCustomProperty = ‘myVal’

print(myDG.numChildren, myDG.myCustomProperty)

local myTab3 = {myCustomProperty = ‘someNewVal’}

setmetatable(myDG, myTab3);

print(myDG.numChildren, myDG.myCustomProperty)[/lua]

you will get

0      myVal

nil    myVal

From this we may conclude that “numChildren” property is actually inherited by display group from its initial metatable.

If you try to add this row to your code

Platform.numChildren = 99

you will see ‘99’ as the result of print(“num Children:”, self.numChildren).

Now how to make things work right. Just create a new table object with your display group as a property of that table. And then set metatable to your table like this:

[lua]function Platform.new(x,y, width, height, type )

    local newPlatform = {}

    newPlatform.group = display.newGroup()

    local test = display.newCircle(30,10,10,10,10)

    newPlatform.group:insert(test)

    print(“num Children:”, newPlatform.group.numChildren)

    setmetatable( newPlatform, Platform )

    print(“num Children:”, newPlatform.group.numChildren)

    return newPlatform;

end[/lua]

And at least a few words about your class template. I wonder how you include this class into your main.lua. I usually look at my classes as on more or less independent modules so I can write require(‘myClass’) when needed.

How do you include this template into your main.lua?

Here is my usual class template:

[lua]local Platform = {}

function Platform:new(x,y, width, height, type )

    local newPlatform = {};

    function newPlatform:init()

        newPlatform.group = display.newGroup()       

        newPlatform.group:insert(display.newCircle(30,10,10,10,10))

            

        print(“num Children:”, newPlatform.group.numChildren)       

    end

   

    function newPlatform:someOtherFunction( … )

        – body

    end

    newPlatform:init();

    self.__index = self

    return setmetatable(newPlatform, self)

end

return Platform[/lua]

If I’ve saved this file as platform.lua then I can easily call it from where I need by using

[lua]local _Platform = require(“platform”)

local myPlatform = _Platform:new(x,y,w,t)[/lua]

I hope this was helpful.

Best regards,

Anton

Hi Anton,

First of all, thank you very much for the detailed answer, you nailed it when you said I was overwriting the displayGroup’s metatable. 

I blame Lua tutorials, many of them introduce the concept of metatables comparing it to operators overloading in other languages. In other languages, if you overload an operator for an object, other operators remain unchanged. So here I thought that if I was updating the metatable for __call and __index, it would leave other metamethods and properties unchanged.

Anyway I’ve made all the necessary updates to my code and for now it seems to be working pretty well.

Thanks again!

endy

Hi, Endy!

First of all I would recommend you to avoid using reserved words like “self” as your local variables names as it may cause hardly discoverable mistakes in future.

Considering your question it looks like Corona’s display objects have complicated logic inside of themselves. And they already have metatables assigned to them.

By doing

[lua]setmetatable(self, Platform )[/lua]

where " self " is a display group you actually overwrite its metatable. Thus you get an unexpectable behaviour.

It is easy to check. Try this code

[lua]local myTab = { a = ‘1’ }

local myTab2 = { a = ‘2’ }

print(myTab.a)

setmetatable(myTab, myTab2)

print(myTab.a)[/lua]

You will see expected result:

1

1

But if you try this code

[lua]local myDG = display.newGroup()

myDG.myCustomProperty = ‘myVal’

print(myDG.numChildren, myDG.myCustomProperty)

local myTab3 = {myCustomProperty = ‘someNewVal’}

setmetatable(myDG, myTab3);

print(myDG.numChildren, myDG.myCustomProperty)[/lua]

you will get

0      myVal

nil    myVal

From this we may conclude that “numChildren” property is actually inherited by display group from its initial metatable.

If you try to add this row to your code

Platform.numChildren = 99

you will see ‘99’ as the result of print(“num Children:”, self.numChildren).

Now how to make things work right. Just create a new table object with your display group as a property of that table. And then set metatable to your table like this:

[lua]function Platform.new(x,y, width, height, type )

    local newPlatform = {}

    newPlatform.group = display.newGroup()

    local test = display.newCircle(30,10,10,10,10)

    newPlatform.group:insert(test)

    print(“num Children:”, newPlatform.group.numChildren)

    setmetatable( newPlatform, Platform )

    print(“num Children:”, newPlatform.group.numChildren)

    return newPlatform;

end[/lua]

And at least a few words about your class template. I wonder how you include this class into your main.lua. I usually look at my classes as on more or less independent modules so I can write require(‘myClass’) when needed.

How do you include this template into your main.lua?

Here is my usual class template:

[lua]local Platform = {}

function Platform:new(x,y, width, height, type )

    local newPlatform = {};

    function newPlatform:init()

        newPlatform.group = display.newGroup()       

        newPlatform.group:insert(display.newCircle(30,10,10,10,10))

            

        print(“num Children:”, newPlatform.group.numChildren)       

    end

   

    function newPlatform:someOtherFunction( … )

        – body

    end

    newPlatform:init();

    self.__index = self

    return setmetatable(newPlatform, self)

end

return Platform[/lua]

If I’ve saved this file as platform.lua then I can easily call it from where I need by using

[lua]local _Platform = require(“platform”)

local myPlatform = _Platform:new(x,y,w,t)[/lua]

I hope this was helpful.

Best regards,

Anton

Hi Anton,

First of all, thank you very much for the detailed answer, you nailed it when you said I was overwriting the displayGroup’s metatable. 

I blame Lua tutorials, many of them introduce the concept of metatables comparing it to operators overloading in other languages. In other languages, if you overload an operator for an object, other operators remain unchanged. So here I thought that if I was updating the metatable for __call and __index, it would leave other metamethods and properties unchanged.

Anyway I’ve made all the necessary updates to my code and for now it seems to be working pretty well.

Thanks again!

endy