当前位置 : 主页 > 网络编程 > lua >

Lua,C和穷人的子类

来源:互联网 收集:自由互联 发布时间:2021-06-23
我是 Bitfighter的主角,我们正在使用Lua和C的混合,使用Lunar(Luna的一个变种,可用的 here)将它们绑定在一起. 我知道这个环境没有很好的支持面向对象和继承,但我想找到一些方法来至少部分解
我是 Bitfighter的主角,我们正在使用Lua和C的混合,使用Lunar(Luna的一个变种,可用的 here)将它们绑定在一起.

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

这是我有的:

C类结构

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

    Robot

Robot实现了一个名为getFiringSolution(GameItem item)的方法,它查看项目的位置和速度,并返回机器人需要点火的角度.

-- This is in Lua
angle = robot:getFiringSolution(rock)
if(angle != nil) then
    robot:fire(angle)
end

所以我的问题是我想把岩石,石头或rockyStones传递给getFiringSolution方法,我不知道该怎么做.

这仅适用于Rocks:

// C++ code
S32 Robot::getFiringSolution(lua_State *L) 
{
   Rock *target = Lunar<Rock>::check(L, 1);
   return returnFloat(L, getFireAngle(target));    // returnFloat() is my func
}

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

// This is C++, doesn't work
S32 Robot::getFiringSolution(lua_State *L) 
{
   GameItem *target = Lunar<GameItem>::check(L, 1);
   return returnFloat(L, getFireAngle(target));    
}

这个潜在的解决方案不起作用,因为Lunar的检查功能希望堆栈上的对象具有与为GameItem定义的类相匹配的className. (对于您在Lunar注册的每个对象类型,您将以Lunar用于确保对象的正确类型的字符串形式提供一个名称.)

我会解决这样的事情,我必须检查每个可能的子类:

// Also C++, also doesn't work
S32 Robot::getFiringSolution(lua_State *L) 
{
   GameItem *target = Lunar<Rock>::check(L, 1);
   if(!target)
       target = Lunar<Stone>::check(L, 1);
   if(!target)
       target = Lunar<RockyStone>::check(L, 1);

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

此解决方案的问题是,如果堆栈上的项目不是正确的类型,那么检查函数会生成错误,并且我相信从堆栈中删除感兴趣的对象,所以我只有一个尝试抓取它.

我想我需要从堆栈中得到一个指向Rock / Stone / RockyStone对象的指针,找出它是什么类型,然后在使用它之前将其转换为正确的东西.

这个类型检查的农历的关键位是:

// from Lunar.h
// get userdata from Lua stack and return pointer to T object
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;  // pointer to T object
}

如果我这样称呼:

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

那么luaL_checkudata()会检查堆栈中的项目是否是Rock.如果是这样,一切都是桃红色的,它返回一个指向我的Rock对象的指针,该对象被传回给getFiringSolution()方法.如果堆栈上有一个非Rock项目,则该转换将返回null,并且调用luaL_typerror(),将该应用程序发送到lala地(错误处理打印诊断并终止机器人极端偏见).

关于如何推进这一点的任何想法?

非常感谢!!

最好的解决方案我想出…丑陋,但工作

根据以下建议,我想出了这一点:

template <class T>
T *checkItem(lua_State *L)
{
   luaL_getmetatable(L, T::className);
   if(lua_rawequal(L, -1, -2))         // Lua object on stack is of class <T>
   {
      lua_pop(L, 2);                   // Remove both metatables
      return Lunar<T>::check(L, 1);    // Return our object
   }
   else                                // Object on stack is something else
   {
      lua_pop(L, 1);    // Remove <T>'s metatable, leave the other in place 
                        // for further comparison
      return NULL;
   }
}

然后,以后…

S32 Robot::getFiringSolution(lua_State *L)
{
   GameItem *target;

   lua_getmetatable(L, 1);    // Get metatable for first item on the stack

   target = checkItem<Rock>(L);

   if(!target)
      target = checkItem<Stone>(L);

   if(!target)
      target = checkItem<RockyStone>(L);

   if(!target)    // Ultimately failed to figure out what this object is.
   {
      lua_pop(L, 1);                      // Clean up
      luaL_typerror(L, 1, "GameItem");    // Raise an error
      return returnNil(L);                // Return nil, but I don't think this 
                                          // statement will ever get run
   }

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

有可能进一步优化我可以做到这一点…我真的很想弄清楚如何将它折叠成一个循环,因为在现实中,我将有三个以上的课程来处理,而这个过程是有点麻烦

对上述解决方案略有改进

C :

GameItem *LuaObject::getItem(lua_State *L, S32 index, U32 type)
{
  switch(type)
  {
    case RockType:
        return Lunar<Rock>::check(L, index);
    case StoneType:
        return Lunar<Stone>::check(L, index);
    case RockyStoneType:
        return Lunar<RockyStone>::check(L, index);
    default:
        displayError();
   }
}

然后,以后…

S32 Robot::getFiringSolution(lua_State *L)
{
   S32 type = getInteger(L, 1);                 // My fn to pop int from stack
   GameItem *target = getItem(L, 2, type);

   return returnFloat(L, getFireAngle(target)); // My fn to push float to stack  
}

Lua帮助函数,作为一个单独的文件,以避免用户需要手动添加到他们的代码:

function getFiringSolution( item )
  type = item:getClassID()      -- Returns an integer id unique to each class
  if( type == nil ) then
     return nil
   end
  return bot:getFiringSolution( type, item )
end

用户通过Lua呼叫:

angle = getFiringSolution( item )
你应该告诉我们在你的代码中究竟不起作用.我想这是所有非岩石失败的Lunar< Rock> :: check(L,1).我对么?

此外,如果您指定使用哪个版本的农历会很好(一个链接将是很棒的).

如果是this one,那么类类型存储在Lua对象metatable中(可以说这个metatable是类型).

看起来像最简单的方法来检查对象是否是没有修补的Rock,Lunar是调用luaL_getmetatable(L,Rock :: className)来获取类metatable,并将其与第一个参数的lua_getmetatable(L,1)进行比较(注意luaL in第一个函数名).这是有点黑客,但应该工作.

如果你在修补月球的时候很好,可能的方法之一是将一些__lunarClassName字段添加到metatable中,并在其中存储T :: name.提供lunar_typename()C函数(在Lunar模板类之外(因为我们不需要T)),然后返回参数的metatable的__lunarClassName字段的值. (不要忘记检查对象是否具有metatable,并且metatable具有这样的字段).您可以通过调用lunar_typename()来检查Lua对象类型.

个人经验的一点建议:你对Lua的业务逻辑越多越好.除非受到严重的性能限制,否则你可能应该考虑将所有层次结构移到Lua – 你的生活会变得更加简单.

如果我可以进一步帮助你,请说.

更新:您更新了您的帖子的解决方案,看起来正确.

要在C中进行基于metatable的调度,您可以使用例如luaL_getmetatable()的整数值为lua_topointer()的值映射到知道如何处理该类型的函数对象/指针的类型.

但是,我再次建议将这一部分转移到Lua.例如:将导出类型特定的函数getFiringSolutionForRock(),getFiringSolutionForStone()和getFiringSolutionForRockyStone()从C到Lua.在Lua,存储表的方法通过metatable:

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

如果我是对的,下一行应该调用机器人对象的正确的专门方法.

dispatch[getmetatable(rock)](robot, rock)
网友评论