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