Classes with Lua

Even though Lua doesn’t have the concept of classes, we can still use tables to simulate them.  You can create tables with string to number/string/bool to represent member variables, and string to functions to represent member functions.  This would be an example of a table with some of these defined:

--This is the syntax to create a new table
Foo = {}
Foo.memberVariable = 1
function Foo.memberFunction()
  print("hi")
end


Now you have an object Foo with a variable and a function.  How would you create multiple instances of this though?  Lua lets you do this through something called metatables – basically, it is a way of defining a table to fall back on if your table doesn’t have some key defined.  So starting with the example above, we can create a metatable for Foo:

Foo_mt = { __index = Foo }

Assigning something to __index defines where to look if some key doesn’t exist. Now we can create a new table and use this metatable:

Bar = {}
setmetatable(Bar, Foo_mt)

And now you can call memberFunction() on Bar, and it will in turn call Foo’s version.

To simulate classes, the basic idea is to define one table with all the functions you need, and then have a factory method create new tables that use that one table through a metatable.  Here is an example:

function Foo.create()
  local instance = {}
  setmetatable( instance , Foo_mt )
  return instance
end

This function creates a new table, sets the metatable to Foo’s metatable, and then returns it. You can use it like this:

Foo2 = Foo.create()
Foo3 = Foo.create()
Foo4 = Foo.create()

Each of these new variables can have memberFunction() called on it.

One thing you should know is that you can declare functions with either “function Foo.A()” or “function Foo:A()”.  Using “:” instead of “.” is shorthand for actually passing the function the current instance as a variable that the function can access though the “self” keyword.  This is similar to C++, which actually passes a pointer to the instance of the class to a function you call on it.  So you might want to use “.” for static functions, but “:” for regular ones.  You can call functions the same way.  Here is an example:

function Foo:Add()
  self.memberVariable = self.memberVariable + 1
end

Foo2 = Foo.create()
Foo2:Add() -- This is fine
Foo2.Add(Foo2) -- This is equivalent to Foo2:Add()
Foo2.Add() -- This will cause an error because "self" will be null in Add()

Next time, I will write about how to implement inheritance in Lua.

Leave a Reply