SIDEBAR
»
S
I
D
E
B
A
R
«
Binding C++ to Lua, Part 1; The Basics
May 8th, 2010 by dholm

I have been working on binding C++ to Lua and found that even though there are some good samples available on the lua-users wiki none of them really offers a complete or clean implementation except for Luabind which requires a lot of Boost components which might be unsuitable for embedded systems. Since I’ve never worked with Lua before this proved to be a good project to learn the rules and inner workings of the language.

If you haven’t worked with Lua before please read chapter 3 of the Lua 5.1 Reference Manual which provides a good introduction to the Lua C API. It’s important to understand that Lua is stack based and you work by pushing and popping values on and off the stack. In this first part I will show how to register a C++ class with Lua so that it can be instantiated and used from within a Lua script. This part is already well covered in LunaWrapper but I will attempt to break it down and explain each part.

Think of a Lua table as a sort of std::map to use C++ jargon. Class definitions in Lua live inside what is known as a metatable. The metatable is a table for which it is possible to modify the behavior of certain operations such as common binary operators and indexing. We need to use the metatable in order to let Lua know how to create and destroy C++ objects as well as binding object instances to method calls.

Enough talking, let’s have a look at some code. I will start out with how to set up the metatable and register your C++ class with it.

lua_newtable(l);
int methods = lua_gettop(l);

Create a new standard table in which we will store our class’ methods and save the index to it. Remember that Lua is stack based and we need to remember the indices of created variables if we need to reference them later. lua_gettop will return the absolute index of the item at the top of the stack, in this case the newly created table.

luaL_newmetatable(l, T::s_className);
int metatable = lua_gettop(l);

Create the metatable for our class and name if after our class (T::s_className).

lua_pushstring(l, T::s_className);
lua_pushvalue(l, methods);
lua_settable(l, LUA_GLOBALSINDEX);

Lua has three types of environments into which you can put values. We will put our our class metatables into the global environment in order to make the type available to all parts of our scripts. You can read about environments in chapter 2.9 of the reference manual.

We push the class name onto the stack followed by the index of our table of methods, which we will fill later. By calling lua_settable we tell Lua to store the method table in the global environment at the key “T::s_className“. In C++-speak this would be equal to having a std::map GLOBALSINDEX and doing GLOBALSINDEX[T::s_className] = methodArray;

lua_pushliteral(l, "__metatable");
lua_pushvalue(l, methods);
lua_settable(l, metatable);

Sets the metatable at the metatable stack index as the metatable of our method table. Meaning that Lua will now look for meta functions in “metatable” when calling methods from our method table.

lua_pushliteral(l, "__index");
lua_pushvalue(l, methods);
lua_settable(l, metatable);

Set up the indexing method of our metatable to get its values from the methods table. This will allow Lua to look for methods in the methods table whenever a call is made to the metatable (the class).

lua_pushliteral(l, "__gc");
lua_pushcfunction(l, gcT);
lua_settable(l, metatable);

Register a garbage collector for the metatable. Here we use lua_pushcfunction to let Lua know that whenever an instance of our class’ goes out of scope the function gcT should be called to clean up the object. This will allow us to execute our objects destructor and avoid any nasty resource leaks.

lua_newtable(l);
int methodTable = lua_gettop(l);
lua_pushliteral(l, "__call");
lua_pushcfunction(l, newT);
lua_pushliteral(l, "new");
lua_pushvalue(l, -2);
lua_settable(l, methods);
lua_settable(l, methodTable);
lua_setmetatable(l, methods);

In order for us to be able to create class instances in Lua we have to let Lua know how to instantiate a class. That is done by registering a C function, newT, which is called whenever someone performs a “function call” using our class name. You would recognize this as T::s_className().

Create a method table methodTable, set the function call to dispatch to the C function newT, which we will define later, and link it to the metatable of our table of methods.

In order to link methods names with the actual methods we need to set up a type to map them. Lets create a struct containing the name and a method type.

typedef int (T::*Method)(lua_State* l);

typedef struct {
    const char* name;
    Method method;
} MethodMap;

Note that we cannot bind our regular C++ classes directly to Lua. We will need an intermediate since it has to be able to marshall data to and from Lua. Since all arguments are marshalled each method call only needs to know which Lua state to push and pop data to. Our method calls will therefore only need a pointer to the Lua state and return the number of values it has pushed onto the stack.

for (MethodMap* m = classMethods; m->name; ++m) {
    lua_pushstring(l, m->name);
    lua_pushlightuserdata(l, reinterpret_cast<void*>(m));
    lua_pushcclosure(l, thunk, 1);
    lua_settable(l, methods);
}

For each method of our class we have to put the method map into the methods table together with a thunk as a C closure so that the object instance of the callee is passed to the method.

lua_pop(l, 2);

Finally pop the methods and class metatable off the stack and we’re done!

Ok, this wasn’t all that hard as long as you understand the stack concept. But we are missing a couple of things, the constructor, destructor and thunk so let’s move on and define them as well.

We need a type to store the pointer to our class instances in. Using Lua-speak we call them user data so we define a struct Userdata for them.

typedef struct { T* pT; } Userdata;

Now we need a “constructor” that creates instances of our objects.

int newT(lua_State* l) {
    lua_remove(l, 1);
    T* obj = new T(l);
    Userdata* ud = reinterpret_cast<Userdata*>(lua_newuserdata(l, sizeof(Userdata)));
    ud->pT = obj;
    luaL_getmetatable(l, T::s_className);
    lua_setmetatable(l, -2);
    return 1;
}

Remove the item at the top of the stack which is the name of the method call, “new“, which we already know since we have linked this specific function to that call. Create a new object of type T followed by a userdata entry in Lua into which we will put the pointer to our object. Get the metatable associated with the class name of T and connect it with our userdata so that Lua will use the data from our class metatable when working with our object instance. Return one to let Lua know that we put one item at the top of the stack, our object instance.

static int gcT(lua_State* l) {
    Userdata* ud = reinterpret_cast<Userdata*>(lua_touserdata(l, 1));
    T* obj = ud->pT;
    delete obj, obj = NULL;
    return 0;
}

This is the garbage collector for our class type. It will fetch the object instance from the top of the stack and call the C++ delete operator on it. Since we didn’t put anything on the stack this function returns zero.

int thunk(lua_State* l) {
    T* obj = reinterpret_cast<Userdata*>(luaL_checkudata(l, 1, T::s_className))->pT;
    lua_remove(l, 1);
    MethodMap* m = reinterpret_cast<MethodMap*>(lua_touserdata(l, lua_upvalueindex(1)));
    return (obj->*(m->method))(l);
}

Those of you who have ever written object-oriented code in a non object-oriented language will know that the first argument to a “method call” usually is a pointer to the object instance and that is the way it is done in Lua as well. We use luaL_checkudata to get the value at the top of the stack and verify that it is of userdata type. This call will actually return NULL if the item at the top of the stack isn’t of userdata type so we should check the return value but in order to cut down the code for the sake of this post I decided to trim out that particular check. lua_remove will remove the specified number of items from the top of the stack, in this case we remove the userdata from the top so that only the method arguments remain when we make the dispatch.

Since thunk is a C closure we can use lua_upvalueindex to get the method map from the methods table and use that to find out which method was being called. We return the result of the method call which again must be the number of items the method has pushed onto the Lua stack.

This concludes the first part of this series. In the next one I will show you how to use this code to register and use a simple C++ class from within a Lua script.

Luna source code, part 1


3 Responses  
  • Kristian writes:
    May 9th, 201020:30at

    Very nice article!

  • Liam writes:
    June 2nd, 201017:03at

    “I have been working on binding C++ to Lua and found that even though there are some good samples available on the lua-users wiki none of them really offers a complete or clean implementation except for Luabind which requires a lot of Boost components which might be unsuitable for embedded systems.”

    Whilst the aim of the article is to explain Luna, I would like to point out that Luabind is not the only “clean” library available. A library I maintain called OOLua is another example which requires no external dependencies and is faster then Luabind or Swig. The library originally started off as “Luna with extras”, so if you understand Luna you will understand quite a lot of OOLua.

    If you are interested in giving it a try here is the url http://code.google.com/p/oolua/

    Liam

  • dholm writes:
    June 7th, 201020:18at

    Thank you Liam,
    OOLua seems to fulfilll the reuirements I have so I must’ve looked it over. I will check it out and probably steal some ideas from it for future blog entries.

    I’m not really meaning to extend Luna I just don’t wanted to start yet another library so I used it as a base (and an excuse).

    Thanks for visiting my blog!
    David


Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

SIDEBAR
»
S
I
D
E
B
A
R
«
»  Substance:WordPress   »  Style:Ahren Ahimsa
Fork me on GitHub