C++ で、似たような処理だけれども関数の引数の数や型が違うせいでその処理をひとまとめにすることが出来ない、ということがあったりしませんか?
自分はよくありました。例えば以下のようなコードです。
int FuncA(Array<Hoge*> array, int a, int b)
{
int count = 0;
for (int i = 0; i < array.length(); i++)
{
int result = array[i]->FuncA(a, b);
if (result < 0)
{
break;
}
count += result;
}
return count;
}
int FuncB(Array<Hoge*> array, double a, int b, float c)
{
int count = 0;
for (int i = 0; i < array.length(); i++)
{
int result = array[i]->FuncB(a, b, c);
if (result < 0)
{
break;
}
count += result;
}
return count;
}
...
これらの関数は処理が似ているので何とかまとめたいのですが、関数名や引数の数が違うため、簡単にはできません。
そこで使えるのが Boost.Bind と Boost.Function。
これらを使うと、次のように実装することが出来ます。
int GenericFunc(Array<Hoge*> array, boost::function1<int, Hoge*> func)
{
int count = 0;
for (int i = 0; i < array.length(); i++)
{
int result = func(array[i]);
if (result < 0)
{
break;
}
count += result;
}
return count;
}
int FuncA(Array<Hoge*> array, int a, int b)
{
return GenericFunc(array,
boost::bind(&Hoge::FuncA, _1, a, b));
}
int FuncB(Array<Hoge*> array, double a, int b, float c)
{
return GenericFunc(array,
boost::bind(&Hoge::FuncB, _1, a, b, c));
}
...
GenericFunc の中に共通の処理が入りました。
boost::bind で引数を閉じこめる(束縛する)ことによって、GenericFunc を共通の処理に落とし込んでいます。
「こんなケース滅多にないよ!」とか思うかもしれませんが、例えばキューにメンバ関数(もしくは普通の関数)を詰めて定期的に実行するようなクラスが欲しくなることはよくあると思います(あるよね?)。
普通に実装しようとすると、
struct Function
{
Function(void (Hoge::*pf)(int, int), Hoge* ph, int _a, int _b)
: pFunc(pf), pHoge(ph), a(_a), b(_b) { }
void (Hoge::*pFunc)(int a, int b);
Hoge* pHoge;
int a;
int b;
};
std::queue<Function> functionQueue;
void Invoke()
{
if (functionQueue.empty()) return;
Function f = functionQueue.front();
functionQueue.pop();
(f.pHoge)->*f.pFunc(f.a, f.b);
}
functionQueue.push(Function(&Hoge::FuncA, &hoge, 10, 20));
こんな感じになるんじゃないかと思います。
この場合、関数の引数は固定だし Hoge クラスのメンバ関数に対してしか使えないし、あまり汎用的ではないです。
インターフェースを使えばもっとスマートになりますが、その方法は恐らく1つの処理に対して1つのクラスを割り当てることになると思うので、それではかなり手間が掛かってしまいます。
Boost.Bind と Boost.Function を使えば、こんな感じになります。
std::queue<boost::function0<void> > functionQueue;
void Invoke()
{
if (functionQueue.empty()) return;
boost::function0<void> f = functionQueue.front();
functionQueue.pop();
f();
}
functionQueue.push(boost::bind(&Hoge::FuncA, &hoge, 10, 20));
functionQueue.push(boost::bind(&Hoge::FuncB, &hoge, 10.0, 20, 30.0));
functionQueue.push(boost::bind(&Fuga::FuncC, &fuga, 100));
何でも格納できる汎用的なファンクションキューの出来上がりです。
Boost.Bind と Boost.Function の組み合わせはほんと凶悪だと思います。
是非とも使ってみて下さい。