2

手動で行う手間をかけずに、テンプレート化された関数ポインターのセットを確立することは可能ですか? これは、私が話していることを説明するための例です。

頻繁に呼び出される関数「write」があり、その 2 つの実装 (write0 と write1) を動的に切り替えられるようにしたいとします。これらの書き込み関数は、引数の型でテンプレート化されています。これを行う 1 つの方法は、内部で if ステートメントを使用するテンプレート化されたフロントエンド関数 write() を使用することです。

これは私のニーズに対して十分に高速であることが判明しましたが、関数ポインターを使用して同じことができるかどうか疑問に思っていました (楽しみのためだけに)。このアプローチの問題点は、関数ポインタの設定が面倒なことです。write() の理想を本質的に達成するが、条件付き (直接静的ディスパッチ) を使用しない他の方法はありますか?

(その他の「ルール」: Msg クラスを write() メソッドを持つように変更することはできず、use サイト コードを変更して Msgs を Msgs のアダプターに置き換えることはできません。)

FWIW、私はこの記事が基本的に私がここで言っているのと同じことを言っているのを見つけました。

#include <iostream>
using namespace std;

template<typename T> void write0(T msg) { cout << "write0: " << msg.name() << endl; }
template<typename T> void write1(T msg) { cout << "write1: " << msg.name() << endl; }

// This isn't so bad, since it's just a conditional (which the processor will
// likely predict correctly most of the time).
bool use_write0;
template<typename T> void write(T msg) { if (use_write0) write0(msg); else write1(msg); }

struct MsgA { const char *name() { return "MsgA"; } };
struct MsgB { const char *name() { return "MsgB"; } };
struct MsgC { const char *name() { return "MsgC"; } };
struct MsgD { const char *name() { return "MsgD"; } };

// This doesn't work: templates may not be virtual.
#if 0
struct Writer { template<typename T> virtual void write(T msg) = 0; };
struct Writer0 { template<typename T> virtual void write(T msg) { cout << "write0: " << msg.name() << endl; } };
struct Writer1 { template<typename T> virtual void write(T msg) { cout << "write0: " << msg.name() << endl; } };
#endif

int main(int argc, char **argv) {
  use_write0 = argc == 1;

  // I can do this:
  write(MsgA());

  // Can I achieve the following without the verbosity (manual setup, named
  // template instantiations, etc.)?
  void (*pwriteA)(MsgA) = use_write0 ? (void(*)(MsgA)) write0<MsgA> : (void(*)(MsgA)) write1<MsgA>;
  void (*pwriteB)(MsgB) = use_write0 ? (void(*)(MsgB)) write0<MsgB> : (void(*)(MsgB)) write1<MsgB>;
  void (*pwriteC)(MsgC) = use_write0 ? (void(*)(MsgC)) write0<MsgC> : (void(*)(MsgC)) write1<MsgC>;
  void (*pwriteD)(MsgD) = use_write0 ? (void(*)(MsgD)) write0<MsgD> : (void(*)(MsgD)) write1<MsgD>;
  pwriteA(MsgA());
  pwriteB(MsgB());
  pwriteC(MsgC());
  pwriteD(MsgD());

  return 0;
}
4

4 に答える 4

4

プログラムの実行中にロギング関数を前後に切り替えたい場合は、タイプごとに関数ポインターを手動で設定する必要があると思います。

起動時にロギング関数を選択するだけで十分な場合は、関数が後で呼び出されるタイプを知らなくても、完全に一般的な方法で実行できます。

// writer functions
template<typename T> void write0(T msg) { std::cout << 0; };
template<typename T> void write1(T msg) { std::cout << 1; };

// global flag
bool use_write0;

// function pointers for all types
template<typename T>
struct dispatch {
   typedef void (*write_t)(T);
   static write_t ptr;
};

// main write function
template<typename T>
inline void write(T msg) {
   (*dispatch<T>::ptr)(msg);
}

// the fun part
template<typename T>
void autoinit(T msg) {
   if (use_write0)
      dispatch<T>::ptr = &write0<T>;
   else
      dispatch<T>::ptr = &write1<T>;
   // call again for dispatch to correct function
   write(msg);
}

// initialization
template<typename T>
typename dispatch<T>::write_t dispatch<T>::ptr = &autoinit<T>;

// usage example
int main(int argc, char **argv) {
   use_write0 = (argc == 1);
   write("abc");
   return 0;
}

タイプごとTに、 の最初の呼び出しで、write<T>()使用する書き込み関数が決定されます。その後の呼び出しでは、その関数への関数ポインターを直接使用します。

于 2009-02-18T10:24:44.920 に答える
1

Don Clugston のFastDelegatesヘッダーを使用することもできます。実行時のオーバーヘッドがまったく発生せず、真にオブジェクト指向のデリゲートが生成されます。それらを使用するための構文は完全ではありませんが、生の関数ポインターをいじるよりは少し簡単です。

于 2009-02-18T10:48:53.473 に答える