我对如何将C ++类翻译成Lua类的方式非常感兴趣。我发现了一个很棒的助手类--LunaWrapper(Description here)
但是看起来,在Lua中,唯一坚持类本身的东西(不是在这里谈论结果对象!)只是构造函数。也就是说,我无法调用任何静态函数。
(例如,让我们在LunaWrapper页面上添加一个函数:
static bool Foo::bar(const char* text)
{
printf("in bar\n");
}
)
所以,例如,我想在Lua做什么:
local foo = Foo()
foo:foo()
Foo.bar() -- this should output "in bar", but this wont work! And this is what I want.
foo.bar() -- this should also output "in bar", but this is probably going to work.
我该怎么做?
答案 0 :(得分:1)
经过一段时间的游戏后,我想出了这个:
首先,向Luna添加另一个结构并在类中的另一个表中定义静态函数(只是为了匹配LunaWrapper代码的样式,您可以使用任何您喜欢的方式获取该信息:
struct StaticRegType {
const char *name;
int(*mfunc)(lua_State*); // pointers to static members are C-style func pointers
// and are incompatible with int(T::*mfunc)() ones
};
...
static const Luna<Foo>::StaticRegType StaticRegister[];
...
const Luna<Foo>::StaticRegType Foo::StaticRegister[] = {
{ "bar", &Foo::bar },
{ 0 }
};
现在有趣的部分。不幸的是,事实证明你必须改变相当多的LunaWrapper才能让它做你想做的事。我们不是创建一个名为Foo的函数来调用构造函数,而是创建一个名为Foo的表,并使用__call方法附加一个元表,以便我们维护Foo()构造函数语法(这在Luna :: Register中) :
lua_newtable(L);
... // here we'll insert our manipulations of the table
lua_setglobal(L, T::className); // setglobal will pop the table off the stack
// so we'll do whatever we want to it and then give it
// a name to save ourselves the extra lookup
创建元表并添加构造函数和垃圾收集函数:
luaL_newmetatable(L, T::className);
lua_pushstring(L, "__gc");
lua_pushcfunction(L, &Luna<T>::gc_obj);
lua_settable(L, -3);
lua_pushstring(L, "__call");
lua_pushcfunction(L, &Luna<T>::constructor);
lua_settable(L, -3);
现在我们需要添加所有方法。我们将使用__index元方法 - 两个选项:1。将__index设置为一个cfunction,它接受我们试图从lua调用的名称并运行该函数,或2.将__index设置为包含所有函数的表(有关这两个选项的更多信息,请参阅this。我更喜欢后者,因为它使我们无法循环遍历所有函数,进行令人讨厌的字符串比较,我们可以做一些复制面食并重用Luna的闭包。但它需要我们创建一个新的staticThunk
函数来处理我们的新方法:
static int staticThunk(lua_State *L) {
int i = (int)lua_tonumber(L, lua_upvalueindex(1));
return (*(T::StaticRegister[i].mfunc))(L);
}
注意它更简单,因为我们不需要获取我们调用函数的对象(我也非常喜欢模板抽象,让编译器处理触发正确函数的细节)对于我们的目标,但这只是对Luna作者的一个很好的决定;))。
现在我们需要为__index创建一个表并将方法添加到它。
lua_pushstring(L,"__index"));
lua_newtable(L);
// adding the normal methods is the same as in Luna (the for-loop over T::Register)
// add the static methods by going over our new StaticRegister array (remember to use
// staticThunk for them and thunk for the members
lua_settable(L, -3); // push the __index table to the metatable
几乎就是......这是metatable技巧 - 我们已经构建了Foo表(即使它还没有真正命名为Foo)现在我们将设置我们为对象构建的元表,因为它也是可元化的。这样我们就可以同时执行Foo.bar()
和local foo = Foo(); foo.bar()
:
lua_setmetatable(L, -2); // at this point the stack contains our metatable right under
// our table
lua_setglobal(L, T::className); // name the darn thing
你需要做的最后一件事是摆脱Luna :: constructor中与实际构造对象无关的任何东西(增加的好处 - Register实际注册对象类型,构造函数实际上只是分配它,它的方式应该是,如果你问我)。我们将原来在这里的循环移动到Register函数。我们已经完成了!
注意:此解决方案的一个缺点是,为方便起见,它允许您同时拨打Foo.bar()
和foo.bar()
,同时也允许您拨打Foo.foo()
,这是没有意义的并且会在编译等效的C / C ++程序时被捕获为非法尝试调用成员函数,但在Lua中会因运行时错误而失败。如果你想摆脱这种行为,你必须为不包含成员函数的Foo表创建一个metatable,为你创建的包含它们的对象创建另一个metatable。