Simple Object Orientation in Lua

This post is a repost from the AGF Games blog.

 

Lua is not object oriented… But some people like it, and it has some advantages…

I decided to use a simple form of it on my project, it does not support multiple inheritance and many other features, it was made to be very, very simple, only to organize the project and advance some form of code reuse.

So, what features I tried to support?

First, very ease of use to create a class
Second, ease to create a instance.
Third, automatic constructor if you do not want to make one.
Fourth, easy “operator overloading” support.
Fifth, static variables (I like them).

Some people will tell me now that there are lots of already done implementations of this, that you only need to search the web… So why make a new one? Well, almost all of the ones I found were too complex for my taste, and mostly for Lua 5.0. Lua 5.1 has a new particular feature, that I really wanted to use to make my class system, a powerful feature.

What we need to use from Lua? Mostly metatables, and the metamethods __index and __call, the second one being available only in Lua 5.1 and onward.

So, how we do it? First, we need to create a new chunk (I suggest you do it creating a file… I will make later a tutorial on how to make a non-file chunk).

My file is named kidclass.lua (the “kid” comes from Kidoteca, the company where I am working now to make children games).

This file will follow my usual style to make “modules”, without using the module() function (that got removed in Lua 5.2 and rightfully so… module() was just plain stupid).

Important, remember to make kidclass (or whatever name you want) local, so you do not end polluting the global namespace (ie: _G) and also avoid some accidental name conflicts…

How you use files made in that style?

Note that doing just require without assigning its return value to something, would be useless, because back when we created the module, we made it local.

Now, how we achieve the first objective, the ease of use to create a class?

The most easy way would just do: myclass = {};
But that would prevent us from achieving the other features, so we need a function, to the sort that creating a class would be “myclass = kidclass.new();”
So we know a kidclass.new, that would at most basic return a {} (this means a empty table), the following could would be the result:

And how we make the second object work now?

I thought that the nicest way to create a instance, would follow the style of C++ (more or less) so “instance = Object()” except Lua does not have a way to create a constructor that is named like a Object… Oh, actually it does have something like that, since 5.1! This is the reason why our code will be different from what you usually see on internet. We will use the might __call.

So, what __call do? __call is a metamethod (a method from a metatable) that is called when you attempt to call a table (ie: you do “table = {}; table()”).

__call is a metamethod, this means that we need to use metatable… I thus define the metatable that contains call on our “new” function we already created.

Since kidclass is what we defined as metatable for class, we need to create __call on kidclass:

Ok, so what we did for now? We made a way to create a class using a simple method.

Thus doing “myClass = kidclass.new()” will create a empty table that has kidclass as metatable.

And we created a way to create a instance.

Thus doing “myInstance = myClass()” will make Lua check if myClass has a metatable (it has… it is kidclass), and then it will check in the metatable (kidclass) if it has a __call. And then it will run the function contained in __call (this is why __call is defined as “= function()”)

But what if we want for example to create a Point class, that supports Point(x, y) as constructor? This mean now we need to create a support for constructor…

We change __call to this:

Alright, now a constructor can be created for our class, following the following format:

This actually already works as very simple OOP, so if you want, you can stop here… But I still want to support operator overloading!

Operator overloading is done in Lua using metamethods too, they are __add for +, __mul for * and so on (look on the Lua manual for all of them, there are also overloading for = and >= and more)

This means we need to add a metatable for the instances, that will look for __add function for example. Since we are making something OOP, the most logical place for __add is the class…

Thus when we do instanceOfBallC = instanceOfBallA + instanceOfBallB; Lua will look for __add in Ball.

We modify again our __call to that:

Static variables we already support, just add them to class (ie: myClass.staticVar = 23) but I think we can add one last cool feature… We can make our instances read from their class any variable that does not exist, thus creating default variables that are also static variables… We do that in lua by setting the __index of the table that is missing a variable… since __index is a metamethod too (we can set it to a table, as a shorthand version of sorts) we need to put it in the metatable.

Thus just before setmetatable(instance, class); line we add class.__index = class, meaning now that any time someone try to read something that does not exist from instance (that has class as metatable) it will read from what the metatable __index pointed (class itself too), thus in practice instances actually inherit from our class.

This is the final version of the code (PLEASE do not just copy paste it… it is here for studying, I am placing a commercial private code here in a goodwill gesture to educate you, not to you just leech it without understanding it).

Buying books in Brazil is hard

I decided to buy “The C++ Programming Language” by Bjarne.

As Brazillian, I had the following options:

  1. Buy translated copy locally
  2. Buy imported copy in local store
  3. Figure how to import a book on my own

So, the first option has the issue that technical books are rarely translated perfectly, yes, when I could not speak english, they were “good enough”, but I could already notice that it had standard library function names translated or some some parts that were too jargon heavy and in the translation ended nonsensical.

Also there are the fact that locally made books rarely are hardcover, and use cheap paper, and black and white printing even in parts that are supposed to have color.

I could buy a imported copy from a local store, and I did took a look around, but the fastest that they could deliver was 8 weeks, plainly unacceptable.

So I went with the third route, not knowing much my options (I in the past tried to import books and found that many stores don’t sell to brazillians at all, not even digital copies), I went with Amazon, that I knew it worked.

Of course, that did not ended well either, the fastest shipping (that was 1 week) was VERY EXPENSIVE, almost expensive as the book itself, so I went with a medium speed shipping that fit my budget, it was expected to arrive in February 7 at most.

It is February 13 today, and it is not here… I asked them to re-send the book. After some back and forth with two attendants (one that was clueless and that I thought it was a bot, because he threw at me lots of canned replies, and one that was very sweet and made me happier just with her behavior) they promised to send again…

I hope it work. And before I might conclude that I should have bought with the 8 weeks shipping in a local store…

But hey, at least the person in costumer service made my day. Thanks Amanda B from Amazon, whoever you are!