//--- main.cpp ---
// ComponentLayoutの実体をプロジェクト毎に
// マクロ定義一発で切り替えたい。さてどうするか。
#include <boost/preprocessor.hpp>
class ComponentLayout
{
public:
virtual void ShowName() {}
virtual void PlaceUnits() {}
virtual void Resize(int cx, int cy) {}
};
// 連結してから文字列化
// #include CL_PROJECTNAME.h としたい
#include BOOST_PP_STRINGIZE( \
BOOST_PP_CAT( CL_, \
BOOST_PP_CAT( PROJECTNAME, \
.h )))
int main()
{
// 連結
// new ComponentLayout_PROJECTNAME としたい
ComponentLayout* pLay = new
BOOST_PP_CAT( ComponentLayout_, PROJECTNAME );
pLay->ShowName();
delete pLay;
return 0;
}
//--- CL_Abc.h ---
#include <iostream>
class ComponentLayout_Abc : public ComponentLayout
{
void ShowName() { std::cout << "Project: ABC" << std::endl; }
};
//--- CL_Iroha.h ---
#include <iostream>
class ComponentLayout_Iroha : public ComponentLayout
{
void ShowName() { std::cout << "Project: IROHA" << std::endl; }
};
>> bcc32 -DPROJECTNAME=Iroha main.cpp >> main Project: IROHA >> bcc32 -DPROJECTNAME=Abc main.cpp >> main Project: ABC
CreateComponentLayout() 関数というのを外部に定義する作るようにして、 main ではその関数を呼び出す、というやり方の方がエレガントなんですけど、 ここではマクロでやってみたということで。
与えた引数を文字列リテラル化したり、二つの引数を連結して一つにしたりするマクロ、 というのは、パッと考えると次のようになります。
#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の真価は、もう少し別なところにあります。 それは、繰り返し記述の自動化。次の例
#define APPLY_TO(I,FUNCTION) FUNCTION(I);
BOOST_PP_REPEAT(10, APPLY_TO, f)
は、このように展開されます。
f(0); f(1); f(2); f(3); f(4); f(5); f(6); f(7); f(8); f(9);
f(I); と言う形の記述を I=0~9 の範囲で行う、という作業が BOOST_PP_REPEAT マクロによって抽象化されているのがわかると思います。 fの様に関数なら普通のC++言語のforループで回してしまえばよいのですが、 例えば class f0, class f1, ... class f9 という10個の似たような型を定義したい、 そんな時にはプリプロセッサの力を借りる必要が出てきます。 実際、そういう場面の多い boost::lambda などの実装には頻繁に使われています。
他にも、BOOST_PP_ADD(x,y) -- BOOST_PP_ADD(3,4) が 3+4 ではなく 7 に展開される -- など実装方法を考えるだけで面白いマクロ関数が色々あるので、 眺めてみるのも楽しいかもしれません。