トランポリン

http://lists.boost.org/MailArchives/boost/msg70484.phpを参考に「だいたいこんな感じだろー」とか適当に解釈して組んでみたら動いてしまった・・・(@VC++7.1).自分でもびっくり.っていうかトランポリンすげー面白い.

#include <iostream>

// トランポリン(この小さな関数テンプレートが一番の主役)
template<class Functor>
void trampoline(void *pf)
{
  (*(static_cast<Functor*>(pf)))();
}

// call_back_on_destructionがデフォルトで呼び出す関数
void null_action()
{
}

// 登録したvoid (void)な関数あるいはファンクタを,デストラクタで呼び出すクラス
class call_back_on_destruction
{
public:
  call_back_on_destruction(void (*pf)() = &null_action)
  {
    p_trampoline = &trampoline<void ()>;
    this->pf = pf;
  }

  template<class Functor>
  call_back_on_destruction(Functor f)
  {
    p_trampoline = &trampoline<Functor>;
    pf = &f;
  }

  ~call_back_on_destruction()
  {
    (*p_trampoline)(pf);
  }

  call_back_on_destruction &operator=(void (*pf)())
  {
    p_trampoline = &trampoline<void ()>;
    this->pf = pf;
    return *this;
  }

  template<class Functor>
  call_back_on_destruction &operator=(Functor f)
  {
    p_trampoline = &trampoline<Functor>;
    pf = &f;
    return *this;
  }

private:
  void (*p_trampoline)(void*);
  void *pf;
};

struct functor1
{
  void operator()() const
  {
    std::cout << "functor1" << std::endl;
  }
};

struct functor2
{
  void operator()() const
  {
    std::cout << "functor2" << std::endl;
  }
};

void g()
{
  std::cout << "g" << std::endl;
}

int main()
{
  {
    call_back_on_destruction a;
  }
  {
    call_back_on_destruction a((functor1()));
  }
  {
    call_back_on_destruction a((functor2()));
  }
  {
    call_back_on_destruction a(&g);
  }
  {
    call_back_on_destruction a((functor1()));
    a = functor2(); // 動的に交換しても全然大丈夫
  }
  {
    call_back_on_destruction a((functor1()));
    a = &g; // 同様
  }

  return 0;
}

普通,ファンクタをコールバックするクラスはファンクタの型を知っていないといけないはずだけれど,このcall_back_on_destructionはファンクタの型が表面に出ていない.にもかかわらず関数ポインタだろうがファンクタだろうが統一的に保持できて(継承関係も何もない型同士をテンプレートのような型情報を表に出さずに統一的に扱っているように見える!),なおかつ動的にそれを交換できている(継承関係も何もない型を動的に交換しているように見える!)というところがミソ.個人的には「保持しているファンクタの型を消している」という表現が似合うと思う.
じゃ,型の情報はどこへ行ったのかというとトランポリン関数の各々のオーバーロードが型の情報を保持している.型の区別をトランポリン関数のディスパッチに押し付けてしまっているといっても良さそう.この観点から見れば,boost::shared_ptrの実装もある意味同じなのかも知れない.(boost::shared_ptrは仮想関数のディスパッチによってdeleterの型を区別していると言える)
長い間boost::functionの実装が不思議で仕方なかったけれど,多分これが基本なんだろうな.不思議な手品はタネの見当も付かないうちは戦々恐々だけれども,手品のタネに見当が付くと途端に「なんだそんなものか」って態度を大きく出来ますな,わっはっは.
ところで,MC++Dのトランポリンの説明はもう少し何とかならなかったんだろうか・・・.
コンパイラなどの文脈で出てくるトランポリンはまたちょっと意味合いが変わってくるんでしょうか?そっち方面には詳しくないので良く分からないんですが.