I'd like to create MyClass
class in Lua in a separate file myclass.lua which I can import and use later.
It should be working the following way:
local MyClass = require 'myclass'
tab = {1,2,3}
m = MyClass(tab)
However, following the code in Lua docs I can't make it work and am getting errors attempt to call global 'MyClass' (a table value)
.
The code I have written so far for myclass.lua:
local MyClass = {}
MyClass.__index = MyClass
function MyClass.__init(tab)
self.tab = tab or {}
setmetatable({},MyClass)
return self
end
return MyClass
There is a plethora of examples how to write classes in Lua but I don't think I understand the difference and as a result getting lost in the implementation details. Is there a more or less conventional way to do it?
In Lua, you cannot usually call a table like you would call a function. For example, this code will produce an error of "attempt to call local 't' (a table value)".
local t = {}
t()
There is a way of making this work by using metatables, however.
local hello = {}
local mt = {} -- The metatable
mt.__call = function ()
print("Hello!")
end
setmetatable(hello, mt)
hello() -- prints "Hello!"
When you try and call a table as you would a function, Lua first checks to see whether the table has a metatable. If it does, then it tries to call the function in the __call
property of that metatable. The first argument to the __call
function is the table itself, and subsequent arguments are the arguments that were passed when the table was called as a function. If the table doesn't have a metatable, or the metatable doesn't have a __call
function, then an "attempt to call local 't'" error is raised.
Your example code has three problems:
__init
instead of __call
. Lua doesn't have an __init
metamethod.__call
takes different parameters than the ones you are using. The first parameter to the __call
function is the table itself. You can either use function MyClass.__call(self, tab)
, or use the colon syntax, function MyClass:__call(tab)
, which implicitly adds the self
parameter for you. These two syntaxes are functionally identical.MyClass
table. While you are setting a metatable for MyClass's objects, that doesn't mean that a metatable is automatically set for MyClass itself.To fix this, you could do something like the following:
local MyClass = {}
setmetatable(MyClass, MyClass)
MyClass.__index = MyClass
function MyClass:__call(tab)
local obj = {}
obj.tab = tab or {}
setmetatable(obj, MyClass)
return obj
end
return MyClass
This sets MyClass to use itself as a metatable, which is perfectly valid Lua.
The system of metatables is very flexible, and allows you to have just about any class/object scheme you want. For example, if you want, you can do everything inline.
local MyClass = {}
setmetatable(MyClass, {
__call = function (class, tab)
local obj = {}
obj.tab = tab or {}
setmetatable(obj, {
__index = MyClass
})
return obj
end
})
return MyClass
As well as being concise, this also has the advantage that people can't change the class's metamethods if they have access to the class table.