ntooop
ntooop

Reputation: 23

Can't inherit table attribute from parent

Sprite = {x = 0, y = 0, pos = {z=0}}
function Sprite:new()
    o = {}
    setmetatable(o,self)
    self.__index = self
    return o
end
s1 = Sprite:new()
s2 = Sprite:new()

s1.x = 10
s1.pos.z = 5

print("s1.x", s1.x, "s2.x", s2.x, "s1.z",s1.pos.z, "s2.z", s2.pos.z )

s2.x = 20
s2.pos.z = 50

print("s1.x", s1.x, "s2.x", s2.x, "s1.z",s1.pos.z, "s2.z", s2.pos.z )

In the above code snippet, I define a class Sprite which has x(int),y(int),pos(table) 3 attributes, but when I init two object s1,s2. I found that they shared the pos attribute.

If you run the code, it will print:

s1.x    10  s2.x    0   s1.z    5   s2.z    5
s1.x    10  s2.x    20  s1.z    50  s2.z    50

s1 and s2 has their own x,y attribute, but share pos attribute, if s1.pos.z is changed, so as the s2.pos.z.

How can I fix this?

Upvotes: 2

Views: 150

Answers (3)

Oliver
Oliver

Reputation: 29463

Sprite represents the class, hence you can think of Sprite table as the "class wide" entries: they will be shared by all "instances". Instance specific entries should be in the o table:

Sprite = {classX = 0, classY = 0} -- class; vars shared by all instances
function Sprite:new()
    o = {pos = {z=0}}
    setmetatable(o,self)
    self.__index = self
    return o
end

Note that "shared" really does mean shared at the reference level, so all instances will see the same values and any changes made by one instance will be seen by all others. OTOH data you put in o table is per instance. Putting this in Sprite.new() ensures that all instances have the same fields, but their own data; changes by one instance will not affect any other instance.

That said, your Sprite:new() does not define self.__newindex. So Sprite.classX = 5 will be seen by all instances, as expected, but s1.classX = 6 will only be seen by s1: it will create a new field, thus hiding that of Sprite. From then on, changes to Sprite.classX will no longer be seen by s1 (but will be by all other instances that have not overridden Sprite.classX). To get around that, you could do this:

function Sprite:new()
    o = {pos = {z=0}}
    setmetatable(o,self)
    self.__index = self
    self.__newindex = self
    return o
end

In Lua console you would see this if you played around with this:

> s1=Sprite:new()
> s2=Sprite:new()
> print(s1.classX, s2.classX)
0       0
> Sprite.classX=1
> print(s1.classX, s2.classX)
1       1
> s1.classX=3
> print(s1.classX, s2.classX)
3       3

Without that change, that last output will show "3 1" and changes to Sprite.classX would not be visible in s1.

Upvotes: 0

lhf
lhf

Reputation: 72312

In Sprite:new, the variable self always has Sprite as its value. So, self.pos refers to Sprite.pos. Try changing to o.pos={}. Also, consider making o a local.

Upvotes: 3

Seagull
Seagull

Reputation: 13859

Lua shares tables(keeps them as reference), and copy variables. Use metatables for methods, and keep fields copying them in your object table.

Sprite = {
  instanceData = { x = 0, y = 0, pos = {z = 0} },
  method = function(self) print("do smth with "..self) end
}

function Sprite:new()
  local o = deepCopy(self.instanceData)
  setmetatable(o,self)
  self.__index = self
  return o
end

deep copy implementation can be found wiki/CopyTable

Upvotes: -1

Related Questions