[英]lua/C++ binding of objects with static members
我对如何将C ++类转换为Lua类非常感兴趣。 我发现了一个很棒的帮助器类-LunaWrapper( 在此处进行说明 )
但是似乎在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.
我怎么做?
经过一番玩弄之后,这是我想到的:
首先,向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设置为一个函数,该函数采用我们试图从lua调用的名称并运行该函数,或者2.将__index设置为包含所有函数的表(有关这两个选项的更多信息,请参见此内容。 我更喜欢后者,因为它使我们免于循环访问所有函数,进行讨厌的字符串比较,并且我们可以执行复制粘贴并重用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
几乎在那里...这是元表的骗局-我们已经构建了Foo表(即使它还没有真正命名为Foo),现在我们将为对象创建的元表也设置为元表。 这样,我们可以同时执行Foo.bar()
和local foo = Foo(); foo.bar()
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表创建一个元表,并为您创建的包含它们的对象创建另一个元表。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.