c – 模板对象的类似访问者的模式

我正在尝试为学术目的制作一个自定义的碰撞引擎,而我却陷入了一般的c编程问题

我已经拥有了所有正常工作的几何形状,并且对于问题的范围我有这个功能:

template<typename lhs_geometry, typename rhs_geometry>
bool intersects( const lhs_geometry& lhs, const rhs_geometry& rhs )
{
    //returns true if does objects intersects 
    //(assume this functions works perfectly with every geometry type)
}

我还有以下课程,我需要完成实现

template<typename geometry_type>
class collidable_object
{
public:
    explicit collidable_object( geometry_type& geometry ) :
        m_geometry( geometry )
    {

    }

    ~collidable_object()
    {

    }

private:
    geometry_type& m_geometry;
};

我的问题出现的地方是我想创建一个collidable_object列表并测试它们的交叉点2乘2.

我已经对Google做了一些研究,发现有一个collidable_object的基类将允许我将对象存储到列表中.但之后如何根据具体的几何形状测试对象?

我试图实现访问者模式,但每次都会卡住,因为我不想硬编码每种可能的几何类型,因为我总是只调用intersetcs().

我还在cooperative visitor上发现了一篇文章,但这似乎很复杂.

有没有人有一个简单有效的解决方案?

编辑:我想避免使用几何列表的原因是因为我希望它相对容易添加新的几何,而不必在树枝中找到文件.

EDIT2:以下是有关intersetcs方法的更多信息:intersects方法基于标签调度以找到正确的几何体,但几乎所有凸形都使用GJK算法,该算法仅要求对象可以返回给定方向上最远的点.对于非凸形状,形状被分割成凸形子形状并且过程重新开始.

没有统一的标准来看看相交是否能够处理给定的形状大多数使用furthest_along但是球体上的球体没有,并且球体agragations也不需要使用furthest_along

附加信息:我使用VS2012和C 11

最佳答案 如果不在某些地方存储所有可能的几何图形列表,就无法逃脱.否则,编译器将不知道要生成哪个模板实例.但是我想出了一些代码,你必须在一个位置,即GeometryTypes的typedef中声明该列表.其他一切都建立在此之上.我这里没有使用vistor模式,这样做的好处是我不必将样板代码添加到不同的几何类实现中.为所有组合实现交叉就足够了.

首先包括:我将在稍后使用shared_ptr,打印东西,并在未知几何类型的情况下中止.

#include <memory>
#include <iostream>
#include <cstdlib>

现在定义一些几何,使用可用于多态指针的公共基类.您必须至少包含一个虚函数,以便获得一个虚拟函数表,以后可以用于dynamic_cast.使析构函数具有多态性,即使通过多态指针删除,也可以确保正确清理派生类.

struct Geometry {
  virtual ~Geometry() { }
};
struct Circle : public Geometry { };
struct Rectangle : public Geometry { };

现在来了你的相交模板.我只为这个演示编写了一个catch-all实现.

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());
}

最后一些主要的,所以你可以在一个小例子中实际运行上述所有代码.

int main() {
  GeomPtr g1(new Rectangle), g2(new Circle);
  std::cout << intersects(g1, g2) << std::endl;
  return 0;
}
点赞