简体   繁体   English

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

[英]lua/C++ binding of objects with static members

I am very interested in the way how can I translate a C++ class into Lua class. 我对如何将C ++类转换为Lua类非常感兴趣。 And I found a great helper class - LunaWrapper ( Description here ) 我发现了一个很棒的帮助器类-LunaWrapper( 在此处进行说明

But it seems, that in Lua, the only thing that persist of the class itself (not talking about resulting objects here!) is only the constructor. 但是似乎在Lua中,类本身唯一持久的东西(这里不讨论结果对象!)仅是构造函数。 That is, I can't call any static functions if any. 也就是说,我无法调用任何静态函数(如果有)。 (for example, let's add to the example which is described on LunaWrapper page a function: (例如,让我们在LunaWrapper页面上描述的示例中添加一个函数:


static bool Foo::bar(const char* text)
{
    printf("in bar\n");
}
)

So, for example, what I want to do in Lua: 因此,例如,我想在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. 

How do I do that? 我怎么做?

After a bit of playing around, this is what I came up with: 经过一番玩弄之后,这是我想到的:

First, add another struct to Luna and define the static functions in another table within your class (just to match the style of the LunaWrapper code, you can use whatever way you like to get that information: 首先,向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 }
};

Now for the interesting part. 现在开始有趣的部分。 Unfortunately, it turns out you have to change quite a bit of LunaWrapper to get it to do what you want. 不幸的是,事实证明,您必须对LunaWrapper进行大量更改才能使其执行您想要的操作。 Instead of making a function called Foo that calls the constructor, we're going to make a table named Foo and attach a metatable with a __call method, so that we maintain the Foo() constructor syntax (this goes in Luna::Register): 除了创建一个名为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

Creating the metatable and adding the constructor and garbage collection functions: 创建元表并添加构造函数和垃圾回收函数:

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);

Now we need to add all of our methods. 现在,我们需要添加所有方法。 We're going to use the __index metamethod - two options: 1. set __index to a cfunction that takes the name we tried to call from lua and runs the function, or 2. set __index to a table that contains all of our functions (see this for more information on both options). 我们将使用__index元方法-两个选项:1.将__index设置为一个函数,该函数采用我们试图从lua调用的名称并运行该函数,或者2.将__index设置为包含所有函数的表(有关两个选项的更多信息,请参见此内容。 I prefer the latter since it saves us from looping through all our functions, doing pesky string comparisons and we can do some copy-pasta and reuse the closures from Luna. 我更喜欢后者,因为它使我们免于循环访问所有函数,进行讨厌的字符串比较,并且我们可以执行复制粘贴并重用Luna中的闭包。 It does however require us to make a new staticThunk function that will handle our new methods: 但是,这确实需要我们创建一个新的staticThunk函数,该函数将处理我们的新方法:

static int staticThunk(lua_State *L) {
  int i = (int)lua_tonumber(L, lua_upvalueindex(1));
  return (*(T::StaticRegister[i].mfunc))(L);
}

Note it's quite a bit simpler since we don't need to fetch the object on which we're calling the function (I'm also rather fond of the template abstraction that let's the compiler handle the specifics of triggering the right function for our object, but that was just a good decision on the Luna author's part ;) ). 请注意,这相当简单,因为我们不需要获取调用函数的对象(我也很喜欢模板抽象,让编译器处理为对象触发正确函数的细节。 ,但这只是Luna作者方面的好决定;))。

Now we need to make a table for __index and add the methods to it. 现在,我们需要为__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

Almost there... Here's the metatable trickery - we have built the Foo table (even if it's not really named Foo just yet) and now we are going to set the metatable we build for our objects as it's metatable too. 几乎在那里...这是元表的骗局-我们已经构建了Foo表(即使它还没有真正命名为Foo),现在我们将为对象创建的元表也设置为元表。 This way we can do both Foo.bar() and local foo = Foo(); foo.bar() 这样,我们可以同时执行Foo.bar()local foo = Foo(); foo.bar() Foo.bar() local 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

The last thing you need to do is get rid of anything in Luna::constructor that isn't related to actually constructing the object (added benefit - Register actually registers the object type and constructor actually just allocates it, the way it should be if you ask me). 您需要做的最后一件事是摆脱Luna :: constructor中与实际构造对象无关的任何内容(增加的好处-Register实际上注册了对象类型,而构造方法实际上只是分配了它,如果这样的话你问我)。 We moved the loop that was originally in here to the Register function. 我们将原来在这里的循环移到了Register函数。 And we're done! 我们完成了!

Note: One drawback of this solution is that while for convenience it allows you to call both Foo.bar() and foo.bar() it also allows you to call Foo.foo() , which doesn't make sense and would be caught as an illegal attempt to call a member function during compilation of an equivalent C/C++ program, but will fail with a runtime error in Lua. 注意:此解决方案的一个缺点是,尽管为了方便起见,它允许您同时调用Foo.bar()foo.bar()但也允许您调用Foo.foo() ,这没有意义,并且可能是在编译等效的C / C ++程序期间被视为非法尝试调用成员函数,但由于Lua中的运行时错误而失败。 If you want to get rid of this behavior you would have to create one metatable for the Foo table that does not include the member functions and another for the objects you create that includes them. 如果要摆脱这种行为,则必须为不包含成员函数的Foo表创建一个元表,并为您创建的包含它们的对象创建另一个元表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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