Lua,C和穷人的子类
我是
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,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,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.例如:将导出类型特定的函数getFiringSolutionForRock(),getFiringSolutionForStone()和getFiringSolutionForRockyStone()从C到Lua.在Lua,存储表的方法通过metatable: dispatch = { [Rock] = Robot.getFiringSolutionForRock; [Stone] = Robot.getFiringSolutionForStone; [RockyStone] = Robot.getFiringSolutionForRockyStone; } 如果我是对的,下一行应该调用机器人对象的正确的专门方法. dispatch[getmetatable(rock)](robot,rock) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |