in Lua we do OO programming like this:
MyClass = {}
function MyClass:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
obj.hello = "hello world"
return obj
end
function MyClass:sayHi()
print(self.hello)
end
function main()
local obj = MyClass:new()
obj:sayHi()
end
When working with more compelx stuff, usually I take advantage of Lua's metamethods to proxy function calls and do whatever I need with it, like arguments parsing etc, by adding this:
MyClassMeta = {}
function MyClassMeta.__index(obj, funcName)
return function (self, ...)
//do some stuff
print("you called " .. funcName .. " with args", ...)
end
end
and changing the line:
setmetatable(obj, self)
to:
setmetatable(obj, MyClassMeta)
every function I call with an instance of MyClass
will execute the code implemented in MyClassMeta.__index
metamethod.
What I want to do now is inherit the MyClass
existing methods, and execute MyClassMeta.__index
only for functions that are not part of MyClass
.
In the above example, the code will always execute the MyClassMeta.__index
metamethod even when calling MyClass:sayHi()
:
function main()
local obj = MyClass:new()
obj:sayHi("hello")
end
you called sayHi with args hello
When you set __index
to a table, it will look for properties on that table and return them if they don't exist on the instance. Since sayHi
exists on the MyClass
table it is used.
self.__index = self
When you set __index
to a function, it can return anything for properties that don't exist on the instance. You can check if the key exists on the MyClass table and return it, then do something else if it doesn't:
MyClass = {}
MyMetatable = {
__index = function(obj, key)
if MyClass[key] ~= nil then return MyClass[key] end
return function(self, ...)
print("you called "..tostring(key))
print(" self.hello is '"..tostring(self.hello).."'")
print(" with args", ...)
end
end
}
function MyClass:new()
local obj = {}
setmetatable(obj, MyMetatable)
obj.hello = "hello world"
return obj
end
function MyClass:sayHi()
print(self.hello)
end
function main()
local obj = MyClass:new()
obj:sayHi()
end
local obj = MyClass:new()
obj:sayHi("hello")
obj:somethingElse(1, 2, 3)
Version with Egor's comment
MyClass = {}
setmetatable(MyClass, {
-- if it's not found on MyClass, return a function
__index = function(self, funcName)
return function(self, ...)
print("you called "..funcName.." with args", ...)
end
end
})
function MyClass:new()
local obj = {}
-- if it's not found on obj, try self (MyClass)
setmetatable(obj, { __index = self })
obj.hello = "hello world"
return obj
end
function MyClass:sayHi()
print(self.hello)
end
local obj = MyClass:new()
obj:sayHi()
obj:somethingElse(1, 2, 3)
When creating an object this sets the __index
of the new object's metatable to MyClass
, and MyClass's metatable's index to the function that is a fallback. So if the property isn't on your object or on MyClass, it will use the fallback.