テンプレートオブジェクトを作成するファクトリパターンの代替 - C ++
-
18-09-2019 - |
質問
私はCGプロジェクトのためのメッシュのクラスを実装したいのですが、いくつかの問題に実行しています。 ユーザーから:私は何をしたい(OpenGLの、DirectXの、CUDA、...特定のAPIへの負荷など)実装の詳細を隠すメッシュクラスです。メッシュクラスは研究プロジェクトで使用されますので、さらに、このメッシュクラスは非常に柔軟である必要があります。
class Channel {
virtual loadToAPI() = 0;
}
template <class T>
class TypedChannel : public Channel {
std::vector<T> data;
};
template <class T>
class OpenGLChannel : public TypedChannel<T> {
loadToAPI(); // implementation
};
class Mesh {
template<class T>
virtual TypedChannel<T>* createChannel() = 0; // error: no virtual template functions
std::vector<Channel*> channels;
};
class OpenGLMesh {
template<class T>
TypedChannel<T>* createChannel()
{
TypedChannel<T>* newChannel = new OpenGLChannel<T>;
channels.push_back(newChannel);
return newChannel;
};
};
の柔軟性のために、各メッシュは、メッシュのいくつかの局面を記述する1つの位置のチャネル、通常のチャンネル、などのように、実際のチャネルの集合です。チャネルは、いくつかの追加機能を備えたスタンダードのラッパー::ベクトルである。
実装の詳細を非表示にするには、API固有のコードを処理し、各API(OpenGLMesh、DirectXMesh、CUDAMesh、...)の派生クラスがあります。同じことが(APIへのチャネルデータのロードを処理OpenGLChannelなど)チャンネルのために行きます。メッシュは、チャネルオブジェクトのファクトリとして動作します。
しかし、ここで問題です:チャンネルはテンプレートクラスであるので、createChannelは、テンプレート方式でなければならず、テンプレートメソッドは、仮想することはできません。私は必要があるだろうと、テンプレートオブジェクトを作成するためのファクトリパターンのようなものです。誰もが同じような何かを達成することができる方法についてのアドバイスがありますか?
おかげ
解決
これは興味深い問題だが、まずはコンパイラエラーを議論しましょう。
コンパイラとしての機能は、仮想およびテンプレートの両方にすることはできません、と述べました。理由を理解するには、単に実装について考える:時間のほとんどは、仮想関数を持つオブジェクトは、各関数へのポインタを格納する仮想テーブルを持っている。
。 だから何のような仮想テーブルする必要があります:テンプレートの場合ただし、タイプの組み合わせは、できるだけ多くの機能があるのですか?これは、コンパイル時に伝えることは不可能だし、自分のクラスのメモリレイアウトは、仮想テーブルを備えており、をコンパイル時に決定されるを持っています。
今すぐあなたの問題へのます。
最も簡単な解決策は、もちろん、それはすぐに退屈になるので、あなたはそれを聞いていないふりをさせることができ、ちょうど種類ごとに仮想メソッドを書くことになります。
Mesh
は、その後、確かに誰が知っているので、virtual
のインスタンス与えられ、Mesh
される機能を必要としない、様々な種類について知っていることになっていない場合は、どのタイプの関数を呼び出すと?
Mesh* mesh = ...;
mesh.createChannel<int>(); // has it been defined for that `Mesh` ??
一方で、私はOpenGLMesh
はそれが必要になりますTypedChannel
の種類を正確に知っていると仮定します。もしそうなら、私たちは非常に単純なトリックを使用することができます。
struct ChannelFactory
{
virtual ~ChannelFactory() {}
virtual Channel* createChannel() = 0;
};
template <class T>
struct TypedChannelFactory: ChannelFactory
{
};
そして
class Mesh
{
public:
template <class T>
Channel* addChannel()
{
factories_type::const_iterator it = mFactories.find(typeid(T).name());
assert(it != mFactories.end() && "Ooops!!!" && typeid(T).name());
Channel* channel = it->second->createChannel();
mChannels.push_back(channel);
return channel;
} // addChannel
protected:
template <class T>
void registerChannelFactory(TypedChannelFactory<T>* factory)
{
mFactories.insert(std::make_pair(typeid(T).name(), factory));
} // registerChannelFactory
private:
typedef std::map < const char*, ChannelFactory* const > factories_type;
factories_type mFactories;
std::vector<Channel*> mChannels;
}; // class Mesh
これはtype erasure
として知られている非常に強力なイディオムを示しています。あなたはおそらく、あなたが名前を知っていた前であっても、それを使用しました。)
さて、あなたにOpenGLMesh
を定義することができます:
template <class T>
struct OpenGLChannelFactory: TypedChannelFactory<T>
{
virtual Channel* createChannel() { return new OpenGLChannel<T>(); }
};
OpenGLMesh::OpenGLMesh()
{
this->registerChannelFactory(new OpenGLChannelFactory<int>());
this->registerChannelFactory(new OpenGLChannelFactory<float>());
}
そして、あなたはそれが好きで使用します。
OpenGLMesh openGLMesh;
Mesh& mesh = openGLMesh;
mesh.addChannel<int>(); // fine
mesh.addChannel<float>(); // fine
mesh.addChannel<char>(); // ERROR: fire the assert... (or throw, or do nothing...)
私はあなたが必要なものを理解ホープ:P
他のヒント
あなたがメッシュから工場(一部のChannelFactoryを導入する)を抽出することができれば、あなたはテンプレート化工場を使用することができます:
template <class T>
class ChannelFactory
{
public:
virtual TypedChannel<T>* createChannel() = 0;
};
あなたはのChannelFactoryからあなたOpenGLMeshを導出できるより、、、どんなます。
このアプローチの唯一の制限は、あなたがOpenGLMeshに使いたいテンプレートのパラメーター事前に知っておくべきことです。
それ以外の場合はあなたが興味がある可能性がどのように Boost.Any 作品(boost::any
は、任意のタイプの値を保持している)。
私はあなたの全体のデザインが壊れていると思います。
virtual TypedChannel<T>* createChannel() = 0; // error: no virtual template functions
std::vector<Channel*> channels;
この2行は、ただ一緒にどんな意味がありません。あなたの概念上だと思う、コンパイラエラーを修正しようとしないでください。
開始するには、まさにあなたの推理は、仮想メンバーをCreateChannel作りのためにあるのですか?
別の言い方をすると、C ++は、もつれた理解不能なデザインのすべての種類を許可するために知られている言語です。そして、あなたもC ++があまりにもねじれていると考えて何かを設計するために管理します。
チャネルによって、あなたは「空間インデックス」を意味するのですか?
あなたは実装の詳細を非表示にする場合は、なぜあなたは右のあなたのメッシュでそれらを持っているのですか?
あなたは別の例では、二重またはモートン番号、多分フロートの上にテンプレート化、メッシュは同じ基本フォーマットになりたいです。それは、それがロードされるだけの方法を変更する必要がメッシュではありません。