One file simple Lua library to do OOP (Object Oriented Programming).
Switch branches/tags
Clone or download
Imagic
Latest commit 163db55 Nov 7, 2018

README.adoc

Luaoop

Luaoop is a library to do OOP (Object Oriented Programming) which aims to be simple, powerful and optimized (for LuaJIT).

Look at the examples for more understanding of the library.

Install

Instead of adding the file manually, luarocks can be used:

See rockspecs and luarocks.

ℹ️
It is designed to work with LuaJIT (Lua 5.1), but the code should work on latest versions.

API

-- create a new class
-- name: identifier for debugging purpose
-- ...: base classes (single/multiple inheritance)
-- return created class
class.new(name, ...)
-- SHORTCUT class(name, ...)

-- t: class or instance
-- return class name or nil
class.name(t)

-- t: instance
-- return the type (class) or nil
class.type(t)

-- check if an instance/class is/inherits from a specific class
-- t: class or instance
-- classdef: can be nil to check if t is a valid (built) class
class.is(t, classdef)

-- return instance/class types map (type => true)
-- t: class or instance
-- return types map (type => true) or nil
class.types(t)

-- works by using tostring(table) address hack or using a counter instead on failure
-- t: instance
-- return unique instance id or nil
class.id(t)

-- t: instance
-- return unique instance data table or nil
class.data(t)

-- get the class metatable applied to the instances
-- useful to apply class behaviour to a custom table
-- will build the class if not already built
-- classdef: class
-- return meta or nil
class.meta(classdef)

-- create instance
-- classdef: class
-- ...: constructor arguments
class.instantiate(classdef, ...)
-- SHORTCUT Class(...)

-- build class
-- will build/re-build the class
-- (if a class is not already built, when used for inheritance or instantiation this function is called)
-- classdef: class
class.build(classdef)

-- get operator
-- lhs: instance
-- name: full name of the operator (starting with "__")
-- rhs: any value, can be nil for unary operators
-- no_error: if passed/true, will not trigger an error if no operator was found
class.getop(lhs, name, rhs, no_error)

Inheritance

  • single and multiple inheritance

  • all properties are inherited for classes

  • all properties except special tables (starting with __) are inherited for instances

⚠️
In case of multiple inheritance with functions with the same name, one will be taken arbitrarily. This issue can be solved by accessing directly to a specific parent method/function using the class definition.

Inheritance is not dynamic (cached) and is built by class.build. class.build must be called for any later changes to the class definition or base classes (in order, build the dependencies, then build the class, then build the derived classes).

ℹ️
The function is already called when using a class for inheritance or instantiation for the first time, in most cases calling this function is not needed because the class is completely defined when used.
Example 1. Multiple inheritance
A = class("A")
function A:test()
  print("a")
end

B = class("B")
function B:test()
  print("b")
end

C = class("C", A, B) -- inheritance from A and B
function C:test() -- force the usage of B:test()
  B.test(self)
end
Example 2. Overload
A = class("A")

function A:__construct()
  print("a")
end

B = class("B", A)
function B:__construct()
  A.__construct(self) -- call parent (A) constructor
  print("b")
end

Special methods

Special methods for a class can be defined, they will be overridden the same way other properties are. Every special method start with __ (they are not metamethods, they are named like this to keep consistency with the Lua notation).

Misc

construct

called at initialization

destruct

called at garbage collection

Operators (or things similar)

Operators can be defined like this:

function Object:__op() end -- unary
Object.__op[rhs] = function(self, rhs) end -- binary
ℹ️
rhs can be a class or a Lua type (as string).
Unary
call

like the metamethod

tostring

like the metamethod

unm

like the metamethod

Binary
concat

like the metamethod (no order, but has a second parameter "inverse" when the concat is not forward)

add

like the metamethod (no order)

sub

like the metamethod (can be omitted if add is defined and unm is defined for rhs)

mul

like the metamethod (no order)

div

like the metamethod

mod

like the metamethod

pow

like the metamethod

eq

like the metamethod (doesn’t throw an error if the operator is missing, will be false by default)

le

like the metamethod

lt

like the metamethod

🔥
Comparison of different instances with different types is possible, but this may change in the future.

Private / Protected

There are no private/protected mechanisms in Luaoop.

"Private" methods can be achieved with local functions in the class definition.

"Private" instance properties can be achieved using class.data, it can be used to keep some data away from the instance user.

Behavior transfer

It’s possible to give Luaoop class and instance behavior to any object by adding the luaoop property (a table) to its metatable (and set some metamethods).

Class behavior

💡
class.new will check (and build if not built) base classes and initializes class special tables. It is easier to use this function and copy/modify the metatable afterwards.
Base properties
name

class name

bases

list of base classes

Optional build hooks can be added to customize some parts of the build process, they are functions starting with __.

Class hooks
postbuild(class, build)

used to add more properties to the build, called after the base classes inheritance process

postmeta(class, meta)

used to modify the built instance metatable, called at the end of the build process

instantiate(class, …​)

used to replace the default instantiate behavior, should return a valid new Luaoop instance (…​ are constructor arguments)

ℹ️
this hook by-pass the construct/destruct default behavior (they will not be set/called)
After-build properties
build

table containing inherited properties and special tables for the class

instance_build

table containing inherited properties without special tables (merged with class properties)

types

map of type (class) ⇒ true

meta

metatable built used for the instances

Base properties
type

instance type

types

map of type (class) ⇒ true

name

base class name

Optional hooks can be added to customize the instances behavior, they are functions starting with __.

Hooks
data(instance)

should return the instance datatable

id(instance)

should return the instance id (number)

Metamethods
index

inherits from class instance_build

call

op call

unm

op unm

add

op add

sub

op sub

mul

op mul

div

op div

pow

op pow

mod

op mod

eq

op eq

le

op le

lt

op lt

tostring

op tostring

concat

op concat

ℹ️
It’s easier to let these properties being created by class.build and just implement the build hooks.
Metamethods
call

shortcut for class.instantiate

tostring

for regular classes, will print class<name>

index

inherits from the build table and each special tables inherit from build special tables

Instance behavior

The instance behavior is set using the meta built metatable. If data or id hooks are not set or in some cases if the class has a destructor, this metatable could be replaced by a copy (not deep) to have custom fields (to store data and id).

💡
The new metatable would be marked as custom with a luaoop.custom boolean property set to true.