Code for Concinnity


Binding C++ classes to Lua: a step by step example for beginners

Well, I lied. This isn’t step by step but I believe this commented source code dump should be more illustrative than any tutorial I’ve read, here we go :)

// fun.cpp
// This is for Lua 5.2, for Lua 5.1, see https://gist.github.com/kizzx2/1594905
 
#include <lua.hpp>
 
#include <iostream>
#include <sstream>
 
class Foo
{
public:
    Foo(const std::string & name) : name(name)
    {
        std::cout << "Foo is born" << std::endl;
    }
 
    std::string Add(int a, int b)
    {
        std::stringstream ss;
        ss << name << ": " << a << " + " << b << " = " << (a+b);
        return ss.str();
    }
 
    ~Foo()
    {
        std::cout << "Foo is gone" << std::endl;
    }
 
private:
    std::string name;
};
 
// The general pattern to binding C++ class to Lua is to write a Lua 
// thunk for every method for the class, so here we go:
 
int l_Foo_constructor(lua_State * l)
{
    const char * name = luaL_checkstring(l, 1);
 
    // We could actually allocate Foo itself as a user data but 
    // since user data can be GC'ed and we gain unity by using CRT's heap 
    // all along.
    Foo ** udata = (Foo **)lua_newuserdata(l, sizeof(Foo *));
    *udata = new Foo(name);
 
    // Usually, we'll just use "Foo" as the second parameter, but I 
    // say luaL_Foo here to distinguish the difference:
    //
    // This 2nd parameter here is an _internal label_ for luaL, it is 
    // _not_ exposed to Lua by default.
    //
    // Effectively, this metatable is not accessible by Lua by default.
    luaL_getmetatable(l, "luaL_Foo");
 
    // The Lua stack at this point looks like this:
    //     
    //     3| metatable "luaL_foo"   |-1
    //     2| userdata               |-2
    //     1| string parameter       |-3
    //
    // So the following line sets the metatable for the user data to the luaL_Foo 
    // metatable
    //
    // We must set the metatable here because Lua prohibits setting 
    // the metatable of a userdata in Lua. The only way to set a metatable 
    // of a userdata is to do it in C.
    lua_setmetatable(l, -2);
 
    // The Lua stack at this point looks like this:
    //     
    //     2| userdata               |-1
    //     1| string parameter       |-2
    // 
    // We return 1 so Lua callsite will get the user data and 
    // Lua will clean the stack after that.
 
    return 1;
}
 
Foo * l_CheckFoo(lua_State * l, int n)
{
    // This checks that the argument is a userdata 
    // with the metatable "luaL_Foo"
    return *(Foo **)luaL_checkudata(l, n, "luaL_Foo");
}
 
int l_Foo_add(lua_State * l)
{
    Foo * foo = l_CheckFoo(l, 1);
    int a = (int)luaL_checknumber(l, 2);
    int b = (int)luaL_checknumber(l, 3);
 
    std::string s = foo->Add(a, b);
    lua_pushstring(l, s.c_str());
 
    // The Lua stack at this point looks like this:
    //     
    //     4| result string          |-1
    //     3| metatable "luaL_foo"   |-2
    //     2| userdata               |-3
    //     1| string parameter       |-4
    //
    // Return 1 to return the result string to Lua callsite.
 
    return 1;
}
 
int l_Foo_destructor(lua_State * l)
{
    Foo * foo = l_CheckFoo(l, 1);
    delete foo;
 
    return 0;
}
 
void RegisterFoo(lua_State * l)
{
    luaL_Reg sFooRegs[] =
    {
        { "new", l_Foo_constructor },
        { "add", l_Foo_add },
        { "__gc", l_Foo_destructor },
        { NULL, NULL }
    };
 
    // Create a luaL metatable. This metatable is not 
    // exposed to Lua. The "luaL_Foo" label is used by luaL
    // internally to identity things.
    luaL_newmetatable(l, "luaL_Foo");
 
    // Register the C functions _into_ the metatable we just created.
	luaL_setfuncs (l, sFooRegs, 0);
 
    // The Lua stack at this point looks like this:
    //     
    //     1| metatable "luaL_Foo"   |-1
    lua_pushvalue(l, -1);
 
    // The Lua stack at this point looks like this:
    //     
    //     2| metatable "luaL_Foo"   |-1
    //     1| metatable "luaL_Foo"   |-2
 
    // Set the "__index" field of the metatable to point to itself
    // This pops the stack
    lua_setfield(l, -1, "__index");
 
    // The Lua stack at this point looks like this:
    //     
    //     1| metatable "luaL_Foo"   |-1
 
    // The luaL_Foo metatable now has the following fields
    //     - __gc
    //     - __index
    //     - add
    //     - new
 
    // Now we use setglobal to officially expose the luaL_Foo metatable 
    // to Lua. And we use the name "Foo".
    //
    // This allows Lua scripts to _override_ the metatable of Foo.
    // For high security code this may not be called for but 
    // we'll do this to get greater flexibility.
    lua_setglobal(l, "Foo");
}
 
int main()
{
    lua_State * l = luaL_newstate();
    luaL_openlibs(l);
    RegisterFoo(l);
 
    int erred = luaL_dofile(l, "fun.lua");
    if(erred)
        std::cout << "Lua error: " << luaL_checkstring(l, -1) << std::endl;
 
    lua_close(l);
 
    return 0;
}

Published by kizzx2, on January 11th, 2012 at 10:32 pm. Filled under: Uncategorized5 Comments

5 Responses to “Binding C++ classes to Lua: a step by step example for beginners”

  1. Hi! Nice example! Been trying to find some information on how to bind c++ and lua manually. Do you have any examples on how to expose a Foo allocated in c++ to lua? Thanks a bunch!

    Comment by uggwar on April 19, 2012 at 6:31 pm



  2. @uggwar

    The above method already exposes Foo from C++ to Lua. You can see the line “Foo:add(3, 4)”. The “Add” method is written in C++, not Lua.

    And then I added “Foo:speak()” from within Lua. That means I can extend a C++ class within Lua.

    To understand how that worked, it is a good idea to really dive into metatables and understand how they work from the guts. Then everything should fall into place.

    Now, in real life, you would probably want to use a framework to bind C++ to Lua instead of coding it by hand like above.

    If you application is not performance critical (e.g. desktop), go with Luabind. It’s easy and elegant.

    For mobile games, try SWIG or toLua++.

    If you need absolute speed or don’t want to introduce a dependency just to bind a couple of classes, you can use the method above.

    Have fun!

    Comment by kizzx2 on April 19, 2012 at 10:45 pm



  3. Hi there! Things are a bit clrearer now, but I’m still having a bit to learn about how to manage resources. I am trying to expose data to lua functions for manipulation, but I don’t want lua to free the resources. I want to be able to allocate objects in C++, send them to lua and then continue using them in my C++ application. If I allocate the userdata in lua, lua should gc it when done.

    Using a framework is nice i guess, but I want to learn a bit more about the inner workings before I use a tool. :-)

    Thanks for your time!

    Comment by uggwar on April 20, 2012 at 2:23 pm



  4. @uggwar

    The trick line here is this “Foo ** udata = (Foo **)lua_newuserdata(l, sizeof(Foo *));”

    Notice that the userdata is a pointer. So the actual object allocated in C++ will never be GC’ed by Lua. If Lua loses references to the object, the Lua GC will deallocate the pointer — an integer

    Lua’s runtime has no knowledge of the C runtime such as how the heap is arranged and what not, so Lua will never GC anything from C++. The GC can only deallocate what it allocated in the first place Remember, deallocating things in C++ must ultimately needs to calling “delete” or “free()”, and your Lua code can’t do that.

    Therefore, in the above code, I allocate the object in C++ and the object always “stays in C++”

    What about the idea of allocating the object itself in Lua? It is a bad idea because it is clumsy for C++ to obtain a pointer to that object. Remember that many GC implementations actually move the object in memory so if the C++ code saved a pointer to the object allocated in Lua, it may go kaboom when the pointer reference has been made obsolete by a compacting GC. What people do in languages like C# is to ask the GC not to move something (“pinning it”) – which makes it quite clumsy and makes GC’s algorithm very complicated.

    So again the above should already satisfy your objective :)

    Comment by kizzx2 on April 20, 2012 at 8:41 pm



  5. I’m experimenting with a FooWrapper that wraps a shared_ptr, and it seems to solve the whole memory management problem. But I am not sure I want to use boost :-) When lua decides to kill the LuaWrapper*, the destructor will dereference the shared_ptr and as long as i’ve got a reference in my c++ code, all is well.

    This way I can create the same type in lua and c++ and not worry about it dieing all of a sudden.

    It’s a robust solution, but not the leanest. :-)

    Thanks again. I’ve learnt alot from your code and comments. The web is littered with lua embedding tutorials that people write the moment the start to grasp this. It’s honestly a sickness of the web :-)

    Comment by uggwar on April 20, 2012 at 9:27 pm



Leave a Reply