Наследование метаметодов в Lua - PullRequest
6 голосов
/ 11 ноября 2010

Мне очень нравится, как объектно-ориентированное программирование описано в "Программирование на языке lua" 16.1, 16.2:

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

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

и хотелось быследовать этому подходу.но я хотел бы пойти немного дальше: я хотел бы иметь базовый «класс» под названием «класс», который должен быть основой всех подклассов, потому что я хочу реализовать там некоторые вспомогательные методы (например, «instanceof» и т. д.).), но по сути это должно быть так, как описано в книге:

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

Теперь к моей проблеме:

Я хотел бы иметь класс "число", который наследуется от "класс"":

number = class:new()

Я хотел бы определить метаметоды для перегрузки операторов (__add, __sub и т. Д.) В этом классе, поэтому что-то вроде:

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

print(n1 + n2)

работает.это на самом деле не проблема.но теперь я хотел бы иметь третий класс "money", который наследуется от "number":

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

я ввожу здесь некоторые новые свойства и тому подобное.

теперь моя проблема в том,что у меня не получается работать, что «деньги» наследуют все методы из «класса» и «числа» , включая все метаметоды, определенные в «числе».

я пробовалнекоторые вещи, такие как перезапись «новых» или модификация метатаблиц, но я не смог заставить вещи работать, не потеряв ни методов «класса» в «деньгах», ни мета-методов «числа» в «деньгах»

Я знаю, что существует множество реализаций классов, но я действительно хотел бы придерживаться минимального подхода самого lua.

любая помощь будет очень признательна!

большое спасибо!

Ответы [ 3 ]

3 голосов
/ 11 ноября 2010

Я думаю, что проблема, с которой вы столкнулись, связана с тем, что мета-методы оператора ищутся с использованием чего-то похожего на rawget(getmetatable(obj) or {}, "__add"). Таким образом, операторы не наследуются вместе с другими функциями.

У меня был некоторый успех с функцией new, копирующей операторы, подобные этому:

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
2 голосов
/ 24 марта 2011

На этот вопрос уже дан ответ, но позвольте мне представить свое решение этой проблемы, которое я использовал некоторое время назад при разработке своей ООП-библиотеки, middleclass .

В моем случае яначните составлять список всех «полезных» имен метаметодов:

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

Я использую этот список для добавления методов в каждый создаваемый класс.Итак, по сути, у меня есть «реализация по умолчанию» для всех метаметодов:

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

Хитрость в том, что реализация по умолчанию «вызывает» реализацию родительского класса;а если этого не существует, выдается ошибка.

Метаметоды, реализованные таким образом, лишь немного медленнее, чем обычные методы (2 дополнительных вызова метода), а объем используемой памяти очень мал (12 дополнительных функций вкласс).

PS: я не включил метаметод __len, поскольку Lua 5.1 все равно его не уважает.

PS2: я не включил __index или __newindex так как я должен использовать их для своих занятий.

0 голосов
/ 11 ноября 2010

Я сделал что-то похожее, и у меня была похожая проблема. Вот мое определение базового класса:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...