Question

I'm trying to learn Lua, so hopefully this is an easy question to answer. The following code does not work. The variable childContext leaks between all instances of classes.

--[[--
Context class.
--]]--
CxBR_Context = {}

-- Name of Context
CxBR_Context.name = "Context Name Not Set"

-- Child Context List
CxBR_Context.childContexts = {}

-- Create a new instance of a context class
function CxBR_Context:New (object)
  object = object or {} -- create object if user does not provide one
  setmetatable(object, self)
  self.__index = self
  return object
end

-- Add Child Context
function CxBR_Context:AddChildContext(context)
  table.insert(self.childContexts, context)
  print("Add Child Context " .. context.name .. " to " .. self.name)
end


--[[--
Context 1 class. Inherits CxBR_Context
--]]--
Context1 = CxBR_Context:New{name = "Context1"}


--[[--
Context 1A class.Inherits CxBR_Context
--]]--
Context1A = CxBR_Context:New{name = "Context1A"}


--[[--
Context 2 class.Inherits CxBR_Context
--]]--
Context2 = CxBR_Context:New{name = "Context2"}


--[[--
TEST
--]]--
context1  = Context1:New() -- Create instance of Context 1 class
print(context1.name .." has " .. table.getn(context1.childContexts) .. " children")
context2  = Context2:New() -- Create instance of Context 2 class
print(context2.name .." has " .. table.getn(context2.childContexts) .. " children")
context1A = Context1A:New() -- Create instance of Context 1A class
print(context1A.name .." has " .. table.getn(context1A.childContexts) .. " children")

context1:AddChildContext(context1A) -- Add  Context 1A as child to context 1

print(context1.name .." has " .. table.getn(context1.childContexts) .. " children") -- Results Okay, has 1 child
print(context2.name .." has " .. table.getn(context2.childContexts) .. " children") -- Why does thin return 1, should be 0

Looking at the Object oriented lua classes leaking I can fix my problem by changing the constructor function to:

-- Child Context List
-- CxBR_Context.childContexts = {}

-- Create a new instance of a context class
function CxBR_Context:New (object)
  object = object or { childContexts = {} } -- create object if user does not provide one
  setmetatable(object, self)
  self.__index = self
  return object
end

So my questions are:

  1. Is there a cleaner way to declare class variables, much like the first example, so I do not have to include it in the constructor?
  2. Why does the CxBR_Context.name work, but the table CxBR_Context.childContexts do not?
Was it helpful?

Solution

  1. No, I don't think so. You want each child Context object you create to have its own childContexts field.

  2. It works, because you pass a table containing a name field to New function. What you did here was:

Context1 = CxBR_Context:New{name = "Context1"}

  • Create a table with field "name" set to "Context1"
  • Pass the table to the constructor
  • [In the constructor] Assign a metatable (original CxBR_Context table) to the table you created in the first step.

So basically, when you call Context1.name, you retrieve a field from the Context1 table you created when invoking the constructor. But when you index it with childContext and no such field exists (since in the constructor stage you only created a "name" field), Lua looks in the __index table, which is CxBR_Context. And this table is common to all ContextX objects.

EDIT:

object = object or { childContexts = {} } -- create object if user does not provide one

Actually, this won't work either, if you supply your own table like in this:

Context1 = CxBR_Context:New{name = "Context1"}

It will only work if your object argument is nil, that is if you invoke the constructor just with self parameter.

OTHER TIPS

1.
By using table.insert(self.member, v) you are modifying the content of the container pointed to by self.member which turns out to be a member of nearest super class where this member exists.
If you need to assign a value to member of subclass, do it explicitly. Use

-- create a copy of array with one additional element
self.childContexts = { context, unpack(self.childContexts) }

instead of

table.insert(self.childContexts, context)

2.
Because you are using assignment for CxBR_Context.name and not using it for CxBR_Context.childContexts.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top