preprocessor
boost::function実装時にあると便利です。
トークンの連結
#define pp_cat( a, b ) a##b
引数で渡したa、bが連結されてabになります。
int pp_cat( a, b ) = 0; //int ab = 0;
しかし、以下のように引数に自分自身を渡すとコンパイル時に展開されません。
int pp_cat( pp_cat( a, b ), c ) = 0; //int abc = 0;ではなくint pp_cat( a, b )c;になってしまう
マクロ展開中に同じマクロがあると展開されないようです。
では、一旦別のマクロに置き換えてしまえば問題ないということですね。
#define pp_cat( a, b ) pp_cat_( a, b ) #define pp_cat_( a, b ) a##b
今度は正しく展開されました。
int pp_cat( pp_cat( a, b ), c ) = 0; //int abc = 0;
繰り返し
- マクロで指定回数繰り返し
では、応用です。
int a0 = 0; int a1 = 1; int a2 = 2; ... int a9 = 9;
ようなコードをマクロで一気に書くことは出来ないのでしょうか?(配列にしろ!という突っ込みは無しでw)
単純に書くと
#define pp_repeat_0 int a0 = 0; #define pp_repeat_1 int a0 = 0; int a1 = 1; #define pp_repeat_2 int a0 = 0; int a1 = 1; int a2 = 2; ... #define pp_repeat_9 int a0 = 0; int a1 = 1; int a2 = 2; ... int a9 = 9;
繰り返している部分をマクロに置き換えましょう。
#define pp_repeat_0 #define pp_repeat_1 pp_repeat_0 int a0 = 0; #define pp_repeat_2 pp_repeat_1 int a1 = 1; ... #define pp_repeat_9 pp_repeat_8 int a8 = 8; #define pp_repeat_10 pp_repeat_9 int a9 = 9;
さらに先ほどのトークン連結マクロを使うと、
//hoge( int a, 0 ); #define hoge( text, n ) pp_cat( text, n ) = n; #define pp_repeat_0 #define pp_repeat_1 pp_repeat_0 hoge( int a, 0 ) #define pp_repeat_2 pp_repeat_1 hoge( int a, 1 ) ... #define pp_repeat_9 pp_repeat_8 hoge( int a, 8 ) #define pp_repeat_10 pp_repeat_9 hoge( int a, 9 )
int aという具体的な部分をマクロの引数にしましょう。
#define hoge( text, n ) pp_cat( text, n ) = n; #define pp_repeat_0( data ) #define pp_repeat_1( data ) pp_repeat_0( data ) hoge( data, 0 ) #define pp_repeat_2( data ) pp_repeat_1( data ) hoge( data, 1 ) ... #define pp_repeat_9( data ) pp_repeat_8( data ) hoge( data, 8 ) #define pp_repeat_10( data ) pp_repeat_9( data ) hoge( data, 9 )
さらに、hogeという具体的な部分をマクロの引数にしましょう。
#define hoge( text, n ) pp_cat( text, n ) = n; #define pp_repeat_0( macro, data ) #define pp_repeat_1( macro, data ) pp_repeat_0( macro, data ) macro( data, 0 ) #define pp_repeat_2( macro, data ) pp_repeat_1( macro, data ) macro( data, 1 ) ... #define pp_repeat_9( macro, data ) pp_repeat_8( macro, data ) macro( data, 8 ) #define pp_repeat_10( macro, data ) pp_repeat_9( macro, data ) macro( data, 9 )
最後に繰り返しの回数部分も引数にしましょう。
#define hoge( text, n ) pp_cat( text, n ) = n; #define pp_repeat( n, macro, data ) pp_cat( pp_repeat_, n )( macro, data ) #define pp_repeat_0( macro, data ) #define pp_repeat_1( macro, data ) pp_repeat_0( macro, data ) macro( data, 0 ) #define pp_repeat_2( macro, data ) pp_repeat_1( macro, data ) macro( data, 1 ) ... #define pp_repeat_9( macro, data ) pp_repeat_8( macro, data ) macro( data, 8 ) #define pp_repeat_10( macro, data ) pp_repeat_9( macro, data ) macro( data, 9 )
これで基本的な部分は完成です。
実際に使ってみましょう。
pp_repeat( 10, hoge, int a );
int a0 = 0;〜int a9 = 9;まで作られていますね。
今回はnの最大値が10までですが、必要な分だけ(例えばpp_repeat_256まで)プログラムで出力しましょう。
これは、今後boost::functionを実装するときに
template< typename T1, typename T2, ... typename T10 > class function { result_t operator()( T1 t1, T2, t2, ..., T10 t10 ); };
のような引数の記述が必要になるので、pp_repeatでまとめて定義すると便利です。自力で書いてもいいんですけどね。
boostにはBOOST_PP_REPEATという同じマクロが用意されています(最大256まで)。
選択
- マクロの条件によって、2つから1つを選択する。
//t=true, f=false //偽のときはf、真のときはtを選択する #define pp_if_0( t, f ) f #define pp_if_1( t, f ) t
上記のようなマクロがあると
int a = pp_if_0( 1, 2 ); //int a = 2; int b = pp_if_1( 1, 2 ); //int b = 1;
となります。つまり、別のマクロでpp_if_0とpp_if_1の選択が可能であれば、マクロの引数の選択が出来るということですね。
//bit=0 or 1 #define pp_if_b( bit, t, f ) pp_cat( pp_if_, bit( t, f ) ) //t=true, f=false //偽のときはf、真のときはtを選択する #define pp_if_0( t, f ) f #define pp_if_1( t, f ) t
0か1かでtとfを選択できるようになりました。
後は条件によって0か1を選択できれば、マクロで条件分岐が可能ですね。
//cond=0〜任意の整数 //pp_bool( cond )=condが0のとき0、それ以外は1 #define pp_if_c( cond, t, f ) pp_if_b( pp_bool( cond ), t, f ) //bit=0 or 1 #define pp_if_b( bit, t, f ) pp_cat( pp_if_, bit( t, f ) ) //t=true, f=false //偽のときはf、真のときはtを選択する #define pp_if_0( t, f ) f #define pp_if_1( t, f ) t
pp_boolは以下のように定義しておきます。
//pp_bool( cond )=condが0のとき0、それ以外は1 #define pp_bool( n ) pp_bool_n( n ) #define pp_bool_n( n ) pp_cat( pp_bool_, n ) #define pp_bool_0 0 #define pp_bool_1 1 #define pp_bool_2 1 #define pp_bool_3 1 ... #define pp_bool_256 1
以上で整数値によって選択が可能になりました。
pp_if( 10, a, b ); //a
へ、変態っ!!
boost以外で絶対使わないですよね。。。