lubyk logo

Lubyk documentation

Lua C++ binding generator Build Status

Create lua bindings by parsing C++ header files using doxygen.

Generated lua bindings do not contain any external dependencies. Dub C++ files are copied along with the generated C++ bindings.

Fork me on GitHub

MIT license © Gaspard Bucher 2014.

Installation

With luarocks:

$ luarocks install dub

Speed

We tried our best to make these bindings as safe and fast as possible, ensuring we do not break return value optimization and we execute as few lines of code between Lua and C++. Even if we did our best to keep the overhead between Lua and C++ as small as possible, calling between Lua and C++ prevents some of the optimizations LuaJIT is very good at.

When needing very fast execution implying lots of Lua and C++ code, either push all data into C++ structures and work from there or push all data into Lua to avoid binding and function calls overhead.

For example, it would be a bad idea to loop through all the pixels of an image using operator[](int i) to implement a filter in Lua. In such a case, use LuaJIT FFI to build a buffer, copy content inside the buffer and work from there.

Use Case

Some of the main reasons for using this binding generator over other solutions such as 'LuaJIT FFI' are:

memorySome libraries put all the burden of memory management on the end-user. Using FFI bindings, you still have to handle memory management. By using 'dub', you get garbage collection protection for pointers. We used this in our bindings for Bullet.
abstractionSometime we want to write C++ wrapper code to keep an API as simple as possible. We did this for custom zmq bindings. See callbacks for another example.
no magicThe generated bindings are easy to read C++ files without needing fancy runtime introspection or advanced C++ features.
customizableMethod bindings can be customized with custom definitions or even entire ".cpp" file templates.

Installation

Install with luarocks:

$ luarocks install dub

.VERSION = '2.2.2'

Current version of 'dub' respecting semantic versioning.

.DEPENDS = {

'lua >= 5.1, < 5.4'

Compatible with Lua 5.1 to 5.3 and LuaJIT

'lub >= 1.0.4, < 2'

Uses Lubyk base library

'xml ~> 1'

Uses Lubyk fast xml library

'yaml ~> 1'

Uses Lubyk yaml library

}

Usage example

The following code is used to generated bindings from all the headers located in include/xml and output generated files inside src/bind.

First we must parse headers. To do this, we create a dub.Inspector which we could also use to query function names, classes and other information on the parsed C++ headers.

local lub = require 'lub'
local dub = require 'dub'

local inspector = dub.Inspector {
  INPUT    = {
    lub.path '|include/xml',
  },
}

We can now generate Lua bindings with the dub.LuaBinder.

local binder = dub.LuaBinder()

binder:bind(inspector, {
  -- Mandatory library name. This is used as prefix for class types.
  lib_name = 'xml',

  output_directory = lub.path '|src/bind',

  -- Remove this part in included headers
  header_base = lub.path '|include',

  -- Open the library with require 'xml.core' (not 'xml') because
  -- we want to add some more Lua methods inside 'xml.lua'.
  luaopen    = 'xml_core',
})

The code above generates files such as:

xml_core.cppThis contains bindings for methods in the 'xml' namespace, constants and also the main 'luaopen_xml_core' function.
xml_Parser.cppThis contains bindings for xml::Parser class.
dub/...Compile time dub C++ and header files.

When building the library, make sure to include all C++ files (including those in the dub folder. You should also include the binding folder in the include path so that #include "dub/dub.h" is properly resolved.

You can view the generated files on xml lib on github.

Compatibility

The bindings generated by dub are heavily tested and are compatible with Lua 5.1, 5.2 and LuaJIT.

There is no external library dependency.

Binding style

Dub generates bindings so that using the library/class from Lua looks as familiar as possible for those knowing C++ code. For example, here is some code based on HelloWorld.cpp from Box2D.

In C++:

// Define the dynamic body. We set its position and call the body factory.
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(0.0f, 4.0f);
b2Body* body = world.CreateBody(&bodyDef);

// Define another box shape for our dynamic body.
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(1.0f, 1.0f);

In Lua:

-- Define the dynamic body. We set its position and call the body factory.
local bodyDef = b2.BodyDef()
bodyDef.type = b2.dynamicBody
bodyDef.position:Set(0.0, 4.0)
local body = world:CreateBody(bodyDef)

-- Define another box shape for our dynamic body.
local dynamicBox = b2.PolygonShape()
dynamicBox:SetAsBox(1.0, 1.0)

Extra features

Apart from supporting many advanced and complicated C++ features (such as inheritance, type casting, overloaded operators, etc). Dub has some nice additions to make working with C++ objects easier.

super

All C++ objects in Lua can be wrapped in a table and behave like the original objects with all the advantages of Lua tables. Simply set 'super' to the userdata in the wrapping class:

local p = xml.Parser() --
print(type(p))
--> userdata

-- Wrap userdata in a Lua table
local t = setmetatable({super = p}, xml.Parser)
t.info = 'some attached information'
-- Methods works just like t.super:parse(some_string)
t:parse(some_string)

automatic cast to parent class

local p = foo.SubClassOfBase()
local b = foo.Base()

If we have a setFriend method which expects Base* type. The following code will automatically check if p is sub-type of Base and cast it. The Base class knows nothing of SubClassOfBase (which could be defined in another module):

-- Cast 'p' to foo.Base
b:setFriend(p)

public class methods

Example:

class Foo {
public:
  static void sayHello() {
    printf("Hello\n");
  }
};

Lua code

Foo.sayHello()
--> Hello

public attributes read/write

Example:

class Foo {
public:
  std::string name;

  void printName() {
    printf("My name is '%s'\n", name.c_str());
  }
};

Lua code

local foo = Foo()
foo.name = 'Prometheus'
foo:printName()
--> My name is 'Prometheus'

Pointer assignment

Pointer assignment is made possible because we use member pointer garbage collection protection. It is thus safer to use in Lua as it is in plain C++ !

Example:

class Foo {
  std::string name;
public:
  Foo(const char *n) : name(n) {}
  Foo *other;

  void printOther() {
    if (other) {
      printf("Other name is '%s'\n", other->name.c_str());
    } else {
      printf("No other\n");
    }
  }
};

Lua code

local a = Foo 'Armand' 
local b = Foo 'Bob'
a.other = b
a:printOther()
--> Other name is 'Bob'

b = nil
collectgarbage 'collect'
--> b is not destroyed because a references it
a:printOther()
--> Other name is 'Bob'

a.other = nil
a:printOther()
--> No other

collectgarbage 'collect'
--> b is destroyed

Operator overloading

All operator[xx]() functions in C++ are translated to native Lua operators when possible. See operators for a complete list.

C++:

Foo x(4);
Foo y(3);
Foo z = x + y; // calls x.operator+(y)

In Lua:

local x, y = Foo(4), Foo(3)
local z = x + y -- calls x:__add(y) which calls operator+()

Callbacks

Calling Lua from C++ is made easy by deriving a class from dub::Thread, this class can then be used to call Lua methods on 'self' in an efficient way. For example, let's say we have a native QWidget class and we want to implement the 'resized' callback:

class Widget : public QWidget, public dub::Thread {
  Q_OBJECT
public:
  Widget(int window_flags)
  : QWidget(NULL, (Qt::WindowFlags)window_flags = 0) {}

  ~Widget() {}
  
protected:
  virtual void resizeEvent(QResizeEvent *event) {
    // If your code is multi-threaded (i.e. no single event loop)
    // you need to use a mutex here or better yet, use a FIFO queue
    // and a pipe between threads.

    // Get 'resized' callback on Lua object
    if (!dub_pushcallback("resized")) return;
    // dub_L is our object's current thread
    lua_pushnumber(dub_L, width);
    lua_pushnumber(dub_L, height);

    // 3 arguments, no return value
    dub_call(3, 0);
  }    
};

Usage in Lua:

win = gui.Widget()

function win:resized(w, h)
  print('Hey why did you resize my window ?')
end

Note that the resized function defined on win object is a regular Lua function and this can be called from Lua as well:

-- manual call to resized callback
win:resized(win:width(), win:height())

Errors in callbacks

If some error occurs during a callback, this error does not disappear in limbo between C++ and Lua (and does not cause the C++ code to crash). The error is captured during the call and either printed out or handled by a custom error function defined on the called object.

Example:

win = gui.Widget()

function win:error(msg)
  print('Something went wrong in my window.', msg)
end

function win:resized(w, h)
  if (w > 300) then
    error('Bird crash!')
  end
end

Custom bindings

Sometimes we need to write custom code either because 'dub' is cannot guess the proper way to call a function or because we want to make it more Lua-like (allowing multiple return values for example). To use custom bindings, pass the path to a folder containing yaml files (one per class). The yaml file for global functions should be named after the namespace (if any) or '_global.yml' if there is no namespace.

Things to note when writing the bindings:

selfThis variable is set before the custom binding code and corresponds to the C++ object the binding is called on. Static methods and plain functions do not have 'self' set.
argumentsArgument types and conversions are handled by dub. You can reference them by their names in the header declaration.
returnIf you are returning a value, you must add the call to return with the number of values pushed on the stack.
arg countIf your method takes a varying number of arguments, you can use 'arg0', 'arg1' to set bindings for each case (see 'showFullScreen' example below).
file nameThe name of the YAML file should reflect the namespace or class being bound. For global functions, use "_global.yaml".
inlineYou can define the custom bindings by passing a lua table instead of a path to dub.LuaBinder.bind. See below for the format.

YAML file format

For example, to define custom bindings for Rect::size, you need to create a 'Rect.yml' file like this:

lua:
  methods:
    size: |
      Point sz = self->size();
      lua_pushnumber(L, sz.x());
      lua_pushstring(L, sz.y());
      return 2;

You do not have to do the arguments type checking. This is done by dub. You do need to return the number of Lua return values if there are any.

You can also define your own methods by using custom headers instead of the original ones. This is what we did for Qt bindings to whitelist the bindings we wanted. We also chose to declare inexistant methods and bind them by hand:

lua:
  methods:
    swapFullScreen: |
      if (!self->isFullScreen()) {
        self->showFullScreen();
      } else {
        self->showNormal();
      }            
    showFullScreen:
      # Varying custom binding on arg count for overloaded-functions.
      arg0: |
        self->showFullScreen();
      arg1: |
        if (enable) {
          self->showFullScreen();
        } else {
          self->showNormal();
        }                  
    globalMove: |
      self->move(
        self->mapToParent(
          self->mapFromGlobal(QPoint(x, y))
        )
      );   

Note that we do not need to write the code testing argument.

Inline custom bindings

This is a quick and dirty solution when you only need to define a couple of custom bindings. In this case, you pass a table to the binder:

local bind = dub.LuaBinder()

binder:bind(inspector, {
  lib_name         = 'foo',
  output_directory = lub.path '|src/bind',
  custom_bindings  = {
    Rect = {
      methods = {
        size = [=[
          Point sz = self->size();
          lua_pushnumber(L, sz.x());
          lua_pushstring(L, sz.y());
          return 2; 
        ]=],
      },
    },
  },
})

The table format is exactly the same as the yaml files except for the 'lua' root key which is not needed.

Other features

As time passes, the undocummented feature list below will shrink. Until then, it's better having an idea what Dub does.

  • pseudo-attributes read/write by calling getter/setter methods.
  • custom read/write attributes (with void *userdata helper, union handling)
  • public static attributes read/write
  • pointer to member (gc protected)
  • cast(default)/copy/disable const attribute
  • natural casting from std::string to string type (can include '\0')
  • class instantiation from templates through typedefs
  • class alias through typedefs
  • bindings for superclass
  • default argument values
  • overloaded functions with optimized method selection from arguments
  • return value optimization (no copy)
  • simple type garbage collection optimization (no __gc method)
  • namespace
  • nested classes
  • class enums
  • global enums
  • build system
  • group multiple bindings in a single library
  • rewrite class or library names
  • native Lua table wrapping setmetatable({super = obj}, Vect)
  • fully tested
  • custom method binding name
  • remove exception catching if never throws

C++ exceptions

The dub library catches all exceptions thrown during binding execution and passes the exceptions as lua errors. Internally, the library itself throws dub::Exception and dub::TypeException.

Verbosity

.warn_level = 5

Default warning level (anything below or equal to this level will be notified). It is usually good to leave this warning level to 5 and fix individual warning messages by explicitely ignoring the problematic function during binding generation.

.warn (level, format, ...)

Warning function. Can be overwriten. The level parameter is a value between 1 and 10 (the higher the level the less important the message).

Classes

CTemplate

(internal) C++ template definition. This is a sub-class of dub.Class.

Class

(internal) A C++ class/struct definition.

Function

(internal) A public C++ function or method definition.

Inspector

The Inspector 'knows' about the functions and classes and can answer queries.

LuaBinder

Use the dub.Inspector to create Lua bindings.

LuaBinderTODO MISSING DOCUMENTATION

#addCustomTypesTODO MISSING DOCUMENTATION

#parseCustomBindingsTODO MISSING DOCUMENTATION

#functionBodyTODO MISSING DOCUMENTATION

#bindNameTODO MISSING DOCUMENTATION

#customTypeAccessorTODO MISSING DOCUMENTATION

#libNameTODO MISSING DOCUMENTATION

#luaTypeTODO MISSING DOCUMENTATION

#toStringBodyTODO MISSING DOCUMENTATION

#resolveTypesTODO MISSING DOCUMENTATION

LuaBinderTODO move this into getParam ?

#openClassesTODO MISSING DOCUMENTATION

MemoryStorage

(internal) This is used to store all definitions in memory.

#findByFullnameTODO MISSING DOCUMENTATION

#findChildTODO MISSING DOCUMENTATION

#findChildForTODO MISSING DOCUMENTATION

#functionsTODO MISSING DOCUMENTATION

#variablesTODO MISSING DOCUMENTATION

#childrenTODO MISSING DOCUMENTATION

#superclassesTODO MISSING DOCUMENTATION

#constantsTODO MISSING DOCUMENTATION

#hasConstantsTODO MISSING DOCUMENTATION

#namespacesTODO MISSING DOCUMENTATION

#resolveTypeTODO MISSING DOCUMENTATION

#fullnameTODO MISSING DOCUMENTATION

#ignoredTODO MISSING DOCUMENTATION

Namespace

(internal) A C++ namespace definition with nested classes, enums and functions.

NamespaceTODO MISSING DOCUMENTATION

#namespacesTODO MISSING DOCUMENTATION

#functionsTODO MISSING DOCUMENTATION

#setNameTODO MISSING DOCUMENTATION

#fullnameTODO MISSING DOCUMENTATION

#childrenTODO MISSING DOCUMENTATION

OptParser

(internal) Simplified yaml parser to retrieve @dub inline options.

OptParserTODO MISSING DOCUMENTATION

OptParserTODO MISSING DOCUMENTATION