C/C++ / Python風のデコレータ(decorator)を書いてみたい ── 関数オブジェクト

前回からの続きです。

今回は、関数オブジェクトについてです。 関数オブジェクトは、operator ()を定義したオブジェクトのことです。 C++のオブジェクトはstructかclassで定義します。


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

ここでは、一つのintを取り、intを返す関数としてoperator ()を定義しました。 operator ()は、オブジェクトを関数に見立てた呼び出しで実行できます。


// ==================================================
int main(int argc, const char* argv[]) {
  Hoge l_Hoge;
  printf("%d\n", l_Hoge(10));

  return 0;
}

g++ --std=c++03 ./main.cc -o main && ./main
20

前回とは違いg++やclang++等、C++用のコンパイラを使用しないとコンパイルできません。注意してください。

関数オブジェクトデコレータ

これをデコレートするには、const Hoge&を受け取る関数を定義すればよさそうです。


// ==================================================
inline int decoratorHoge(const Hoge& a_Hoge, int a_Int) {
  printf("KOOL!!! Hoge!!! ");
  return a_Hoge(a_Int) * 2;
}

// ==================================================
int main(int argc, const char* argv[]) {
  Hoge l_Hoge;
  printf("%d\n", l_Hoge(10));
  printf("%d\n", decoratorHoge(l_Hoge, 10));

  return 0;
}

g++ --std=c++03 ./main.cc -o main && ./main
20
KOOL!!! Hoge!!! 40

decoratorHogeに与えた10がHoge::operatorで2倍、decoratorHogeで2倍になって40が出力されました。

インターフェース共通化

しかし、デコレータがHogeしかデコレートできないのでは、意味がありません。 そこで、インターフェースを別のクラスに吐き出して共通化できるようにします。


// /////////////////////////////////////////////////////////
// ==================================================
class FunctionObjectInterface {
 public:
  virtual ~FunctionObjectInterface()  {}

 public:
  virtual int operator()(int) const = 0;
};

この基底から、派生クラスHogeとFugaを作ります。


// /////////////////////////////////////////////////////////
// ==================================================
class Hoge : public FunctionObjectInterface {
 public:
  virtual ~Hoge(){}

 public:
  inline int operator()(int a_Int) const {
    return a_Int * 2;
  }
};
// ==================================================
class Fuga : public FunctionObjectInterface {
 public:
  virtual ~Fuga(){}

 public:
  inline int operator()(int a_Int) const {
    return a_Int * 10;
  }
};

対応するデコレータは次の様になります。


// /////////////////////////////////////////////////////////
// ==================================================
inline int decoratorFunctionObject(const FunctionObjectInterface& a_Obj,
                 int a_Int) {
  printf("KOOL!!! FunctionObject!!! ");
  return a_Obj(a_Int) * 2;
}

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

g++ --std=c++03 ./main.cc -o main && ./main
Hoge ============================
20
KOOL!!! FunctionObject!!! 40
Fuga ============================
100
KOOL!!! FunctionObject!!! 200

operator()を仮想関数化したことによって、 動的に振る舞いを変えることができるようになりました。 ──って、これは別にデコレータについての話ではないですね。 単に継承と仮想関数の話になってしまいました。

実をいえば、クラス階層でデコレータを実装する場合も、仮想関数を定義して基底の仮想関数を派生から呼び出すというだけの事に落ち着きます。

継承とデコレータは、実現できる要求にほとんど、差がない気がします。 自分の場合、Pythonでデコレータを使い出すと、ほとんど継承を使わずにコードを書いています。

ところで、このままではちょっと面倒です。 「前回の関数ポインタと今回の関数オブジェクトを統一的に扱うにはどうしたらよいか?」 という問題がでてきました。

これを解決するには──

──続きます。

目録

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

0 件のコメント:

コメントを投稿