Meta Method Inheritance in Lua

I really 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 a little more detail: 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 (for example, "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" that inherits from a "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) 

working. It's not a problem. but now I would like to have a third class “money” that inherits from the “number”:

 money = number:new{value=10,currency='EUR'} 

i introduce some new properties here.

Now my problem is that I can’t get it to work, that “money” inherits all the methods from the “class” and “number”, including all the metamethods defined in the “number”.

I tried several things, such as rewriting "new" or changing metathetes, but I could not get it working without losing the "class" methods in the "money" or losing the "number" metamethods in the "money"

I know that there are many class implementations, but I really would like to stick with a minimal approach to lua itself.

Any help would be greatly appreciated!

Many thanks!

+6
inheritance oop lua
source share
3 answers

I think the problem you are facing is that the metamotodes of the operator are scanned using something similar to rawget(getmetatable(obj) or {}, "__add") . Therefore, operators are not inherited along with other functions.

I had some success when the new function copied such statements:

 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 
+3
source share

This question has already been given, but let me introduce my solution to this problem, which I hit some time ago while developing my OOP lib, middleclass .

In my case, I begin to make a list of all the "useful" names of metamethods:

 local _metamethods = { -- all metamethods except __index '__add', '__call', '__concat', '__div', '__le', '__lt', '__mod', '__mul', '__pow', '__sub', '__tostring', '__unm' } 

I use this list to add methods for each class I create. Thus, in essence, I have a "default implementation" for all 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 "invokes" the implementation of the parent class; and if it does not exist, it throws an error.

Metamethods implemented in this way are only slightly slower than regular methods (2 additional method calls), and the amount of memory used is very small (12 additional functions for each class).

PS: I did not include the __len __len , since Lua 5.1 still does not respect.

PS2: I did not include __index or __newindex , since I have to use them inside my classes.

+2
source share

I did something similar and had a similar problem. Here is the definition of my base class:

 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
source share