views:

101

answers:

3

I am looking for a way to transfer the variable addresses back and forth between C++ and Lua. For instance, transferring an object from C++ to Lua and do some processing, then transfer it back to C++.

However, the thing is how can you execute a C++ functions or methods from Lua? Or is a workaround required?

If possible, could you include a code snippet to show me how it is done?

I am aware that I do not fully understand the whole picture yet, so if something is amiss, please correct me.

+1  A: 

I'm not sure if this is what you're looking for but have you tried luabind?

Maybe this question is also of help.

celavek
+3  A: 

See the article at the Lua user's wiki about binding code to Lua. There are several techniques for making a C++ object useful from the Lua side of the fence that range in complexity from just letting Lua hold an opaque pointer that it can't directly use, to a full wrapping exposing all C++ methods and potentially even allowing the object to be extended with methods written in Lua.

For simple functional libraries written in C, it is fairly easy to write your own binding by hand using the Lua API to move between the Lua stack and your library's functions.

For an object based system, it is a lot more work to do by hand, so one of the binding tools listed on that page will make your life a lot easier.

RBerteig
+1  A: 

It took me a lot of fiddling around to get Lua to work well with C++ classes. Lua is much more of a C style API than C++ but there are plenty of ways to use it with C++.

In the Lua C API a pointer is represented by userdata (or light userdata which have no metatables and are not garbage collected). Userdata can be associated with a metatable which will act a bit like a class in Lua. The C functions that are a part of that metatable wrap the c++ class's methods and act as the class's methods in Lua.

Consider a basic person class with private members name (a c string) and age (an int). Name is set by the constructor and can not change. Age is exposed with a getter and setter:

class person
{
  private:  
    const char* name;
    int age;
  public:
    person(const char* n) {
        name = strdup(n);
    }
    ~person() {
        free((void*)name);
    }
    void print() {
        printf("%s is %i\n",name, age);
    }
    int getAge() {
        return this->age;
    }
    void setAge(int a) {
        this->age=a;
    }
};

To expose this to Lua first I will write wrapper functions for all of the methods that conform to the lua_CFunction prototype which takes the lua state as an argument and returns an int for the number of values it pushed on to the stack (usually one or zero).

The trickiest of these functions is the constructor which will return a Lua table which acts like an object. To do this lua_newuserdata is used to create a pointer to the pointer to the object. I will assume that we are going to create a meta table "Person" during the Lua init which contain these c functions. This meta table must be associated with the userdata in the constructor.

// wrap the constructor
int L_newPerson(lua_State* L) {
    //pointer to pointer
    person **p = (person **)lua_newuserdata(L, sizeof(person *));
    //pointer to person
    *p = new person(lua_tostring(L, 1)); 
    // associate with Person meta table
    lua_getglobal(L, "Person"); 
    lua_setmetatable(L, -2); 
    return 1;
}

When the other methods are created, you just have to remember that the first argument will always be the pointer to the pointer we created with newPerson. To get the C++ object from that we just de-reference the return from lua_touserdata(L, 1);.

int L_print(lua_State* L) {
    person** p = (person**) lua_touserdata(L, 1);
    (*p)->print();
    return 0;
}

int L_getAge(lua_State* L) {
    person** p = (person**) lua_touserdata(L, 1);
    lua_pushnumber(L, (*p)->getAge());
    return 1;
}

int L_setAge(lua_State* L) {
    person** p = (person**) lua_touserdata(L, 1);
    (*p)->setAge(lua_tonumber(L, 2));
    return 0;
}

Finally the Person meta table is set up using luaL_register during the initializing of Lua.

// our methods...
static const luaL_Reg p_methods[] = {
    {"new", L_newPerson},{"print", L_print},
    {"getAge", L_getAge},{"setAge", L_setAge}, 
    {NULL, NULL}
};

lua_State* initLuaWithPerson() {
    lua_State* L=lua_open();
    luaL_openlibs(L);
    luaL_register(L, "Person", p_methods);  
    lua_pushvalue(L,-1);
    lua_setfield(L, -2, "__index"); 
    return L;
}

and to test it...

const char* Lua_script = 
    "p1=Person.new('Angie'); p1:setAge(25);"
    "p2=Person.new('Steve'); p2:setAge(32);"
    "p1:print(); p2:print();";

int main() {
    lua_State* L=initLuaWithPerson();
    luaL_loadstring(L, Lua_script);
    lua_pcall(L, 0, 0, 0);
    return 0;
}

There are other ways to implement OO in Lua. This article covers alternatives: http://loadcode.blogspot.com/2007/02/wrapping-c-classes-in-lua.html

Nick