Inheritance for metamethods in Lua
i very much like how object oriented programming is described in "programming in lua" 16.1, 16.2:
http://www.lua.org/pil/16.1.html
http://www.lua.org/pil/16.2.html
and would like to follow this approach. but i would like to take things a little further: i would like to have a base "class" called "class", which should be the base of all subclasses, because i want to implement some helper methods there (like "instanceof" etc.), but essentially it should be as described in the book:
function class:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
now to my problem:
i would like to have a class "number", which inherits from "class":
number = class:new()
i would like to define metamethods for operator overloading (__add, __sub, etc.) in this class, so something like:
n1 = number:new()
n2 = number:new()
print(n1 + n2)
works. this is not really a problem. but now i would like to have a third class "money", which inherits from "number":
money = number:new{value=10,currency='EUR'}
i introduce some new properties here and such.
now my pro开发者_如何学Pythonblem is, that i don't get things to work, that "money" inherits all methods from "class" and "number" including all metamethods defined in "number".
i've tried several things like overwriting "new" or modifying metatables but i was not able to get things to work, without either loosing the methods of "class" in "money" or loosing the metamethods of "number" in "money"
i know, that there a lot's of class implementations out there, but i would really like to stick with the minimal approach of lua itself.
any help would be very much appreciated!
thanks very much!
I think that the problem you are having is due to the fact the the operator metamethods are looked up using something similar to rawget(getmetatable(obj) or {}, "__add")
. Thus the operators are not inherited with along with the other functions.
I have had some success with having the new
function copy the operators like this:
function class:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
local m=getmetatable(self)
if m then
for k,v in pairs(m) do
if not rawget(self,k) and k:match("^__") then
self[k] = m[k]
end
end
end
return o
end
This question has already been answered, but let me present my solution for this problem, which I hit some time ago when developing my OOP lib, middleclass.
In my case I start making a list of all the "useful" metamethod names:
local _metamethods = { -- all metamethods except __index
'__add', '__call', '__concat', '__div', '__le', '__lt', '__mod', '__mul', '__pow', '__sub', '__tostring', '__unm'
}
I use that list to add methods to every class that is created. So, in effect, I have a "default implementation" for all the metamethods:
-- creates a subclass
function Object.subclass(theClass, name)
...
local dict = theSubClass.__classDict -- classDict contains all the [meta]methods of the
local superDict = theSuperClass.__classDict -- same for the superclass
...
for _,mmName in ipairs(_metamethods) do -- Creates the initial metamethods
dict[mmName]= function(...) -- by default, they just 'look up' for an implememtation
local method = superDict[mmName] -- and if none found, they throw an error
assert( type(method)=='function', tostring(theSubClass) .. " doesn't implement metamethod '" .. mmName .. "'" )
return method(...)
end
end
The trick is that the default implementation "calls" the parent class' implementation; and if that doesn't exist, it throws an error.
Metamethods implemented this way are only slightly slower than regular methods (2 extra method invocations) and the amount of memory used is very small (12 extra functions per class).
PS: I didn't include a __len
metamethod since Lua 5.1 doesn't respect it anyway.
PS2: I didn't include __index
or __newindex
since I have to use them internally for my classes.
I did something similar and had a similar problem. Here is my base class definition:
RootObjectType = {}
RootObjectType.__index = RootObjectType
function RootObjectType.new( o )
o = o or {}
setmetatable( o, RootObjectType )
o.myOid = RootObjectType.next_oid()
return o
end
function RootObjectType.newSubclass()
local o = {}
o.__index = o
setmetatable( o, RootObjectType )
RootObjectType.copyDownMetaMethods( o, RootObjectType )
o.baseClass = RootObjectType
return o
end
function RootObjectType.copyDownMetaMethods( destination, source ) -- this is the code you want
destination.__lt = source.__lt
destination.__le = source.__le
destination.__eq = source.__eq
destination.__tostring = source.__tostring
end
RootObjectType.myNextOid = 0
function RootObjectType.next_oid()
local id = RootObjectType.myNextOid
RootObjectType.myNextOid = RootObjectType.myNextOid + 1
return id
end
function RootObjectType:instanceOf( parentObjectType )
if parentObjectType == nil then return nil end
local obj = self
--while true do
do
local mt = getmetatable( obj )
if mt == parentObjectType then
return self
elseif mt == nil then
return nil
elseif mt == obj then
return nil
else
obj = mt
end
end
return nil
end
function RootObjectType:__lt( rhs )
return self.myOid < rhs.myOid
end
function RootObjectType:__eq( rhs )
return self.myOid == rhs.myOid
end
function RootObjectType:__le( rhs )
return self.myOid <= rhs.myOid
end
function RootObjectType.assertIdentity( obj, base_type )
if obj == nil or obj.instanceOf == nil or not obj:instanceOf( base_type ) then
error( "Identity of object was not valid" )
end
return obj
end
function set_iterator( set )
local it, state, start = pairs( set )
return function(...)
local v = it(...)
return v
end, state, start
end
精彩评论