考えられるすべてのジオメトリのリストをどこかに保存せずに逃げることはできません。そうしないと、コンパイラは生成するテンプレートのインスタンス化を認識できません。しかし、そのリストを単一の場所、つまりGeometryTypes
. 他のすべてはそれに基づいています。ここでは vistor パターンを使用していません。これには、さまざまなジオメトリ クラスの実装にボイラープレート コードを追加する必要がないという利点があります。intersects
すべての組み合わせに対して実装するだけで十分です。
最初のいくつかの内容: 後で使用しshared_ptr
て印刷し、不明なジオメトリ タイプの場合は中止します。
#include <memory>
#include <iostream>
#include <cstdlib>
ここで、ポリモーフィック ポインターに使用できる共通の基本クラスを使用して、いくつかのジオメトリを定義します。後で使用できる仮想関数テーブルを取得できるように、少なくとも 1 つの仮想関数を含める必要がありますdynamic_cast
。デストラクタをポリモーフィックにすると、ポリモーフィック ポインタを介して削除された場合でも、派生クラスが適切にクリーンアップされることが保証されます。
struct Geometry {
virtual ~Geometry() { }
};
struct Circle : public Geometry { };
struct Rectangle : public Geometry { };
intersects
これでテンプレートが表示されます。このデモ用に、すべてを網羅する実装を 1 つだけ作成します。
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 引数を反復し、2 番目は 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;
}