前回からの続きです。
テンプレートを使います。みんなが大好きな奴です。
関数ポインタと関数オブジェクトを処理できるデコレータを書きます。
// /////////////////////////////////////////////////////////
// ==================================================
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
期待どおりです。
これで、概要の解説は終わりです。 次回は実際の使用例を上げてみたいと思います。
──続きます。
0 件のコメント:
コメントを投稿