2

1 つまたは複数の型パラメーターに基づいてクラス テンプレートを作成する場合、実行時にこれらの型をどのようにクエリしますか?

例えば:

template <typename T>
class Foo {
 public:
  typedef T TypeT;

  Foo() {}

  // assume i is in range
  void set(size_t i, T value) { store[i] = value; }
  T get(size_t i) { return store[i]; }

  void fast_copy(T* dest) { memcpy(dest, store, 100 * size_of(T)); }

  // ugly public data member
  T store[100];
};


void main() {
  Foo<double> fd;
  Foo<int> fi;

  // this is not C++!
  if (fd::TypeT == fi::TypeT) {

    // do a fast copy since the types match
    fd.fast_copy(fi.store);

  } else {

    // do a slower item by copy since the types don't match
    // and let the runtime perform the type conversion:
    for (size_t i = 0; i < 100; ++i) {
      fi.set(i, static_cast<fd::TypeT>(fd.get(i)));
    }
  }
}

これを行う理由は、float、double、int のいずれかの配列を含むランタイム オブジェクトがあり、それらを別の型の配列にコピーしたいためですが、それらの型がどのようなものになるかわかりません。それらは実行時に決定されます。上記の例では、fi と fd をインスタンス化するために使用される型はわかっていますが、完全な例では、fi と fd は、任意の基本的な数値型の配列のコンテナーである可能性があるポリモーフィック クラスへの参照になります。

このメカニズムは、float、double、または int の配列を返す一連の関数と、float、double、または int の配列を必要とする別のセットとの間の実行時に構成可能なブリッジとなることを目的としています。橋の両側。ブリッジは自明ではなく、他にもいくつかのことを行いますが、ある型の配列 (または配列のコンテナー) を受け取り、別の型の配列 (または配列のコンテナー) を別の型で生成する必要があります。タイプが同じ場合とそうでない場合があります。

注:これは私の以前の質問に関連しており、これがより理にかなっている場合は置き換えます。

編集:私がやろうとしていることについてもう少し情報があります。

私は、一方が数値結果を返す一連の関数で構成されるシステムを持っています。単一の float または double を返すものもあれば、単一の int を返すものもあれば、float、double、または int の配列を変更する (したがって「返す」) ものもあります。たくさんあります。それぞれ (実際には、返されたデータだけで、関数は問題ではありません) を「ソース」オブジェクトと呼ぶものに関連付けたいと考えていますSource<T>。単一の値または配列を処理SourceScalar<T>する必要があるかもしれませんが。SourceVector<T>とにかく、このオブジェクトは「橋」への入り口です。各Source<T>オブジェクトはソース オブジェクトの異種コレクションに格納され、一意の「ソース キー」によって参照されます。たとえば、そのようなソース関連関数の 1 つは、ウェーブテーブル オシレーターからの最新の出力を表す 32 個の float の配列を返す場合があります。別の例では、32 サンプルのピーク検出器出力を表す 32 double の配列を返す場合があります。

システムの反対側には、さらに別の機能セットがあります。これらはすべてパラメータとして数値を取ります。単一の float または double を必要とするものもあれば、単一の int を必要とするものもあれば、同じ配列を必要とするものもあります。これも一杯あります。上記のようにDest<T>、「Dest」オブジェクトと呼ばれるものがあります。このオブジェクトは、必要に応じて関数を呼び出すことができるように、これらの関数のいずれかに関連付けられた std::function ラッパーを格納します。各 Dest オブジェクトは、異種の Dest オブジェクトのコレクションに格納され、一意の「Dest キー」によって参照されます。たとえば、そのような関数の 1 つは、サイズが 32 の配列を受け取ることを期待しているが、浮動小数点数ではない double の配列を受け取ることを期待するデジタル フィルターである可能性があります。DestScalar<T>DestVector<T>

現在、実行時に、任意の Source を任意の Dest に関連付けるために、より高いレベルのシステム (実際にはユーザーによって制御される) が必要です。ユーザーが source と dest の 2 つのキーを指定すると、システムは一方のデータをもう一方の関数に「接続」します。したがって、上記の例では、ウェーブテーブル オシレーターの 32 個の float 配列をデジタル フィルターの 32 個の double 配列パラメーターに接続しようとする場合があります。この関連付けもいつでも削除される可能性があります。ピーク検出器の 32-double 配列がデジタル フィルターの 32-double 配列パラメーターに接続されている場合、配列が同じタイプであるため、転送/コピーをできるだけ高速にする必要があります。

編集 2:これはコンパイル可能なコードであり、Source オブジェクトと Dest オブジェクトの 2 つのコレクションを作成し、それらを動的に「接続」しようとする最も単純なケースです。最初の 2 つの呼び出しはsource_collection[1]->set(...)正しく機能しますが、2 番目の 2 つの呼び出しは機能せず、最終的には の関数set_itemではなく、BaseDest 基本クラス メンバー関数を呼び出しDest<T>ます。これはまた、クエリされた型が同じ場合に typeid() が静的ポインターを返すという観察を使用し、これらのポインターのを比較して、型が一致することを判断します。これはおそらく安全ではなく、代わりに列挙型を使用するのが最善かもしれません。

これはすべて私にとって非常に恐ろしいことです-もっと良い方法があるに違いありませんか?

#include <vector>
#include <typeinfo>
#include <cassert>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>

class Source;    // fwd
class BaseDest;  // fwd

typedef boost::shared_ptr<Source>   SourcePtr;
typedef boost::shared_ptr<BaseDest> BaseDestPtr;

// target function that takes an array of doubles
void target_func_vd(double *array, size_t len) {
  for (size_t i = 0; i < len; ++i) {
    assert(array[i] == i);
  }
}

// target function that takes an array of floats
void target_func_vf(float *array, size_t len) {
  for (size_t i = 0; i < len; ++i) {
    assert(array[i] == i);
  }
}

// base class for Dest
class BaseDest {
public:
  BaseDest() {}
  virtual ~BaseDest() {}

  virtual void set(float *array, size_t len)  { /* not implemented, but can't be pure */ };
  virtual void set(double *array, size_t len) { /* not implemented, but can't be pure */ };

  virtual void set_item(size_t index, double item) { /* not implemented, but can't be pure */ };
  virtual void set_item(size_t index, float item) { /* not implemented, but can't be pure */ };
  virtual void finished(size_t len) = 0;

  virtual const std::type_info* get_type_info() const = 0;
private:
};

template <typename T>
class DestVector : public BaseDest {
public:
  typedef boost::function<void (T *, size_t)> Callable;

  explicit DestVector(Callable callable) : callable_(callable) { }

  virtual void set(T *array, size_t len) { callable_(array, len); }
  virtual void set_item(size_t index, T item) { buffer_[index] = item; }
  virtual void finished(size_t len) { callable_(buffer_, len); }

  virtual const std::type_info* get_type_info() const { return &typeid(T); };
private:
  Callable callable_;
  T buffer_[256];
};

// 'set' is overloaded by array type
class Source {
public:
  Source() {}
  void connect(const BaseDestPtr& dest) { dest_ = dest; }

  void set(float *array, size_t len) {
    if (dest_->get_type_info() == &typeid(double)) {
      // convert to double
      for (size_t i = 0; i < len; ++i) {
        dest_->set_item(i, array[i]);  // calls the base member function
      }
      dest_->finished(len);
    } else if (dest_->get_type_info() == &typeid(float)) {
      dest_->set(array, len);
    }
  }

  void set(double *array, size_t len) {
    if (dest_->get_type_info() == &typeid(float)) {
      // convert to float
      for (size_t i = 0; i < len; ++i) {
        dest_->set_item(i, array[i]);  // calls the base member function
      }
      dest_->finished(len);
    } else if (dest_->get_type_info() == &typeid(double)) {
      dest_->set(array, len);
    }
  }

private:
  BaseDestPtr dest_;
};


void main() {

  // test arrays
  float float_array[256];
  for (size_t i = 0; i < 256; ++i) {
    float_array[i] = static_cast<float>(i);
  }

  double double_array[256];
  for (size_t i = 0; i < 256; ++i) {
    double_array[i] = static_cast<double>(i);
  }

  // collection of Sources
  std::vector<SourcePtr> source_collection;

  SourcePtr s0(new Source());
  source_collection.push_back(s0);

  SourcePtr s1(new Source());
  source_collection.push_back(s1);


  // collection of Dests
  std::vector<BaseDestPtr> dest_collection;

  BaseDestPtr t0(new DestVector<float>(&target_func_vf));
  dest_collection.push_back(t0);

  BaseDestPtr t1(new DestVector<double>(&target_func_vd));
  dest_collection.push_back(t1);


  // create and invoke connections
  source_collection[0]->connect(dest_collection[0]);
  source_collection[0]->set(float_array, 256);  // this should end up passing float_array to target_func_vf, and it does

  source_collection[0]->connect(dest_collection[1]);
  source_collection[0]->set(double_array, 256); // this should end up passing double_array to target_func_vd, and it does

  source_collection[1]->connect(dest_collection[0]);
  source_collection[1]->set(double_array, 256); // this should end up passing double_array to target_func_vf, but it doesn't

  source_collection[1]->connect(dest_collection[1]);
  source_collection[1]->set(float_array, 256); // this should end up passing float_array to target_func_vd, but it doesn't
}
4

1 に答える 1

0

オーバーロードを使用できます。これをクラスに追加します

void copy(T & dest)
{
   fast_copy(&dest):
}

template<class U>
void copy(U & dest)
{
   for (size_t i = 0; i < 100; ++i) {
      dest.set(i, static_cast<TypeT>(get(i)));
}

PS: より多くの C++ の方法は、コピー コンストラクターまたは代入演算子を定義することです。

編集: 動的な mke のものをより多形的にしたい場合

struct BaseDest
{
   virtual void assign(const double * v, size_t cnt) = 0;
   virtual void assign(const float * v, size_t cnt) = 0;
//etc
}

template<class T>
struct DestImpl
{
   void assign(const double * v, size_t cnt)
   {
       assign_impl(v, cnt);
   }
   void assign(const float * v, size_t cnt)
   {
       assign_impl(v, cnt);
   }

   template<class U>
   void assign_impl(const U * v, size_t cnt)
   {
      for (size_t i = 0; i < cnt; ++i) {
         set(i, static_cast<T>(v[i])));
   }

   template<>
   void assign_impl<T>(const T * v, size_t cnt)
   {
      fast_copy(v, cnt);
   }
//stuff
}

struct Source
{
//blah
   template<class T>
   void set(const T * v, size_t cnt)
   {
     dest_->set(v, cnt);
   }

//blah
}
于 2013-03-25T09:16:39.933 に答える