Lua、C++和贫民子类化

我是Bitfighter的首席开发者,我们使用Lua和C ++的混合使用,使用Lunar(此处提供的Lunar变种)将它们绑在一起 。

我知道这种环境对面向对象和继承没有很好的支持,但我希望找到一种至少部分解决这些限制的方法。

这是我拥有的:

C ++类结构

GameItem
| ---- Rock
| ---- Stone
| ---- RockyStone

机器人

机器人实现了一个名为_getFiringSolution(GameItem item)的方法,该方法查看_item_的位置和速度,并返回机器人需要射击以击中_item_的角度。

--这是在Lua中
angle =机器人:getFiringSolution(rock)
if(angle!= nilthen
机器人:火(角度)
end

所以我的问题是我想将Rocks,Stones或RockyStones传递给getFiringSolution方法,而我不确定该怎么做。

这仅对Rocks有效:

// C++ code
S32 Robot::getFiringSolution(lua_State *L)
{
   Rock * target = Lunar <Rock> ::check(L,1);
   return returnFloat(L,getFireAngle(target)); // returnFloat()是我的函数
}

理想情况下,我想做的是这样的:

//这是C ++,不起作用
S32 Robot::getFiringSolution(lua_State *L)
{
   GameItem * target = Lunar <GameItem> ::check(L,1);
   return returnFloat(L,getFireAngle(target));
}

这个潜在的解决方案无法正常工作,因为Lunar的检查函数希望堆栈上的对象具有与GameItem中定义的名称相匹配的className。 (对于您向Lunar注册的每种对象类型,都会提供字符串形式的名称,Lunar使用该名称以确保对象类型正确。)

我将接受像这样的东西,其中我必须检查每个可能的子类:

//也是C ++,也不起作用
S32 Robot::getFiringSolution(lua_State *L)
{
   GameItem * target = Lunar <Rock> ::check(L,1);
   if(!target)
目标= Lunar <Stone> ::check(L,1);
   if(!target)
目标= Lunar <RockyStone> ::check(L,1);

   return returnFloat(L,getFireAngle(target));
}

这种解决方案的问题是,如果堆栈上的项目不是正确类型,则检查函数会生成错误,并且我认为会从堆栈中删除所需的对象,因此只有一次尝试来抓取它。

我认为我需要从堆栈中获取Rock / Stone / RockyStone对象的指针,弄清楚它是何种类型,然后在使用之前将其转换为正确的类型。

进行类型检查的关键点是Lunar的这一点:

//从Lunar.h
//从Lua堆栈获取userdata并返回T对象的指针
static T *check(lua_State *L, int narg) {
  userdataType * ud =
    static_cast <userdataType *>(luaL_checkudata(L,narg,T :: className));
  if(!ud)luaL_typerror(L,narg,T :: className);
  return ud->pT; //指向T对象的指针
}

如果我这样调用它:

GameItem * target = Lunar <Rock> ::check(L,1);

然后luaL_checkudata()检查堆栈上的项目是否为Rock。如果是这样,一切就都清楚了,并且它返回指向我的Rock对象的指针,该指针传回了getFiringSolution()方法。如果堆栈上有非Rock项,则转换返回null,并调用luaL_typerror(),该函数将应用程序发送到lala land(在那里,错误处理会打印出诊断信息并以极端偏见终止机器人)。

有任何关于如何继续前进的想法吗?

非常感谢!!

我想到的最好的解决方法…不美观,但有效

根据下面的建议,我想出了这个:

template <class T>
T * checkItem(lua_State * L)
{
   luaL_getmetatable(L,T :: className);
   if(lua_rawequal(L,-1-2))//堆栈上的Lua对象是类<T>
   {
      lua_pop(L,2); //删除两个元表
      返回Lunar <T> ::check(L,1); //返回我们的对象
   }
   else //堆栈上的对象是其他东西
   {
      lua_pop(L,1); //删除<T>的元表,将其他继续保留
      返回

原文链接 https://stackoverflow.com/questions/866362

点赞
stackoverflow用户6236
stackoverflow用户6236

你应该告诉我们你的代码出了什么问题。我推测失败的是 Lunar<Rock>::check(L, 1) 函数,对于所有非 Rock 对象都是如此。我正确吗?

如果你能够指定你使用的 Lunar 版本的话(提供一个链接会更好),那就太好了。

如果是使用的这个,那么类类型被存储在 Lua 对象的元表中(可以说这个元表就是类型)。

似乎在不更改 Lunar 的情况下检查对象是否是 Rock 最简单的方法是调用 luaL_getmetatable(L, Rock::className) 函数以获取类元表,并将其与第一个参数中的 lua_getmetatable(L, 1) 进行比较(注意第一个函数名称中的 lua L)。这有点像黑客行为,但应该可以工作。

如果你准备对 Lunar 进行修补,一种可能的方法是在元表中添加一些 __lunarClassName 字段,并将 T::name 存储在其中。然后提供 lunar_typename() C++ 函数(放在 Lunar 模板类之外——因为我们在那里不需要 T),从它返回参数元表的 __lunarClassName 字段的值。 (不要忘记检查对象是否具有元表以及该元表是否具有这样的字段。)之后可以使用 lunar_typename() 来检查 Lua 对象类型。

根据个人经验提供一点建议:将更多的业务逻辑转移到 Lua 中,将更为优秀。除非你受到严格的性能限制,否则你可能应该考虑将整个层次结构移植到 Lua 中——这样会使你的生活变得更简单。

如果我可以帮你进一步,请告诉我。

更新:你在帖子中更新过的解决方案是正确的。

要在 C 中进行基于元表的分派,例如,可以将 luaL_getmetatable() 返回的整数值映射为一个函数对象/指针的映射表,该函数对象/指针知道如何处理该类型。

但是,我建议将此部分转移到 Lua 中。例如:将 C++ 中的类型特定函数 getFiringSolutionForRock()getFiringSolutionForStone()getFiringSolutionForRockyStone() 导出到 Lua 中。在 Lua 中,通过元表存储方法表:

dispatch =
{
  [Rock] = Robot.getFiringSolutionForRock;
  [Stone] = Robot.getFiringSolutionForStone;
  [RockyStone] = Robot.getFiringSolutionForRockyStone;
}

如果我没错的话,下一行会调用机器人对象的正确专用方法。

dispatch[getmetatable(rock)](robot, rock)
2009-05-15 00:01:31
stackoverflow用户41661
stackoverflow用户41661

我认为你尝试在错误的地方进行方法分派。(这个问题是所有这些“自动化”使Lua与C或C ++交互的困难的症状:对于每一个,都有一些魔法在幕后进行,它并不总是很明显如何使其工作。我不理解为什么更多的人不只使用Lua的C API。)

我查看了 Lunar 网页,看起来你需要在类型 T 上创建一个名为 methods 的表,然后调用 Luna<T>::Register 方法。网上有一个简单的例子。如果我阅读代码正确,你的问题中的粘合代码实际上不是使用 Lunar 推荐的方式。(我也假设你能够完全使用C ++来实现这些方法。)

这些都相当可疑,因为关于 Lunar 的文档还比较简单。 一个合理的替代方案是自己完成所有的工作,只需将每个C++类型与包含其方法的Lua表相关联即可。然后你就可以让Lua的 __index 元方法参考该表,就大功告成了。 Lunar 正在做一个 接近 这些的事情,但它被 C++ 模板吸引得太过深入,因此我不确定如何使它起作用。

模板的东西非常聪明。您可能希望花时间深入了解其工作原理,或重新考虑是否以及如何使用它。

摘要:对于每个类,制作一个明确的方法表,并使用 Lunar 的 Register 方法注册每个类。或者自己动手。

2009-05-15 00:14:55
stackoverflow用户52273
stackoverflow用户52273

我建议在纯 Lua 中定义一个面向对象系统,然后为 API 的这个方面编写自定义的 C++ 绑定。

Lua 很适合原型 OO 实现,其中使用表格来模拟类,在其中一个条目具有称为 new 的函数,当调用它时,它返回一个相应的表格,类型相同。

但是从 C++ 中,制作一个具有 .invoke 方法的 LuaClass,接受一个 C 字符串(即一个以空字符结尾的 const char 数组)来指定您想调用的成员函数的名称,根据你想要处理可变参数的方式,如果需要可以定义几个模板化版本的这个 .invoke 方法,或者定义一种方法将可变数量的参数传递进去,有许多方法可以实现。

对于 Lua,我建议制作两个 .invoke 方法,一个方法期望 std::vector,另一个方法则期望 std::map,但这由您决定。 :)

在我的上一个 Lua/C++ 项目中,我仅使用了以空字符结尾的 C 字符串数组,需要 Lua 将字符串转换为适当的值。

享受。

2009-05-15 00:29:02