繁体   English   中英

lua / C ++绑定具有静态成员的对象

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM