我知道这个环境没有很好的支持面向对象和继承,但我想找到一些方法来至少部分解决这些限制.
这是我有的:
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)