[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Multi-level inheritance and table passing.
- From: "Gavin Kistner" <gavin.kistner@...>
- Date: Fri, 9 Mar 2007 23:16:54 -0700
--[[
**** Summary:
****
**** How do I create an inheritance chain that can use __index
**** getter methods for some properties, while passing in
**** the original calling table to the function?
****
**** Details:
****
**** I've written my own class system in Lua, with single
**** inheritance and shared properties and methods. Primary goals
**** are lookup speed and sharing metatables where possible, while
**** providing a specific set of features. It's working well, leaning
**** heavily on __index=table for no-function-call inheritance.
****
**** To use it to mimic an old interface, I now need to add getter
**** methods tied to properties, so that asking for "foo.bar" will
**** run a function to calculate the value of 'bar', using properties
**** set on the foo object/table.
****
**** I'm having trouble coming up with a way to do this that passes
**** the foo table along as necessary.
****
**** What follows is a simplified version of my current setup,
**** showing the problem in the end.
--]]
-- Each 'class' has its own prototype table for properties
-- specific to its instances.
Rectangle = {}
Rectangle.prototype = {
class = Rectangle,
polygon = true
}
-- A shared metatable for subclasses to use
Rectangle.protometa = { __index = Rectangle.prototype }
-- A shared metatable for instances to use prototype lookup
-- (In practice, this is has additional properties different
-- from protometa; that's why they're different.)
Rectangle.instancemeta = { __index = Rectangle.prototype }
-- An instance
myRect = { width=10, height=20 }
setmetatable( myRect, Rectangle.instancemeta )
-- Sanity check
assert( myRect.width == 10 )
assert( myRect.class == Rectangle )
assert( myRect.polygon == true )
-- A subclass
Square = { superclass=Rectangle }
-- Properties that all the square instances inherit...
Square.prototype = { class = Square }
-- ...and thence on to the Rectangle.prototype
setmetatable( Square.prototype, Rectangle.protometa )
-- Shared metatable for all square instances
Square.instancemeta = { __index = Square.prototype }
-- Another instance
mySquare = { width=15, height=15 }
setmetatable( mySquare, Square.instancemeta )
-- More sanity checks, including Rectangle.prototype
assert( mySquare.width == 15 )
assert( mySquare.class == Square )
assert( mySquare.polygon == true )
-- So far so good.
-- Now I want to add some getter methods masquerading as properties
local theMeta = { }
theMeta.getters = {
ploygon = function( ) return "I think you mean 'polygon'" end,
area = function( object )
print( "...Calculating area on", object )
return object.width * object.height
end
}
theMeta.__index = function( obj, property )
print( "...Looking for '"..property.."' on ", obj )
local theFunc = theMeta.getters[ property ]
if theFunc then
return theFunc( obj )
end
end
setmetatable( Rectangle.prototype, theMeta )
-- Let's test it out!
print( myRect.ploygon )
--> ...Looking for 'ploygon' on table: 0032B550
--> I think you mean 'polygon'
print( mySquare.area )
--> ...Looking for 'area' on table: 0032B550
--> ...Calculating area on table: 0032B550
--> ...Looking for 'width' on table: 0032B550
--> ...Looking for 'height' on table: 0032B550
--> Lua50.exe: tmp.lua:59: attempt to perform arithmetic on field
`width' (a nil value)
-- Bummer. What is the 'obj' passed to the getter then?
print( myRect )
--> table: 0032BA68
print( mySquare )
--> table: 0032BF70
print( Rectangle.prototype )
--> table: 0032B550
--> Ah.
--[[
**** The problem, thus, is that when __index is used on table A to
**** look at parent table B, and table B has its own __index function,
**** it is table B that is passed to the function, not table A.
****
**** If it was only one level of inheritance, I would change the
**** __index on table A (Square.instancemeta in the above) to perform
**** the lookup, and all would be well.
****
**** Is my only choice to write my own lookup mechanism, then?
**** Perhaps use __index on a chain of getter function hierarchy?
--]]