我花了很多时间才让 Lua 与 C++ 类一起工作。 Lua 比 C++ 更像是一个 C 风格的 API,但是有很多方法可以将它与 C++ 一起使用。
在 Lua C API 中,指针由 userdata(或 light userdata 表示,它没有元表且不被垃圾回收)。用户数据可以与一个元表相关联,它的作用有点像 Lua 中的一个类。作为该元表一部分的 C 函数包装了 c++ 类的方法,并在 Lua 中充当该类的方法。
考虑一个具有私有成员名称(一个 c 字符串)和年龄(一个 int)的基本人员类。名称由构造函数设置,不能更改。使用 getter 和 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;
}
};
为了首先向 Lua 公开它,我将为所有符合 lua_CFunction 原型的方法编写包装函数,该原型将 lua 状态作为参数并返回一个 int 表示它压入堆栈的值的数量(通常一或零)。
这些函数中最棘手的是构造函数,它将返回一个像对象一样的 Lua 表。为此,lua_newuserdata 用于创建指向对象的指针。我假设我们将在 Lua 初始化期间创建一个包含这些 c 函数的元表“Person”。此元表必须与构造函数中的用户数据相关联。
// 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;
}
最后在 Lua 的初始化过程中使用 luaL_register 建立了 Person 元表。
// 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 中还有其他实现 OO 的方法。本文介绍了替代方案:
http://loadcode.blogspot.com/2007/02/wrapping-c-classes-in-lua.html