SIDEBAR
»
S
I
D
E
B
A
R
«
Binding C++ to Lua, Part 3; Pushing C++ Objects to Lua
October 7th, 2010 by dholm

Part 3 of my Lua C++ binding series has been 95% ready for several months now and I finally got around to finished the last five percent.

In this part I will add code which enables us to return a class instance usable from Lua as a value from a method or function call. By calling the static member push we can put objects onto Lua’s stack (see section 3.1 – The Stack in the Lua reference manual).

First we need to rewrite our garbage collection code so that it is able to differentiate between objects that are managed by Lua and objects that are deleted elsewhere. We will control this by adding a new field to our class instance metatable called “unmanaged“. If the metatable has the unmanaged field we will not delete the C++ object instance when Lua is executing its garbage collector.

static int gcT(lua_State* l) {
    if (luaL_getmetafield(l, 1, "unmanaged")) {
        lua_pushvalue(l, 1);
        lua_gettable(l, -2);
        if (!lua_isnil(l, -1)) {
            return 0;
        }
    }

    Userdata* ud = reinterpret_cast(lua_touserdata(l, 1));
    T* obj = ud->pT;
    delete obj;
    return 0;
}

The allocUserdata allocates room for a light userdata item in the specified table of the specified size and returns a pointer to the data region. Light userdata is a pointer to a C object stored in Lua and we use it to store the pointer to our object instance.

static Userdata* allocUserdata(lua_State* l, Index table, void* key, std::size_t size) {
    Userdata* userdata = NULL;
    lua_pushlightuserdata(l, key);
    lua_gettable(l, table);
    if (lua_isnil(l, -1)) {
        lua_pop(l, 1);
        lua_checkstack(l, 3);
        userdata = reinterpret_cast<Userdata*>(lua_newuserdata(l, size));
        lua_pushlightuserdata(l, key);
        lua_pushvalue(l, -2);
        lua_settable(l, -4);
    }
    return userdata;
}

Weak tables are tables who reference data that is not to be garbace collected by Lua and they are explained in section 2.10.2 of the reference manual. The mode of the weak table defines whether the key, value or both are to be considered weak and protected from collection.

static void weaktable(lua_State* l, const char* mode) {
    lua_newtable(l);
    lua_pushvalue(l, -1);
    lua_setmetatable(l, -2);
    lua_pushliteral(l, "__mode");

The mode field is a string literal whose values are defined in section 2.10.2 of the Lua reference manual.

    lua_pushstring(l, mode);
    lua_settable(l, -3);
}

A subtable to the metatable of our object is used to store the mode setting for garbage collection.

static void subtable(lua_State* l, Index metatable, const char* name, const char* mode) {
    lua_pushstring(l, name);
    lua_gettable(l, metatable);
    if (lua_isnil(l, -1)) {
        lua_pop(l, 1);

If the table hasn’t been defined we create a new one otherwise we just reuse the old one.

        lua_checkstack(l, 3);
        weaktable(l, mode);

Three spaces are allocated on the stack and we put a weak table with the specified mode parameter on the first position.

        lua_pushstring(l, name);
        lua_pushvalue(l, -2);
        lua_settable(l, metatable);

Finally we set metatable[name] = weaktable so that our garbage collection setting is associated with the object instance.

    }
}

Now we have the necessary methods we need so let’s move on to the meat of it all, the push method.

static Index push(lua_State* l, T* instance, bool gc = false) {
    if (!instance) {
        lua_pushnil(l);
        return 0;
    }

First we add a safety measure so that if a NULL instance is pushed we put a nil on Lua’s stack.

    luaL_getmetatable(l, T::s_lunaClassName);
    if (lua_isnil(l, -1)) {
        luaL_error(l, "[Luna::%s] Class %s has not been commited!", __func__, T::s_lunaClassName);
        return 0;
    }
    Index metatable = lua_gettop(l);

Next we attempt to locate the metatable (the implementation) for the class type that we are going to push. If that particular class hasn’t been registered with Lua we raise an error.

    subtable(l, metatable, "userdata", "v");

Since we now have the metatable defining the class API we need to create a subtable to hold the user data (the instance pointer) which we can put on the stack and that makes sense to Lua.

    Userdata* userdata = allocUserdata(l, metatable, instance, sizeof(Userdata));

allocUserdata allocates an entry in metatable[instance] to hold a userdata (see 2.2 – Values and Types) instance for us to store the object instance pointer in.

Now we move on to the final part of the process which is to associate the object instance table with the class definition table and set the appropriate memory management type.

    if (userdata) {
        userdata->pT = instance;
        lua_pushvalue(l, metatable);
        lua_setmetatable(l, -2);

Here we push the class definition (var: metatable) onto the stack and associate the element at stack[-2] (which happens to be our userdata subtable) with it.

        if (!gc) {
            lua_checkstack(l, 3);
            subtable(l, metatable, "unmanaged", "k");
            lua_pushvalue(l, -2)
            lua_pushboolean(l, 1);
            lua_settable(l, -3);
            lua_pop(l, 1);
        }

If we push the object instance with garbage collection disabled we have to add the “unmanaged” subtable that we talked about when we implemented the new garbage collector which acts as a tag to inform it that the object instance should not be automatically garbage collected (i.e. someone has to call delete on it from C++).

    }
    lua_replace(l, metatable);
    lua_settop(l, metatable);
    return metatable;
}

Finally we replace the old metatable with our modified one containing the object instance, reset the stack and then returns the index of our metatable so that Lua knows where we put the object instance.


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=""> <strike> <strong>

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