tir*_*til 5 c++ templates double-dispatch c++11
我\xe2\x80\x99m 试图为学术目的制作一个自定义碰撞引擎,但我陷入了一般的 C++ 编程问题\n我已经拥有了所有可以正常工作的几何图形,并且对于问题的范围,我有这个函数:
\n\ntemplate<typename lhs_geometry, typename rhs_geometry>\nbool intersects( const lhs_geometry& lhs, const rhs_geometry& rhs )\n{\n //returns true if does objects intersects \n //(assume this functions works perfectly with every geometry type)\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n我还有以下课程需要完成实施
\n\ntemplate<typename geometry_type>\nclass collidable_object\n{\npublic:\n explicit collidable_object( geometry_type& geometry ) :\n m_geometry( geometry )\n {\n\n }\n\n ~collidable_object()\n {\n\n }\n\nprivate:\n geometry_type& m_geometry;\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n当我想创建一个列表collidable_object
并测试它们是否有 2 × 2 的交集时,我的问题就出现了。
我在 Google 上做了一些研究,发现有一个基类可以collidable_object
让我将对象存储到列表中。但之后我如何根据对象的具体几何形状来测试对象呢?
我尝试实现访问者模式,但每次都会陷入困境,因为我不想对每种可能的几何类型进行硬编码,因为我总是只调用intersetcs()
.
我还找到了一篇关于合作访客的文章,但这似乎很复杂。
\n\n有谁有简单有效的解决方案吗?
\n\n编辑:我想避免拥有几何图形列表的原因是因为我希望添加新几何图形相对容易,而不必在树状结构中查找文件。
\n\nEDIT2:这里是有关该intersetcs
方法的更多信息:相交方法基于标签调度来查找正确的几何形状,但几乎所有凸形状都使用 GJK 算法,该算法只要求对象可以返回给定方向上最远的点。对于非凸形状,形状被分割成凸子形状并且该过程重新开始。
没有统一的标准来判断是否intersects
能够处理大多数使用的给定形状furthest_along
,但球体叠加球体不能,并且球体聚合也不需要使用furthest_along
附加信息:我使用 VS2012 和 C++11
\n如果不将所有可能的几何形状的列表存储在某个地方,您就无法逃脱。否则编译器将不知道要生成哪个模板实例化。但我提出了一些代码,您必须在其中仅在一个位置(即GeometryTypes
. 其他一切都建立在这一基础上。我在这里没有使用访问者模式,它的好处是您不必向不同的几何类实现添加样板代码。实现intersects
所有组合就足够了。
首先包括:我稍后将使用shared_ptr
,打印内容,并在几何类型未知的情况下中止。
#include <memory>
#include <iostream>
#include <cstdlib>
Run Code Online (Sandbox Code Playgroud)
现在定义一些几何图形,使用可用于多态指针的公共基类。您必须至少包含一个虚拟函数,这样您就可以获得一个可供以后使用的虚拟函数表dynamic_cast
。使析构函数具有多态性可以确保派生类即使通过多态指针删除也能被正确清理。
struct Geometry {
virtual ~Geometry() { }
};
struct Circle : public Geometry { };
struct Rectangle : public Geometry { };
Run Code Online (Sandbox Code Playgroud)
现在你的intersects
模板来了。我只为此演示编写了一个包罗万象的实现。
template<typename lhs_geometry, typename rhs_geometry>
bool intersects(const lhs_geometry& lhs, const rhs_geometry& rhs) {
std::cout << __PRETTY_FUNCTION__ << " called\n"; // gcc-specific?
return false;
}
Run Code Online (Sandbox Code Playgroud)
这是我们声明所有几何图形列表的地方。如果您有彼此派生的几何图形,请确保首先拥有最具体的几何图形,因为将尝试这些几何图形以便进行动态转换。
template<typename... Ts> class TypeList { };
typedef TypeList<Circle, Rectangle> GeometryTypes;
Run Code Online (Sandbox Code Playgroud)
现在是一堆辅助代码。基本思想是迭代一个这样的类型TypeList
并尝试对每种类型进行动态转换。第一个帮助器针对 lhs 参数进行迭代,第二个帮助器针对 rhs 参数进行迭代。如果未找到匹配项,则列表不完整,这将导致应用程序中止并显示一条希望有用的错误消息。
template<typename TL1, typename TL2> struct IntersectHelper1;
template<typename T1, typename TL2> struct IntersectHelper2;
template<typename TL2, typename T1, typename... Ts>
struct IntersectHelper1<TypeList<T1, Ts...>, TL2> {
static bool isects(Geometry* lhs, Geometry* rhs) {
T1* t1 = dynamic_cast<T1*>(lhs);
if (!t1)
return IntersectHelper1<TypeList<Ts...>, TL2>::isects(lhs, rhs);
else
return IntersectHelper2<T1, TL2>::isects(t1, rhs);
}
};
template<typename T1, typename T2, typename... Ts>
struct IntersectHelper2<T1, TypeList<T2, Ts...>> {
static bool isects(T1* lhs, Geometry* rhs) {
T2* t2 = dynamic_cast<T2*>(rhs);
if (!t2)
return IntersectHelper2<T1, TypeList<Ts...>>::isects(lhs, rhs);
else
return intersects(*lhs, *t2);
}
};
// Catch unknown types, where all dynamic casts failed:
bool unknownIntersects(Geometry* g) {
std::cerr << "Intersection with unknown type: "
<< typeid(*g).name() << std::endl;
std::abort();
return false; // should be irrelevant due to abort
}
template<typename TL2>
struct IntersectHelper1<TypeList<>, TL2> {
static bool isects(Geometry* lhs, Geometry* rhs) {
return unknownIntersects(lhs);
}
};
template<typename T1>
struct IntersectHelper2<T1, TypeList<>> {
static bool isects(T1* lhs, Geometry* rhs) {
return unknownIntersects(rhs);
}
};
Run Code Online (Sandbox Code Playgroud)
有了所有这些助手,您现在可以进行多态交叉测试。我正在引入 ashared_ptr
来存储此类多态指针,并且我建议您在类中执行相同的操作collidable_object
。否则,您必须负责确保只要可碰撞对象还活着,引用的几何图形就保持活动状态,但最终会被清理掉。你想要这样的责任吗?
typedef std::shared_ptr<Geometry> GeomPtr;
bool intersects(GeomPtr lhs, GeomPtr rhs) {
return IntersectHelper1<GeometryTypes, GeometryTypes>::
isects(lhs.get(), rhs.get());
}
Run Code Online (Sandbox Code Playgroud)
最后是一些 main,这样您就可以在一个小示例中实际运行上述所有代码。
int main() {
GeomPtr g1(new Rectangle), g2(new Circle);
std::cout << intersects(g1, g2) << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)