boost::preprocessor

トップページ > メタプログラミング >

abstract

必要なヘッダ
<boost/preprocessor.hpp>
出来ること
プリプロセッサで色々するためのライブラリ
リファレンス
en

sample

//--- 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 ではその関数を呼び出す、というやり方の方がエレガントなんですけど、 ここではマクロでやってみたということで。

etc

与えた引数を文字列リテラル化したり、二つの引数を連結して一つにしたりするマクロ、 というのは、パッと考えると次のようになります。

#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 に展開される -- など実装方法を考えるだけで面白いマクロ関数が色々あるので、 眺めてみるのも楽しいかもしれません。

see also

presented by k.inaba   under NYSDL.