サンプルの動作確認バージョン [GCC4.4/1.41.0] [VC9/1.41.0]
#include <iostream>
#include <boost/preprocessor.hpp>
#define GEN_FUNC(x, i, name) \
void BOOST_PP_CAT(name, BOOST_PP_CAT(_, i)) () \
{ \
std::cout << BOOST_PP_STRINGIZE(name) << i << std::endl;\
}
// GEN_FUN(..., 0, func)
// GEN_FUN(..., 1, func)
// GEN_FUN(..., 2, func)
// ...
// GEN_FUN(..., 9, func)
// を順に呼び出し
BOOST_PP_REPEAT(10,GEN_FUNC,func)
// void func_0(){std::cout<<"func"<<0<<std::endl;}
// void func_1(){std::cout<<"func"<<1<<std::endl;}
// void func_2(){std::cout<<"func"<<2<<std::endl;}
// ...
// void func_9(){std::cout<<"func"<<9<<std::endl;}
// と展開される
int main()
{
func_0();
func_1();
func_9();
}
func0 func1 func9
与えた引数を文字列リテラル化したり、二つの引数を連結して一つにしたりするマクロ、 というのは、パッと考えると次のようになります。
#define PP_STRINGIZE(x) #x
#define PP_CAT(x, y) x##y
が、これだと上の例のようにマクロをパラメータとして与えたときに破綻します。
与えたマクロが展開されず、例えば
#include "PP_CAT(CL_,PP_CAT(PROJECTNAME,.h))"
と妙な名前のファイルをincludeしようとしたことになってしまうのです。
この問題を回避するには PP_CAT や PP_STRINGIZE
を2段重ねにして強制的にマクロ展開させるという手がありまして、
それを実装したのが上のBOOST_PP_*というわけですね。
その二つは一番簡単ですぐに使えそうな部分です。 が、BOOST_PPの真価は、もう少し別なところにあります。 それは、繰り返し記述の自動化。上にあげたサンプルが例となっています。
関数を f(0); f(1); ...; f(9); と 10回と呼び出したい、 という場合なら for ループで呼び出せばよいですが、 「func_0, func_1, .. func_9 という似たような関数を10個定義したい」 「class f1, class f2, ... という型を10個定義したい」 といった繰り返しの要求は、そうは行きません。 こんな時にはプリプロセッサの力を借りる必要が出てきます。 特に、同名の似たような型や関数を量産する場面は実際にはあまりないかもしれませんが、 「ある関数の 0 引数バージョン、1 引数バージョン、2 引数バージョン、…を全部作りたい」 は汎用ライブラリを作る際にしばしば必要です。 実際、そういう場面の多い boost::lambda などの実装には頻繁に使われています。 C++0x に導入された可変長テンプレート引数のおかげで、 今後は必要な場面が減っていくかもしれませんが…。
他にも、BOOST_PP_ADD(x,y) -- BOOST_PP_ADD(3,4) が 3+4 ではなく 7 に展開される -- など実装方法を考えるだけで面白いマクロ関数が色々あるので、 眺めてみるのも楽しいかもしれません。