Мне потребовалось много усилий, чтобы заставить Lua хорошо работать с классами C ++. Lua - это гораздо больше API в стиле C, чем в C ++, но есть множество способов использовать его с C ++.
В Lua C API указатель представлен пользовательскими данными (или легкими пользовательскими данными , которые не имеют метатаблицы и не являются сборщиком мусора). Пользовательские данные могут быть связаны с метатаблицей, которая будет немного похожа на класс в Lua. Функции C, являющиеся частью этого метатабеля, обертывают методы класса c ++ и действуют как методы класса в Lua.
Рассмотрим базовый класс person с закрытыми именами (строка c) и возрастом (int) Имя задается конструктором и не может быть изменено. Возраст подвергается геттеру и сеттеру:
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;
}
};
Чтобы сначала представить это Lua, я напишу функции-оболочки для всех методов, которые соответствуют прототипу lua_CFunction, который принимает состояние lua в качестве аргумента и возвращает int для числа значений, которые он помещает в стек (обычно один или ноль).
Самой хитрой из этих функций является конструктор, который возвращает таблицу Lua, которая действует как объект. Для этого lua_newuserdata используется для создания указателя на указатель на объект. Я предполагаю, что мы собираемся создать мета-таблицу "Person" во время инициализации Lua, которая содержит эти c функции. Эта мета-таблица должна быть связана с пользовательскими данными в конструкторе.
// 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;
}
Когда создаются другие методы, вам просто нужно помнить, что первым аргументом всегда будет указатель на указатель, который мы создали с помощью newPerson. Чтобы получить объект C ++ из этого, мы просто отменяем ссылку на возврат из 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;
}
Наконец, мета-таблица Person настраивается с использованием luaL_register во время инициализации 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;
}
и проверить это ...
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;
}
Существуют и другие способы реализации ОО в Lua. В этой статье рассматриваются альтернативы:
http://loadcode.blogspot.com/2007/02/wrapping-c-classes-in-lua.html