ファンクタと関数ポインタ両用の入れ物

pthreadのラッパークラスをファンクタに対応させるべく、
clxのソースを見てどうやって関数ポインタとファンクタを一緒くたに扱っているのか調べてみた。
仕組みとしては仮想関数呼び出しをテンプレートクラスで継承する手法になっていた。
こんな感じ。

#include <iostream>
#include <boost/shared_ptr.hpp>

struct IFunc
{
  virtual void run()=0;
};

template<typename FUNC>
struct FuncHolder : public IFunc
{
  FUNC func_;

  FuncHolder(FUNC func) : func_(func){}

  void run()
  {
    func_();
  }
};

//------------------------------------------------------------//
void func(){
  std::cout << "function pointer." << std::endl;
}

struct Functor
{
  void operator()(){ std::cout << "functor." << std::endl; }
};

int main(int argc, char **argv)
{
  {
    IFunc *pFunc=new FuncHolder<void(*)()>(func);
    pFunc->run();
    delete pFunc;
  }

  {
    IFunc *pFunc=new FuncHolder<Functor>(Functor());
    pFunc->run();
    delete pFunc;
  }

  return 0;
}

しかし、いちいちテンプレート引数を指定するのはたるいのでtemplate関数による型の自動検出を利用するためのテンプレート関数を作成する。

template<typename FUNC>
IFunc* getFuncPointer(FUNC func)
{ 
  return new FuncHolder<FUNC>(func);
}

すると・・・

{ 
  IFunc* pFunc=getFuncPointer(func);
  pFunc->run();
  delete pFunc;
}

{ 
  IFunc* pFunc=getFuncPointer(Functor());
  pFunc->run();
  delete pFunc;
}

あとはこれをメンバに持たせたクラスを作って
スレッド作成に一工夫すればできあがりとなる。

class FuncProxy
{ 
  typedef boost::shared_ptr<IFunc> funcPtr;
  funcPtr func_;

public:
  template<typename FUNC>
  FuncProxy(FUNC func)
  : func_(new FuncHolder<FUNC>(func))
  {
  }

  // 仮想関数から関数・ファンクタをコール
  void run(){ func_->run(); }
};

void *enterThread(void *arg)
{ 
  // ここで仮想関数を経由して目的の関数・ファンクタをコールする
  ((FuncProxy*)arg)->run();
  return NULL;
}

//------------------------------------------------------------//
int main()
{ 
  { 
    FuncProxy proxy(func);
    pthread_t id;
    pthread_create(&id, NULL, enterThread, &proxy);
  }

  { 
    Functor functor;
    FuncProxy proxy(functor);
    pthread_t id;
    pthread_create(&id, NULL, enterThread, &proxy);
  }
}

コンストラクタをテンプレート関数として定義してみた。
上記の方法で巧妙なのはテンプレートで変わるのは継承先の具象クラスで
それを保持する方(FuncProxy)は何も変わらないというところ。


仕上げにファンクタをリファレンスで渡すべく、
boost::refに対応させるためにテンプレートの特殊化を追加。

#include <boost/ref.hpp>

// specialization for boost::ref
template<typename FUNC>
struct FuncHolder<boost::reference_wrapper<FUNC> > : public IFunc
{
  typedef boost::reference_wrapper<FUNC> REF;
  REF func;

  FuncHolder(REF _func)
    :func(_func)
  {}

  void run(Thread &thread){ func.get()(thread); }
};

多分こう。

Functor functor;
FuncProxy proxy(boost::ref(functor));
pthread_t id;
pthread_create(&id, NULL, enterThread, &proxy);

もっとよいやり方があるかも。