如果不将所有可能的几何图形列表存储在某个地方,您将无法逃脱。否则编译器将不知道要生成哪些模板实例。但我想出了一些代码,你必须在一个位置声明该列表,即GeometryTypes
. 其他一切都建立在此之上。我在这里没有使用 vistor 模式,它的好处是您不必将样板代码添加到不同的几何类实现中。实现intersects
所有组合就足够了。
首先包括:我稍后将使用shared_ptr
,打印东西,并在未知几何类型的情况下中止。
#include <memory>
#include <iostream>
#include <cstdlib>
现在使用可用于多态指针的通用基类定义一些几何图形。你必须至少包含一个虚函数,这样你才能得到一个可以在dynamic_cast
以后使用的虚函数表。使析构函数具有多态性可确保即使通过多态指针删除派生类也将被正确清理。
struct Geometry {
virtual ~Geometry() { }
};
struct Circle : public Geometry { };
struct Rectangle : public Geometry { };
现在是您的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;
}
这是我们声明所有几何图形列表的地方。如果您有相互衍生的几何图形,请确保首先拥有最具体的几何图形,因为将尝试这些几何图形以进行动态转换。
template<typename... Ts> class TypeList { };
typedef TypeList<Circle, Rectangle> GeometryTypes;
现在一堆帮助代码。基本思想是迭代一个这样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);
}
};
有了所有这些助手,您现在可以进行多态交叉测试。我正在介绍一个shared_ptr
来存储这样的多态指针,我建议你在你的collidable_object
类中做同样的事情。否则,只要可碰撞对象还活着,您就必须负责确保所引用的几何图形仍然存在,但最终会被清理掉。你想要这样的责任吗?
typedef std::shared_ptr<Geometry> GeomPtr;
bool intersects(GeomPtr lhs, GeomPtr rhs) {
return IntersectHelper1<GeometryTypes, GeometryTypes>::
isects(lhs.get(), rhs.get());
}
最后是一些 main ,这样您就可以在一个小示例中实际运行上述所有代码。
int main() {
GeomPtr g1(new Rectangle), g2(new Circle);
std::cout << intersects(g1, g2) << std::endl;
return 0;
}