lua-users home
lua-l archive

Multi-level inheritance and table passing.

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


--[[
**** 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?
--]]

AltStyle によって変換されたページ (->オリジナル) /