4

CG プロジェクトにメッシュ クラスを実装したいのですが、いくつかの問題が発生しました。私がやりたいのは、実装の詳細 (特定の API へのロードなど: OpenGL、DirectX、CUDA など) をユーザーから隠す Mesh クラスです。さらに、Mesh クラスは研究プロジェクトで使用されるため、この Mesh クラスは非常に柔軟でなければなりません。

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 つの位置チャネル、通常チャネルなどのチャネルのコレクションです。チャネルは、いくつかの機能が追加された std::vector のラッパーです。

実装の詳細を非表示にするために、API 固有のコードを処理する各 API (OpenGLMesh、DirectXMesh、CUDAMesh、...) の派生クラスがあります。同じことがチャネル (API へのチャネル データのロードを処理する OpenGLChannel など) にも当てはまります。メッシュは、Channel オブジェクトのファクトリとして機能します。

ただし、ここに問題があります。チャネルはテンプレート クラスであるため、createChannel はテンプレート メソッドである必要があり、テンプレート メソッドは仮想化できません。私が必要とするのは、テンプレート化されたオブジェクトを作成するためのファクトリー パターンのようなものです。同様のことをどのように達成できるかについて、誰かアドバイスがありますか?

ありがとう

4

4 に答える 4

2

これは興味深い問題ですが、最初にコンパイラエラーについて説明しましょう。

コンパイラが言ったように、関数は仮想とテンプレートの両方にすることはできません。理由を理解するには、実装について考えてみてください。ほとんどの場合、仮想関数を持つオブジェクトには、各関数へのポインターを格納する仮想テーブルがあります。

ただし、テンプレートの場合、タイプの組み合わせと同じ数の関数があります。では、仮想テーブルはどのようにする必要がありますか?コンパイル時に判断することは不可能であり、クラスのメモリレイアウトには仮想テーブルが含まれているため、コンパイル時に決定する必要があります。

さて、あなたの問題に移りましょう。

最も簡単な解決策は、タイプごとに1つの仮想メソッドを作成することです。もちろん、すぐに面倒になる可能性があるため、聞いたことがないふりをしてみましょう。

さまざまなタイプについて知る必要がない場合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

于 2010-02-05T18:44:05.380 に答える
0

チャネルとは、「空間インデックス」を意味しますか?

実装の詳細を非表示にしたい場合、なぜそれらをメッシュに正しく配置するのですか?

メッシュを同じ基本形式にし、場合によってはフロート数、ダブル数、またはモートン数をテンプレート化する必要があります。変更する必要があるのはメッシュではなく、ロードされる方法だけです。

于 2010-02-05T18:07:03.717 に答える
0

あなたのデザイン全体が壊れていると思います。

  virtual TypedChannel<T>* createChannel() = 0; // error: no virtual template functions
  std::vector<Channel*> channels;

これらの 2 つの行は一緒に意味を成しません。コンパイラ エラーを修正しようとしないでください。コンセプトを考え直してください。

まずはじめに、CreateChannel を仮想メンバーにした理由は何ですか?

別の言い方をすれば、C++ は、あらゆる種類の複雑でわかりにくい設計を可能にすることで知られる言語です。そして、あなたは、C++ でさえねじれすぎていると思われるものを設計することができました。

于 2010-02-05T13:35:46.897 に答える
0

メッシュからファクトリを抽出できる場合 (いくつかの ChannelFactory を導入)、テンプレート化されたファクトリを使用できます。

template <class T>
class ChannelFactory
{
public:
   virtual TypedChannel<T>* createChannel() = 0;
};

ChannelFactory から OpenGLMesh を派生させるよりも、、、何でも。

このアプローチの唯一の制限は、OpenGLMesh で使用するテンプレート パラメーターを事前に知っておく必要があることです。

それ以外の場合は、 Boost.Anyの動作 (boost::any任意の型の値を保持する)に興味があるかもしれません。

于 2010-02-05T12:54:09.600 に答える