2010-11-11 6 views
6

me gusta mucho la forma en la programación orientada a objetos se describe en la "programación en Lua" 16.1, 16.2:de herencias de metamétodos en Lua

http://www.lua.org/pil/16.1.html

http://www.lua.org/pil/16.2.html

y le gustaría seguir este enfoque. pero me gustaría llevar las cosas un poco más lejos: me gustaría tener una "clase" de base llamada "clase", que debería ser la base de todas las subclases, porque quiero implementar algunos métodos de ayuda allí (como "instanceof", etc. .), pero en esencia debería ser como se describe en el libro:

function class:new(o) 
    o = o or {} 
    setmetatable(o, self) 
    self.__index = self 
    return o 
end 

ahora a mi problema:

me gustaría tener un "número" de clase, que hereda de "clase":

number = class:new() 

me gustaría definir metametodos para la sobrecarga del operador (__add, __sub, etc.) en esta clase, algo así como:

n1 = number:new() 
n2 = number:new() 

print(n1 + n2) 

funciona. esto no es realmente un problema pero ahora me gustaría tener una clase de tercero "dinero", que hereda de "número":

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

i introducir algunas nuevas propiedades aquí y tal.

ahora mi problema es que no consigo que las cosas funcionen, que "el dinero" hereda todos los métodos de "clase" y "número" incluyendo todos metamétodos definidos en "número".

intenté varias cosas como sobreescribir metatablas "nuevas" o modificar pero no pude hacer que las cosas funcionen, sin perder los métodos de "clase" en "dinero" o perder los meta-métodos de "número" en "dinero"

sé que hay muchas implementaciones de clase, pero me gustaría seguir con el enfoque mínimo de lua.

¡cualquier ayuda sería muy apreciada!

muchas gracias!

Respuesta

3

Creo que el problema que está teniendo es debido a que los metametodos del operador se buscan usando algo similar al rawget(getmetatable(obj) or {}, "__add"). Por lo tanto, los operadores no son heredados junto con las otras funciones.

que han tenido cierto éxito con que tiene la función de copia new los operadores de la siguiente manera:

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 
+0

gracias, funciona! – aurora

+0

Buena solución. Para evitar tener que repetir esto en todas las clases base, creé una 'Clase' toplevel para todos ellos. El truco para hacer que funcione es agregar 'o = Class.new (self)' a sus constructores, en caso de que anulen los constructores. – Ludwik

0

me hizo algo similar y tenía un problema similar. Aquí está mi definición de la clase de base:

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

muchas gracias! – aurora

2

Esta pregunta ya ha sido contestada, pero permítanme presentar mi solución para este problema, que me golpeó hace algún tiempo en el desarrollo de mi lib programación orientada a objetos, middleclass.

En mi caso yo empezar a hacer una lista de todos los nombres metamétodo "útiles":

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

uso esa lista para añadir métodos a todas las clases que se crea. Así que, de hecho, tengo una "aplicación por defecto" para todos los metamétodos:

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

El truco es que la implementación predeterminada "llama" aplicación de la clase padre; y si eso no existe, arroja un error.

Los metamedios implementados de esta manera son solo un poco más lentos que los métodos regulares (2 invocaciones de métodos adicionales) y la cantidad de memoria utilizada es muy pequeña (12 funciones adicionales por clase).

PD: No incluí un meta-método __len ya que Lua 5.1 no lo respeta de todos modos.

PS2: No incluí __index o __newindex ya que tengo que usarlos internamente para mis clases.

+0

gracias por compartir ... voy a echar un vistazo más de cerca a su enfoque. – aurora