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