Lua、C++和贫民子类化
我是Bitfighter的首席开发者,我们使用Lua和C ++的混合使用,使用Lunar(此处提供的Lunar变种)将它们绑在一起 。
我知道这种环境对面向对象和继承没有很好的支持,但我希望找到一种至少部分解决这些限制的方法。
这是我拥有的:
C ++类结构
GameItem
| ---- Rock
| ---- Stone
| ---- RockyStone
机器人
机器人实现了一个名为_getFiringSolution(GameItem item)的方法,该方法查看_item_的位置和速度,并返回机器人需要射击以击中_item_的角度。
--这是在Lua中
angle =机器人:getFiringSolution(rock)
if(angle!= nil)then
机器人:火(角度)
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
我认为你尝试在错误的地方进行方法分派。(这个问题是所有这些“自动化”使Lua与C或C ++交互的困难的症状:对于每一个,都有一些魔法在幕后进行,它并不总是很明显如何使其工作。我不理解为什么更多的人不只使用Lua的C API。)
我查看了 Lunar 网页,看起来你需要在类型 T
上创建一个名为 methods
的表,然后调用 Luna<T>::Register
方法。网上有一个简单的例子。如果我阅读代码正确,你的问题中的粘合代码实际上不是使用 Lunar 推荐的方式。(我也假设你能够完全使用C ++来实现这些方法。)
这些都相当可疑,因为关于 Lunar 的文档还比较简单。
一个合理的替代方案是自己完成所有的工作,只需将每个C++类型与包含其方法的Lua表相关联即可。然后你就可以让Lua的 __index
元方法参考该表,就大功告成了。 Lunar 正在做一个 接近 这些的事情,但它被 C++ 模板吸引得太过深入,因此我不确定如何使它起作用。
模板的东西非常聪明。您可能希望花时间深入了解其工作原理,或重新考虑是否以及如何使用它。
摘要:对于每个类,制作一个明确的方法表,并使用 Lunar 的 Register
方法注册每个类。或者自己动手。
我建议在纯 Lua 中定义一个面向对象系统,然后为 API 的这个方面编写自定义的 C++ 绑定。
Lua 很适合原型 OO 实现,其中使用表格来模拟类,在其中一个条目具有称为 new 的函数,当调用它时,它返回一个相应的表格,类型相同。
但是从 C++ 中,制作一个具有 .invoke 方法的 LuaClass,接受一个 C 字符串(即一个以空字符结尾的 const char 数组)来指定您想调用的成员函数的名称,根据你想要处理可变参数的方式,如果需要可以定义几个模板化版本的这个 .invoke 方法,或者定义一种方法将可变数量的参数传递进去,有许多方法可以实现。
对于 Lua,我建议制作两个 .invoke 方法,一个方法期望 std::vector,另一个方法则期望 std::map,但这由您决定。 :)
在我的上一个 Lua/C++ 项目中,我仅使用了以空字符结尾的 C 字符串数组,需要 Lua 将字符串转换为适当的值。
享受。
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
你应该告诉我们你的代码出了什么问题。我推测失败的是
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[getmetatable(rock)](robot, rock)