Defining classes in Lua

In addition to binding C++ functions and classes with Lua, luabind also provide an OO-system in Lua.

class 'lua_testclass'

function lua_testclass:__init(name)
    self.name = name
end

function lua_testclass:print()
    print(self.name)
end

a = lua_testclass('example')
a:print()

Inheritance can be used between lua-classes:

class 'derived' (lua_testclass)

function derived:__init()
    lua_testclass.__init(self, 'derived name')
end

function derived:print()
    print('Derived:print() -> ')
    lua_testclass.print(self)
end

The base class is initialized explicitly by calling its __init() function.

As you can see in this example, you can call the base class member functions. You can find all member functions in the base class, but you will have to give the this-pointer (self) as first argument.

Deriving in lua

It is also possible to derive Lua classes from C++ classes, and override virtual functions with Lua functions. To do this we have to create a wrapper class for our C++ base class. This is the class that will hold the Lua object when we instantiate a Lua class.

class base
{
public:
    base(const char* s)
    { std::cout << s << "\n"; }

    virtual void f(int a)
    { std::cout << "f(" << a << ")\n"; }
};

struct base_wrapper : base, luabind::wrap_base
{
    base_wrapper(const char* s)
        : base(s)
    {}

    virtual void f(int a)
    {
        call<void>("f", a);
    }

    static void default_f(base* ptr, int a)
    {
        return ptr->base::f(a);
    }
};

// ...

module(L)
[
    class_<base, base_wrapper>("base")
        .def(constructor<const char*>())
        .def("f", &base::f, &base_wrapper::default_f)
];

Important

Since MSVC6.5 doesn’t support explicit template parameters to member functions, instead of using the member function call() you call a free function call_member() and pass the this-pointer as first parameter.

Note that if you have both base classes and a base class wrapper, you must give both bases and the base class wrapper type as template parameter to class_ (as done in the example above). The order in which you specify them is not important. You must also register both the static version and the virtual version of the function from the wrapper, this is necessary in order to allow luabind to use both dynamic and static dispatch when calling the function.

Important

It is extremely important that the signatures of the static (default) function is identical to the virtual function. The fact that one of them is a free function and the other a member function doesn’t matter, but the parameters as seen from lua must match. It would not have worked if the static function took a base_wrapper* as its first argument, since the virtual function takes a base* as its first argument (its this pointer). There’s currently no check in luabind to make sure the signatures match.

If we didn’t have a class wrapper, it would not be possible to pass a Lua class back to C++. Since the entry points of the virtual functions would still point to the C++ base class, and not to the functions defined in Lua. That’s why we need one function that calls the base class’ real function (used if the lua class doesn’t redefine it) and one virtual function that dispatches the call into luabind, to allow it to select if a Lua function should be called, or if the original function should be called. If you don’t intend to derive from a C++ class, or if it doesn’t have any virtual member functions, you can register it without a class wrapper.

You don’t need to have a class wrapper in order to derive from a class, but if it has virtual functions you may have silent errors.

The wrappers must derive from luabind::wrap_base, it contains a Lua reference that will hold the Lua instance of the object to make it possible to dispatch virtual function calls into Lua. This is done through an overloaded member function:

template<class Ret>
Ret call(char const* name, ...)

Its used in a similar way as call_function, with the exception that it doesn’t take a lua_State pointer, and the name is a member function in the Lua class.

Warning

The current implementation of call_member is not able to distinguish const member functions from non-const. If you have a situation where you have an overloaded virtual function where the only difference in their signatures is their constness, the wrong overload will be called by call_member. This is rarely the case though.

Note

You can also override virtual member functions per instance which often makes it unnecessary to derive a new class in Lua. Instead of e.g.

class "D" (B)

function D:__init() B.__init(self) end
function D:virtual_function() ... end

you may be able to get around with

b = B()
function b:virtual_function() ... end

Object identity

When a pointer or reference to a registered class with a wrapper is passed to Lua, luabind will query for it’s dynamic type. If the dynamic type inherits from wrap_base, object identity is preserved.

struct A { .. };
struct A_wrap : A, wrap_base { .. };

A* f(A* ptr) { return ptr; }

module(L)
[
    class_<A, A_wrap>("A"),
    def("f", &f)
];
> class 'B' (A)
> x = B()
> assert(x == f(x)) -- object identity is preserved when object is
                    -- passed through C++

This functionality relies on RTTI being enabled (that LUABIND_NO_RTTI is not defined).

Overloading operators

You can overload most operators in Lua for your classes. You do this by simply declaring a member function with the same name as an operator (the name of the metamethods in Lua). The operators you can overload are:

  • __add
  • __sub
  • __mul
  • __div
  • __pow
  • __lt
  • __le
  • __eq
  • __call
  • __unm
  • __tostring
  • __len

__tostring isn’t really an operator, but it’s the metamethod that is called by the standard library’s tostring() function. There’s one strange behavior regarding binary operators. You are not guaranteed that the self pointer you get actually refers to an instance of your class. This is because Lua doesn’t distinguish the two cases where you get the other operand as left hand value or right hand value. Consider the following examples:

class 'my_class'

  function my_class:__init(v)
      self.val = v
  end

  function my_class:__sub(v)
      return my_class(self.val - v.val)
  end

  function my_class:__tostring()
      return self.val
  end

This will work well as long as you only subtracts instances of my_class with each other. But If you want to be able to subtract ordinary numbers from your class too, you have to manually check the type of both operands, including the self object.

function my_class:__sub(v)
    if (type(self) == 'number') then
        return my_class(self - v.val)

    elseif (type(v) == 'number') then
        return my_class(self.val - v)

    else
        -- assume both operands are instances of my_class
        return my_class(self.val - v.val)

    end
end

The reason why __sub is used as an example is because subtraction is not commutative (the order of the operands matters). That’s why luabind cannot change order of the operands to make the self reference always refer to the actual class instance.

If you have two different Lua classes with an overloaded operator, the operator of the right hand side type will be called. If the other operand is a C++ class with the same operator overloaded, it will be prioritized over the Lua class’ operator. If none of the C++ overloads matches, the Lua class operator will be called.

Finalizers

If an object needs to perform actions when it’s collected we provide a __finalize function that can be overridden in lua-classes. The __finalize functions will be called on all classes in the inheritance chain, starting with the most derived type.

...

function lua_testclass:__finalize()
    -- called when the an object is collected
end

Slicing

If your lua C++ classes don’t have wrappers (see Deriving in lua) and you derive from them in lua, they may be sliced. Meaning, if an object is passed into C++ as a pointer to its base class, the lua part will be separated from the C++ base part. This means that if you call virtual functions on that C++ object, they will not be dispatched to the lua class. It also means that if you adopt the object, the lua part will be garbage collected.

+--------------------+
| C++ object         |    <- ownership of this part is transferred
|                    |       to c++ when adopted
+--------------------+
| lua class instance |    <- this part is garbage collected when
| and lua members    |       instance is adopted, since it cannot
+--------------------+       be held by c++.

The problem can be illustrated by this example:

struct A {};

A* filter_a(A* a) { return a; }
void adopt_a(A* a) { delete a; }
using namespace luabind;

module(L)
[
    class_<A>("A"),
    def("filter_a", &filter_a),
    def("adopt_a", &adopt_a, adopt(_1))
]

In lua:

a = A()
b = filter_a(a)
adopt_a(b)

In this example, lua cannot know that b actually is the same object as a, and it will therefore consider the object to be owned by the C++ side. When the b pointer then is adopted, a runtime error will be raised because an object not owned by lua is being adopted to C++.

If you have a wrapper for your class, none of this will happen, see Object identity.

Table Of Contents

Previous topic

Object

Next topic

Exceptions