C/C++ / Python風のデコレータを書いてみたい ── テンプレート

前回からの続きです。

テンプレートを使います。みんなが大好きな奴です。

関数ポインタと関数オブジェクトを処理できるデコレータを書きます。


// /////////////////////////////////////////////////////////
// ==================================================
template <class T>
inline int decoratorAll(T& a_Func, int a_Int) {
  return a_Func(a_Int) * 1000;
}

引数a_Funcの型がテンプレート引数Tによって決まります。 これで、関数ポインタと、関数オブジェクトのどちらも受け取れます。

さらに。 テンプレート化したことで、前回のHogeとFugaのインターフェース基底クラスも必要なくなります。 継承がデコレータによって置き換えられました。


// /////////////////////////////////////////////////////////
// ==================================================
int hoge(int a_Int) {
  return a_Int * 2;
}
// ==================================================
class Hoge {
 public:
  inline int operator()(int a_Int) const {
    return a_Int * 10;
  }
};
// ==================================================
class Fuga {
 public:
  inline int operator()(int a_Int) const {
    return a_Int * 100;
  }
};

// /////////////////////////////////////////////////////////
// ==================================================
int main(int argc, const char* argv[]) {
  {
    printf("hoge ============================\n");
    printf("%d\n", hoge(10));
    printf("%d\n", decoratorAll(&hoge, 10));
  }
  {
    Hoge l_Hoge;
    printf("Hoge ============================\n");
    printf("%d\n", l_Hoge(10));
    printf("%d\n", decoratorAll(l_Hoge, 10));
  }
  {
    Fuga l_Fuga;
    printf("Fuga ============================\n");
    printf("%d\n", l_Fuga(10));
    printf("%d\n", decoratorAll(l_Fuga, 10));
  }
  return 0;
}

コンパイルして実行してみます。


 g++ ./main.cc -o ./main && ./main
hoge ============================
20
20000
Hoge ============================
100
100000
Fuga ============================
1000
1000000

期待通りになっていることが、確認できます。

名前を付ける

残った問題は、デコレータが関数を返さず、結果の値を返すことです。 仕方がないので、新しい名前を付けることにします。


// /////////////////////////////////////////////////////////
// ==================================================
int new_hoge(int a_Int) {
  return decoratorAll(&hoge, a_Int);
}
// ==================================================
class NewHoge :
    private Hoge {  // (A)
 public:
  inline int operator()(int a_Int) const {
    return decoratorAll<Hoge>(*this, a_Int);  // (B)
  }
};
// ===================================================
class NewFuga {
 private:
  Fuga m_Fuga;  // (A)

 public:
  inline int operator()(int a_Int) const {
    return decoratorAll(m_Fuga, a_Int);
  }
};
// ===================================================
class NewVar {
 public:
  inline int operator()(int a_Int) const {
    return decoratorAll(Hoge(), a_Int);  // (A)
  }
};

pythonと違い、実行時ではなくコンパイル時に決定するので、これでOKです。

注意すべきは、関数オブジェクトが元の関数オブジェクトにアクセスする部分(A)。 Hogeの例ではprivate継承させています。 基底のoperator ()にアクセスを許す必要はないからです。 要求によっては、protected,publicでも構いません。

ただし、この場合、 関数オブジェクトがデコレータテンプレートを呼び出す際に基底の型をdecoratorAllに指定してやる必要があります(B)。 指定しない場合、NewHogeをT型として、テンプレートが実体化してoperator()が無限ループに陥ります。 g++では"Segmentation fault"しました。

他にも、NewFugaのようにメンバ変数として持たせてもかまいません。 ここでのHoge自体は状態を持っていないので、NewVarのように、operator ()内で構築しても構わないことになります。 NewVarはHogeを使用しています。 継承の場合と違い、NewFugaもNewVarもT型の推測が働くので、decoratorAllへの型は指定しなくても大丈夫です。


// /////////////////////////////////////////////////////////
// ==================================================
int main(int argc, const char* argv[]) {
  {
    printf("hoge ============================\n");
    printf("%d\n", hoge(10));
    printf("%d\n", new_hoge(10));
  }
  {
    Hoge l_Hoge;
    printf("Hoge ============================\n");
    printf("%d\n", l_Hoge(10));
    printf("%d\n", NewHoge()(10));
  }
  {
    Fuga l_Fuga;
    printf("Fuga ============================\n");
    printf("%d\n", l_Fuga(10));
    printf("%d\n", NewFuga()(10));
  }
  {
    printf("NewVar ============================\n");
    printf("%d\n", NewVar()(10));
  }
  return 0;
}

実行すると……。


g++ ./deco.cc -o ./main && ./main
hoge ============================
20
20000
Hoge ============================
100
100000
Fuga ============================
1000
1000000
NewVar ============================
100000

期待どおりです。

これで、概要の解説は終わりです。 次回は実際の使用例を上げてみたいと思います。

──続きます。

目録

  1. C/C++ / Python風のデコレータを書いてみたい ── 関数ポインタ
  2. C/C++ / Python風のデコレータを書いてみたい ── 関数オブジェクト
  3. C/C++ / Python風のデコレータを書いてみたい ── テンプレート
  4. C/C++ / Python風のデコレータを書いてみたい ── Buffered Loop

0 件のコメント:

コメントを投稿