开发者

Inheritance for metamethods in Lua

开发者 https://www.devze.com 2023-01-24 19:55 出处:网络
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

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
0

精彩评论

暂无评论...
验证码 换一张
取 消