C++ プリプロセッサメタプログラミングの基礎(続き)
(前回の続き)
マクロの再帰はエラー
マクロの再帰はエラーになりますが、これはプリプロセスでエラーが出ているのではなく、プリプロセスで置換されずに残ったシンボルがコンパイルの段階で見つからないためにエラーになっているものです
例えば下記のコードは Visual C++ 以外ではエラーになります
#if defined( _MSC_VER ) #define PP_LPAREN ( #define PP_RPAREN ) #define PP_INVOKE( callback,... ) callback PP_LPAREN __VA_ARGS__ PP_RPAREN #else // _MSC_VER #define PP_INVOKE( callback,... ) callback( __VA_ARGS__ ) #endif // _MSC_VER #define FOO(n) debug_printf( #n "\n" ); #define BAR(n) PP_INVOKE(FOO,n) #define BAZ(n) PP_INVOKE(BAR,n) int main(){ FOO( 10 ); BAR( 20 ); BAZ( 30 ); // VC++ 以外ではエラー return 0; }
エラーが出ないようにするには再帰にならないようにする必要があります
※ 各処理系には大なり小なりそれぞれ独自の仕様というものがありますが、Visual C++ にも独自の仕様がいろいろとあります
マクロの再帰もその一つです
Visual C++ のプリプロセッサはマクロの再帰に寛容なので標準で必要な回避策が不要な場合もあります
同機能のマクロを複数用意する
次のように同機能のマクロを必要なだけ用意しておいて呼び出すときにマクロの名前を変えてあげると文法的な再帰に陥ることなく再帰のような処理を実現できます
#if defined( _MSC_VER ) #define PP_LPAREN ( #define PP_RPAREN ) #define PP_INVOKE1( callback,... ) callback PP_LPAREN __VA_ARGS__ PP_RPAREN #define PP_INVOKE2( callback,... ) callback PP_LPAREN __VA_ARGS__ PP_RPAREN #else // _MSC_VER #define PP_INVOKE1( callback,... ) callback( __VA_ARGS__ ) #define PP_INVOKE2( callback,... ) callback( __VA_ARGS__ ) #endif // _MSC_VER #define FOO(n) debug_printf( #n "\n" ); #define BAR(n) PP_INVOKE2(FOO,n) #define BAZ(n) PP_INVOKE1(BAR,n) int main(){ FOO( 10 ); BAR( 20 ); BAZ( 30 ); return 0; }
ただ、いちいち名前を変えるのは面倒ですね
しかもこの例では呼び出し順が固定なのでいいのですが、必ずしもそうとは限りません
何かマクロの再帰を実現するいい方法はないでしょうか
マクロの再帰はエラーではない
ところで最初の例でエラーの出ている BAZ の展開の様子を見てみると、下記のように PP_INVOKE の呼び出しを置換中に再び PP_INVOKE が現れるためにそこで置換処理が停止し、PP_INVOKE と FOO がコンパイルの段階に進んでしまっているのがわかると思います
BAZ( 30 ) ↓ // n → 30 PP_INVOKE(BAR,30) ↓ // callback = BAR, ... → 30 BAR(30) ↓ // n → 30 PP_INVOKE(FOO,30)
問題は PP_INVOKE と FOO が見つからないということなので、下記のようにその実体を追加してあげるとエラーは起きなくなります
void FOO( int n ){ debug_printf( "%d\n", n ); } void PP_INVOKE( void (*callback)(int), int n ){ callback( n ); }
つまり、マクロの置換で再帰ができないというのは再帰的に置換されないというだけで文法違反ではないということです
文法違反でないのなら何か抜け道がありそうです
再帰を回避する方法
まず呼び出すマクロの名前をいちいち変えなくていいようにしなければいけませんが、PP_INVOKE の実装をどのように工夫したとしても最終的には指定されたマクロを呼ぶわけで、その呼び出し先に PP_INVOKE があれば必ず再帰になってしまいます
これはどうしようもありません
考え方を変える必要があります
関数形式のマクロが展開されてしまうともうダメです
展開される前に何か細工をして展開されるマクロの名前を変える必要があります
×
BAZ( 30 ) ↓ PP_INVOKE(BAR,30) ↓ BAR(30) ↓ PP_INVOKE(FOO,30) // 再帰のため置換停止
何かこのように展開してくれれるような方法はないでしょうか
○
BAZ( 30 ) ↓ PP_INVOKE(BAR,30) ↓ PP_INVOKE_1(BAR,30) ↓ BAR(30) ↓ PP_INVOKE(FOO,30) ↓ PP_INVOKE_2(FOO,30) ↓ FOO(30)
もちろん解決策はあります
それは PP_INVOKE を関数形式のマクロにするのではなく、オブジェクト形式のマクロにするのです
×
#define PP_INVOKE( callback,... ) callback( __VA_ARGS__ )
○
#define PP_INVOKE PP_CONCAT(PP_INVOKE_,何らかのカウンタ的なもの)
このようになっていれば文法的な再帰を避けながら実質的な再帰処理を実現することができます
問題は「何らかのカウンタ的なもの」です
どうしたらいいでしょうか
再帰可能なマクロの実装
再帰判定をどうするかは置いておいて、カウンタの実装はとりあえずは次のようになっているとよさそうですね
#define PP_INVOKE_RECURSIVE_COUNTER_3 PP_IF( 3回目か?, 3, 4 ) #define PP_INVOKE_RECURSIVE_COUNTER_2 PP_IF( 2回目か?, 2, PP_INVOKE_RECURSIVE_COUNTER_3 ) #define PP_INVOKE_RECURSIVE_COUNTER PP_IF( 1回目か?, 1, PP_INVOKE_RECURSIVE_COUNTER_2 )
そして再帰かどうかの判定ですが、これには PP_INVOKE マクロ自身を使います
#define PP_INVOKE_1_RECURSIVE_PP_INVOKE_1(x) 0 #define PP_INVOKE_1_RECURSIVE_PP_INVOKE_1_RECURSIVE_UNUSED() 1 #undef PP_INVOKE_1_RECURSIVE_UNUSED #define PP_INVOKE_1_RECURSIVE PP_CONCAT(PP_INVOKE_1_RECURSIVE_, PP_INVOKE_1(PP_INVOKE_1_RECURSIVE_UNUSED))
再帰でない場合には、PP_INVOKE_1 の呼び出しが正しく置換されるので最終的に PP_INVOKE_1_RECURSIVE_PP_INVOKE_1_RECURSIVE_UNUSED() という形に置換されます
このとき PP_INVOKE を PP_INVOKE_1 に置換したいので、PP_INVOKE_1_RECURSIVE_PP_INVOKE_1_RECURSIVE_UNUSED() が 1 になるように定義しておきます
PP_CONCAT(PP_INVOKE_1_RECURSIVE_, PP_INVOKE_1(PP_INVOKE_1_RECURSIVE_UNUSED)) ↓ // callback → PP_INVOKE_1_RECURSIVE_UNUSED PP_CONCAT(PP_INVOKE_1_RECURSIVE_, PP_INVOKE_1_RECURSIVE_UNUSED()) ↓ // first → PP_INVOKE_1_RECURSIVE_, second → PP_INVOKE_1_RECURSIVE_UNUSED() PP_INVOKE_1_RECURSIVE_ ## PP_INVOKE_1_RECURSIVE_UNUSED() ↓ PP_INVOKE_1_RECURSIVE_PP_INVOKE_1_RECURSIVE_UNUSED() ↓ 1
再帰である場合には、PP_INVOKE_1 の呼び出しが置換されないので最終的に PP_INVOKE_1_RECURSIVE_PP_INVOKE_1(PP_INVOKE_1_RECURSIVE_UNUSED) という形に置換されます
このとき PP_INVOKE を PP_INVOKE_1 に置換したくないので、PP_INVOKE_1_RECURSIVE_PP_INVOKE_1(x) が 0 になるように定義しておきます
PP_CONCAT(PP_INVOKE_1_RECURSIVE_, PP_INVOKE_1(PP_INVOKE_1_RECURSIVE_UNUSED)) ↓ // first → PP_INVOKE_1_RECURSIVE_, second → PP_INVOKE_1(PP_INVOKE_1_RECURSIVE_UNUSED) PP_INVOKE_1_RECURSIVE_ ## PP_INVOKE_1(PP_INVOKE_1_RECURSIVE_UNUSED) ↓ PP_INVOKE_1_RECURSIVE_PP_INVOKE_1(PP_INVOKE_1_RECURSIVE_UNUSED) ↓ 0
これで PP_INVOKE_1 の再帰判定ができました
PP_INVOKE_2 以降も同様です
「何らかのカウンタ的なもの」は次のように書けます
#define PP_INVOKE_RECURSIVE_COUNTER_3 PP_IF( PP_INVOKE_3_RECURSIVE, 3, 4 ) #define PP_INVOKE_RECURSIVE_COUNTER_2 PP_IF( PP_INVOKE_2_RECURSIVE, 2, PP_INVOKE_RECURSIVE_COUNTER_3 ) #define PP_INVOKE_RECURSIVE_COUNTER PP_IF( PP_INVOKE_1_RECURSIVE, 1, PP_INVOKE_RECURSIVE_COUNTER_2 )
まとめると PP_INVOKE は次のように書けます
#define PP_INVOKE_1_RECURSIVE_PP_INVOKE_1(x) 0 #define PP_INVOKE_2_RECURSIVE_PP_INVOKE_2(x) 0 #define PP_INVOKE_3_RECURSIVE_PP_INVOKE_3(x) 0 #define PP_INVOKE_1_RECURSIVE_PP_INVOKE_1_RECURSIVE_UNUSED() 1 #define PP_INVOKE_2_RECURSIVE_PP_INVOKE_2_RECURSIVE_UNUSED() 1 #define PP_INVOKE_3_RECURSIVE_PP_INVOKE_3_RECURSIVE_UNUSED() 1 #undef PP_INVOKE_1_RECURSIVE_UNUSED #undef PP_INVOKE_2_RECURSIVE_UNUSED #undef PP_INVOKE_3_RECURSIVE_UNUSED #define PP_INVOKE_1_RECURSIVE PP_CONCAT(PP_INVOKE_1_RECURSIVE_, PP_INVOKE_1(PP_INVOKE_1_RECURSIVE_UNUSED)) #define PP_INVOKE_2_RECURSIVE PP_CONCAT(PP_INVOKE_2_RECURSIVE_, PP_INVOKE_2(PP_INVOKE_2_RECURSIVE_UNUSED)) #define PP_INVOKE_3_RECURSIVE PP_CONCAT(PP_INVOKE_3_RECURSIVE_, PP_INVOKE_3(PP_INVOKE_3_RECURSIVE_UNUSED)) #define PP_INVOKE_RECURSIVE_COUNTER_3 PP_IF( PP_INVOKE_3_RECURSIVE, 3, 4 ) #define PP_INVOKE_RECURSIVE_COUNTER_2 PP_IF( PP_INVOKE_2_RECURSIVE, 2, PP_INVOKE_RECURSIVE_COUNTER_3 ) #define PP_INVOKE_RECURSIVE_COUNTER_1 PP_IF( PP_INVOKE_1_RECURSIVE, 1, PP_INVOKE_RECURSIVE_COUNTER_2 ) #define PP_INVOKE_RECURSIVE_COUNTER PP_INVOKE_RECURSIVE_COUNTER_1 #define PP_INVOKE PP_CONCAT(PP_INVOKE_,PP_INVOKE_RECURSIVE_COUNTER)
コードを整理しましょう
PP_INVOKE_1, PP_INVOKE_2, PP_INVOKE_3 のための実装が全部別になっていますが、数字が違うだけなのでまとめてしまいましょう
#define PP_INVOKE_RECURSIVE_PP_INVOKE_1(...) 0 #define PP_INVOKE_RECURSIVE_PP_INVOKE_2(...) 0 #define PP_INVOKE_RECURSIVE_PP_INVOKE_3(...) 0 #define PP_INVOKE_RECURSIVE_PP_INVOKE_4(...) 0 #define PP_INVOKE_RECURSIVE_PP_INVOKE_RECURSIVE_UNUSED(...) 1 #undef PP_INVOKE_RECURSIVE_UNUSED #define PP_INVOKE_RECURSIVE( n ) PP_CONCAT(PP_INVOKE_RECURSIVE_, PP_CONCAT(PP_INVOKE_,n)(PP_INVOKE_RECURSIVE_UNUSED))
さらに、この再帰判定からカウンタを得る部分は機能が異なるので独立させましょう
#define PP_RECURSIVE_COUNTER_RETURN_1( callback ) 1 #define PP_RECURSIVE_COUNTER_RETURN_2( callback ) 2 #define PP_RECURSIVE_COUNTER_RETURN_3( callback ) 3 #define PP_RECURSIVE_COUNTER_RETURN_4( callback ) 4 #define PP_RECURSIVE_COUNTER_3( callback ) PP_IF( callback(3), PP_RECURSIVE_COUNTER_RETURN_3, PP_RECURSIVE_COUNTER_RETURN_4 )( callback ) #define PP_RECURSIVE_COUNTER_2( callback ) PP_IF( callback(2), PP_RECURSIVE_COUNTER_RETURN_2, PP_RECURSIVE_COUNTER_3 )( callback ) #define PP_RECURSIVE_COUNTER_1( callback ) PP_IF( callback(1), PP_RECURSIVE_COUNTER_RETURN_1, PP_RECURSIVE_COUNTER_2 )( callback ) #define PP_RECURSIVE_COUNTER( callback ) PP_RECURSIVE_COUNTER_1( callback )
これで完成です
#define PP_INVOKE PP_CONCAT(PP_INVOKE_,PP_RECURSIVE_COUNTER(PP_INVOKE_RECURSIVE))
再帰的に呼び出してもエラーにならず正しく置換されます
#define FOO(n) debug_printf( #n "\n" ); #define BAR(n) PP_INVOKE(FOO,n) #define BAZ(n) PP_INVOKE(BAR,n) int main(){ FOO( 10 ); BAR( 20 ); BAZ( 30 ); return 0; }
ループ
再帰ができるとループができるようになります
とりあえず簡単に、指定したマクロが指定した回数展開されるというマクロを実装してみましょう
PP_REPEAT( 3, FOO_ ) ↓ FOO_( 0 ) FOO_( 1 ) FOO_( 2 )
基本的には PP_INVOKE と同じです
まず呼び出し口となるマクロはオブジェクト形式のマクロにします
#define PP_REPEAT PP_CONCAT(PP_REPEAT_,PP_RECURSIVE(PP_REPEAT_RECURSIVE))
再帰判定には PP_REPEAT マクロ自身を使います
#undef PP_REPEAT_RECURSIVE_UNUSED #define PP_REPEAT_RECURSIVE( n ) PP_CONCAT(PP_REPEAT_RECURSIVE_, PP_CONCAT(PP_REPEAT_,n)(1,PP_REPEAT_RECURSIVE_UNUSED))
展開後に必要なマクロを用意します
#define PP_REPEAT_RECURSIVE_PP_REPEAT_1(...) 0 #define PP_REPEAT_RECURSIVE_PP_REPEAT_2(...) 0 #define PP_REPEAT_RECURSIVE_PP_REPEAT_3(...) 0 #define PP_REPEAT_RECURSIVE_PP_REPEAT_4(...) 0 #define PP_REPEAT_RECURSIVE_PP_REPEAT_RECURSIVE_UNUSED(...) 1
個々の実体を用意します
#define PP_REPEAT_1( n, callback ) PP_REPEAT_1_( n, callback ) #define PP_REPEAT_1_( n, callback ) PP_CONCAT(PP_REPEAT_1_,n)(callback) #define PP_REPEAT_1_0( callback ) #define PP_REPEAT_1_1( callback ) PP_REPEAT_1_0( callback ) callback( 0 ) #define PP_REPEAT_1_2( callback ) PP_REPEAT_1_1( callback ) callback( 1 ) #define PP_REPEAT_2( n, callback ) PP_REPEAT_2_( n, callback ) #define PP_REPEAT_2_( n, callback ) PP_CONCAT(PP_REPEAT_2_,n)(callback) #define PP_REPEAT_2_0( callback ) #define PP_REPEAT_2_1( callback ) PP_REPEAT_2_0( callback ) callback( 0 ) #define PP_REPEAT_2_2( callback ) PP_REPEAT_2_1( callback ) callback( 1 ) #define PP_REPEAT_3( n, callback ) PP_REPEAT_3_( n, callback ) #define PP_REPEAT_3_( n, callback ) PP_CONCAT(PP_REPEAT_3_,n)(callback) #define PP_REPEAT_3_0( callback ) #define PP_REPEAT_3_1( callback ) PP_REPEAT_3_0( callback ) callback( 0 ) #define PP_REPEAT_3_2( callback ) PP_REPEAT_3_1( callback ) callback( 1 ) #define PP_REPEAT_4( n, callback ) PP_REPEAT_4_( n, callback ) #define PP_REPEAT_4_( n, callback ) PP_CONCAT(PP_REPEAT_4_,n)(callback) #define PP_REPEAT_4_0( callback ) #define PP_REPEAT_4_1( callback ) PP_REPEAT_4_0( callback ) callback( 0 ) #define PP_REPEAT_4_2( callback ) PP_REPEAT_4_1( callback ) callback( 1 )
Visual C++ は素直に書けばいいだけです
#if defined( _MSC_VER ) #define PP_REPEAT( n, callback,... ) PP_CONCAT(PP_REPEAT_,n)( callback, ##__VA_ARGS__ ) #define PP_REPEAT_0( callback,... ) #define PP_REPEAT_1( callback,... ) PP_REPEAT_0( callback, ##__VA_ARGS__ ) PP_INVOKE( callback, 0, ##__VA_ARGS__ ) #define PP_REPEAT_2( callback,... ) PP_REPEAT_1( callback, ##__VA_ARGS__ ) PP_INVOKE( callback, 1, ##__VA_ARGS__ ) #define PP_REPEAT_3( callback,... ) PP_REPEAT_2( callback, ##__VA_ARGS__ ) PP_INVOKE( callback, 2, ##__VA_ARGS__ ) #define PP_REPEAT_4( callback,... ) PP_REPEAT_3( callback, ##__VA_ARGS__ ) PP_INVOKE( callback, 3, ##__VA_ARGS__ ) #define PP_REPEAT_5( callback,... ) PP_REPEAT_4( callback, ##__VA_ARGS__ ) PP_INVOKE( callback, 4, ##__VA_ARGS__ ) #define PP_REPEAT_6( callback,... ) PP_REPEAT_5( callback, ##__VA_ARGS__ ) PP_INVOKE( callback, 5, ##__VA_ARGS__ ) #endif // _MSC_VER
これだけです
簡単ですね
使用例
#define FOO_( n ) debug_printf( #n "\n" ); #define FOO( n ) PP_REPEAT( n, FOO_ ) #define BAR_2( n ) debug_printf( #n "\n" ); #define BAR_1( n ) PP_REPEAT( 2, BAR_2 ) #define BAR( n ) PP_REPEAT( n, BAR_1 ) #define BAZ_4( n ) debug_printf( #n "\n" ); #define BAZ_3( n ) PP_REPEAT( 2, BAZ_4 ) #define BAZ_2( n ) PP_REPEAT( 2, BAZ_3 ) #define BAZ_1( n ) PP_REPEAT( 2, BAZ_2 ) #define BAZ( n ) PP_REPEAT( n, BAZ_1 ) int main(){ FOO( 2 ); BAR( 2 ); BAZ( 2 ); return 0; }
指定された回数だけ指定したマクロを適用する
上記の例では指定するマクロにループカウンタ以外の引数を渡すことができませんでした
次のように任意の引数を渡せるようにしてもいいでしょう
PP_REPEAT( 3, FOO_, 10, 11, 12 ) ↓ FOO_( 0, 10, 11, 12 ) FOO_( 1, 10, 11, 12 ) FOO_( 2, 10, 11, 12 )
#define PP_REPEAT_1( n, callback,... ) PP_CONCAT(PP_REPEAT_1_,n)( callback, ##__VA_ARGS__ ) #define PP_REPEAT_1_0( callback,... ) #define PP_REPEAT_1_1( callback,... ) PP_REPEAT_1_0( callback, ##__VA_ARGS__ ) callback( 0, ##__VA_ARGS__ ) #define PP_REPEAT_1_2( callback,... ) PP_REPEAT_1_1( callback, ##__VA_ARGS__ ) callback( 1, ##__VA_ARGS__ ) #define PP_REPEAT_1_3( callback,... ) PP_REPEAT_1_2( callback, ##__VA_ARGS__ ) callback( 2, ##__VA_ARGS__ ) #define PP_REPEAT_1_4( callback,... ) PP_REPEAT_1_3( callback, ##__VA_ARGS__ ) callback( 3, ##__VA_ARGS__ ) #define PP_REPEAT_1_5( callback,... ) PP_REPEAT_1_4( callback, ##__VA_ARGS__ ) callback( 4, ##__VA_ARGS__ ) #define PP_REPEAT_1_6( callback,... ) PP_REPEAT_1_5( callback, ##__VA_ARGS__ ) callback( 5, ##__VA_ARGS__ ) #define PP_REPEAT_2( n, callback,... ) PP_CONCAT(PP_REPEAT_2_,n)( callback, ##__VA_ARGS__ ) #define PP_REPEAT_2_0( callback,... ) #define PP_REPEAT_2_1( callback,... ) PP_REPEAT_2_0( callback, ##__VA_ARGS__ ) callback( 0, ##__VA_ARGS__ ) #define PP_REPEAT_2_2( callback,... ) PP_REPEAT_2_1( callback, ##__VA_ARGS__ ) callback( 1, ##__VA_ARGS__ ) #define PP_REPEAT_2_3( callback,... ) PP_REPEAT_2_2( callback, ##__VA_ARGS__ ) callback( 2, ##__VA_ARGS__ ) #define PP_REPEAT_2_4( callback,... ) PP_REPEAT_2_3( callback, ##__VA_ARGS__ ) callback( 3, ##__VA_ARGS__ ) #define PP_REPEAT_2_5( callback,... ) PP_REPEAT_2_4( callback, ##__VA_ARGS__ ) callback( 4, ##__VA_ARGS__ ) #define PP_REPEAT_2_6( callback,... ) PP_REPEAT_2_5( callback, ##__VA_ARGS__ ) callback( 5, ##__VA_ARGS__ ) #define PP_REPEAT_3( n, callback,... ) PP_CONCAT(PP_REPEAT_3_,n)( callback, ##__VA_ARGS__ ) #define PP_REPEAT_3_0( callback,... ) #define PP_REPEAT_3_1( callback,... ) PP_REPEAT_3_0( callback, ##__VA_ARGS__ ) callback( 0, ##__VA_ARGS__ ) #define PP_REPEAT_3_2( callback,... ) PP_REPEAT_3_1( callback, ##__VA_ARGS__ ) callback( 1, ##__VA_ARGS__ ) #define PP_REPEAT_3_3( callback,... ) PP_REPEAT_3_2( callback, ##__VA_ARGS__ ) callback( 2, ##__VA_ARGS__ ) #define PP_REPEAT_3_4( callback,... ) PP_REPEAT_3_3( callback, ##__VA_ARGS__ ) callback( 3, ##__VA_ARGS__ ) #define PP_REPEAT_3_5( callback,... ) PP_REPEAT_3_4( callback, ##__VA_ARGS__ ) callback( 4, ##__VA_ARGS__ ) #define PP_REPEAT_3_6( callback,... ) PP_REPEAT_3_5( callback, ##__VA_ARGS__ ) callback( 5, ##__VA_ARGS__ ) #define PP_REPEAT_4( n, callback,... ) PP_CONCAT(PP_REPEAT_4_,n)( callback, ##__VA_ARGS__ ) #define PP_REPEAT_4_0( callback,... ) #define PP_REPEAT_4_1( callback,... ) PP_REPEAT_4_0( callback, ##__VA_ARGS__ ) callback( 0, ##__VA_ARGS__ ) #define PP_REPEAT_4_2( callback,... ) PP_REPEAT_4_1( callback, ##__VA_ARGS__ ) callback( 1, ##__VA_ARGS__ ) #define PP_REPEAT_4_3( callback,... ) PP_REPEAT_4_2( callback, ##__VA_ARGS__ ) callback( 2, ##__VA_ARGS__ ) #define PP_REPEAT_4_4( callback,... ) PP_REPEAT_4_3( callback, ##__VA_ARGS__ ) callback( 3, ##__VA_ARGS__ ) #define PP_REPEAT_4_5( callback,... ) PP_REPEAT_4_4( callback, ##__VA_ARGS__ ) callback( 4, ##__VA_ARGS__ ) #define PP_REPEAT_4_6( callback,... ) PP_REPEAT_4_5( callback, ##__VA_ARGS__ ) callback( 5, ##__VA_ARGS__ )
Visual C++ の場合はこんな感じ
#if defined( _MSC_VER ) #define PP_REPEAT( n, callback,... ) PP_CONCAT(PP_REPEAT_,n)( callback, ##__VA_ARGS__ ) #define PP_REPEAT_0( callback,... ) #define PP_REPEAT_1( callback,... ) PP_REPEAT_0( callback, ##__VA_ARGS__ ) PP_INVOKE( callback, 0, ##__VA_ARGS__ ) #define PP_REPEAT_2( callback,... ) PP_REPEAT_1( callback, ##__VA_ARGS__ ) PP_INVOKE( callback, 1, ##__VA_ARGS__ ) #define PP_REPEAT_3( callback,... ) PP_REPEAT_2( callback, ##__VA_ARGS__ ) PP_INVOKE( callback, 2, ##__VA_ARGS__ ) #define PP_REPEAT_4( callback,... ) PP_REPEAT_3( callback, ##__VA_ARGS__ ) PP_INVOKE( callback, 3, ##__VA_ARGS__ ) #define PP_REPEAT_5( callback,... ) PP_REPEAT_4( callback, ##__VA_ARGS__ ) PP_INVOKE( callback, 4, ##__VA_ARGS__ ) #define PP_REPEAT_6( callback,... ) PP_REPEAT_5( callback, ##__VA_ARGS__ ) PP_INVOKE( callback, 5, ##__VA_ARGS__ ) #endif // _MSC_VER
使用例
#define FOO_( n ) debug_printf( "foo[" #n "]\n" ); #define FOO( n ) PP_REPEAT( n, FOO_ ) #define BAR_2( m, n ) debug_printf( "bar[" #n "][" #m "]\n" ); #define BAR_1( n ) PP_REPEAT( 2, BAR_2, n ) #define BAR( n ) PP_REPEAT( n, BAR_1 ) #define BAZ_4( l, i, j, k ) debug_printf( "baz[" #i "][" #j "][" #k "][" #l "]\n" ); #define BAZ_3( k, i, j ) PP_REPEAT( 2, BAZ_4, i, j, k ) #define BAZ_2( j, i ) PP_REPEAT( 2, BAZ_3, i, j ) #define BAZ_1( i ) PP_REPEAT( 2, BAZ_2, i ) #define BAZ( n ) PP_REPEAT( n, BAZ_1 ) int main(){ FOO( 2 ); BAR( 2 ); BAZ( 2 ); return 0; }
可変長引数が使えると簡単ですね
リストの個々の要素について指定したマクロを適用する
次のように指定したリストの個々の要素について指定したマクロを適用してくれるようなマクロを実装してみましょう
PP_FOR_EACH( FOO_, 1, 2, 3 ) ↓ FOO_( 1 ) FOO_( 2 ) FOO_( 3 )
これも基本的には PP_INVOKE や PP_REPEAT と同じです
まず呼び出し口となるマクロはオブジェクト形式のマクロにします
#define PP_FOR_EACH PP_CONCAT(PP_FOR_EACH_,PP_RECURSIVE(PP_FOR_EACH_RECURSIVE))
再帰判定には PP_FOR_EACH マクロ自身を使います
#undef PP_FOR_EACH_RECURSIVE_UNUSED #define PP_FOR_EACH_RECURSIVE( n ) PP_CONCAT(PP_FOR_EACH_RECURSIVE_, PP_CONCAT(PP_FOR_EACH_,n)(PP_FOR_EACH_RECURSIVE_UNUSED,0))
展開後に必要なマクロを用意します
#define PP_FOR_EACH_RECURSIVE_PP_FOR_EACH_1(...) 0 #define PP_FOR_EACH_RECURSIVE_PP_FOR_EACH_2(...) 0 #define PP_FOR_EACH_RECURSIVE_PP_FOR_EACH_3(...) 0 #define PP_FOR_EACH_RECURSIVE_PP_FOR_EACH_4(...) 0 #define PP_FOR_EACH_RECURSIVE_PP_FOR_EACH_RECURSIVE_UNUSED(...) 1
個々の実体を用意します
#define PP_FOR_EACH_1( callback,... ) PP_FOR_EACH_1_( PP_VA_ARGS_COUNT(__VA_ARGS__), callback, ##__VA_ARGS__ ) #define PP_FOR_EACH_1_( n, callback,... ) PP_CONCAT(PP_FOR_EACH_1_,n)( callback, ##__VA_ARGS__ ) #define PP_FOR_EACH_1_0( callback,... ) #define PP_FOR_EACH_1_1( callback, _0 ) callback(_0) #define PP_FOR_EACH_1_2( callback, _1,... ) callback(_1) PP_FOR_EACH_1_1( callback, ##__VA_ARGS__ ) #define PP_FOR_EACH_1_3( callback, _2,... ) callback(_2) PP_FOR_EACH_1_2( callback, ##__VA_ARGS__ ) #define PP_FOR_EACH_1_4( callback, _3,... ) callback(_3) PP_FOR_EACH_1_3( callback, ##__VA_ARGS__ ) #define PP_FOR_EACH_1_5( callback, _4,... ) callback(_4) PP_FOR_EACH_1_4( callback, ##__VA_ARGS__ ) #define PP_FOR_EACH_1_6( callback, _5,... ) callback(_5) PP_FOR_EACH_1_5( callback, ##__VA_ARGS__ ) #define PP_FOR_EACH_2( callback,... ) PP_FOR_EACH_2_( PP_VA_ARGS_COUNT(__VA_ARGS__), callback, ##__VA_ARGS__ ) #define PP_FOR_EACH_2_( n, callback,... ) PP_CONCAT(PP_FOR_EACH_2_,n)( callback, ##__VA_ARGS__ ) #define PP_FOR_EACH_2_0( callback,... ) #define PP_FOR_EACH_2_1( callback, _0 ) callback(_0) #define PP_FOR_EACH_2_2( callback, _1,... ) callback(_1) PP_FOR_EACH_2_1( callback, ##__VA_ARGS__ ) #define PP_FOR_EACH_2_3( callback, _2,... ) callback(_2) PP_FOR_EACH_2_2( callback, ##__VA_ARGS__ ) #define PP_FOR_EACH_2_4( callback, _3,... ) callback(_3) PP_FOR_EACH_2_3( callback, ##__VA_ARGS__ ) #define PP_FOR_EACH_2_5( callback, _4,... ) callback(_4) PP_FOR_EACH_2_4( callback, ##__VA_ARGS__ ) #define PP_FOR_EACH_2_6( callback, _5,... ) callback(_5) PP_FOR_EACH_2_5( callback, ##__VA_ARGS__ )
これだけです
簡単ですね
と言いたいところですが、可変長引数の数を得る PP_VA_ARGS_COUNT マクロをまだ書いていませんでした
どのような実装になるか考えてみましょう
可変長引数の数を得る
可変長引数で使えるものはなぜか __VA_ARGS__ だけです
引数の個数を返してくれるようなものは標準にはありません
自分で書く必要があります
具体的に考えましょう
#define PP_VA_ARGS_COUNT(...) __VA_ARGS__ ↓ // __VA_ARGS__ →空 // __VA_ARGS__ →p1 p1 // __VA_ARGS__ →p1, p2 p1, p2 // __VA_ARGS__ →p1, p2, p3 p1, p2, p3 . . .
ここからなんとか個数を導くことができるでしょうか
より具体的に引数の数が 0個の場合と 1個の場合を考えてみましょう
次のように可変長引数の後ろに欲しい数である 0 と 1 を並べてみると
#define PP_VA_ARGS_COUNT(...) PP_VA_ARGS_COUNT_(__VA_ARGS__, 0, 1) ↓ // __VA_ARGS__ →空 0, 1 // __VA_ARGS__ →p1 p1, 0, 1
2番目の引数の逆の数を得ることができれば個数になりそうですね
始めに指定した数の並びを逆にしてみるとよさそうです
#define PP_VA_ARGS_COUNT_(p1,N,...) N #define PP_VA_ARGS_COUNT(...) PP_VA_ARGS_COUNT_(__VA_ARGS__, 1, 0) ↓ // __VA_ARGS__ →空 1, 0 ↓ 0 // __VA_ARGS__ →p1 p1, 1, 0 ↓ 1
確かに引数の個数を得ることができています
引数が2つまでのときではどうでしょうか
#define PP_VA_ARGS_COUNT(...) PP_VA_ARGS_COUNT_(__VA_ARGS__, 2, 1, 0) ↓ // __VA_ARGS__ →空 2, 1, 0 // __VA_ARGS__ →p1 p1, 2, 1, 0 // __VA_ARGS__ →p1, p2 p1, p2, 2, 1, 0
今度は3番目の引数が個数になっているようです
#define PP_VA_ARGS_COUNT_(p1,p2,N,...) N ↓ // __VA_ARGS__ →空 2, 1, 0 ↓ 0 // __VA_ARGS__ →p1 p1, 2, 1, 0 ↓ 1 // __VA_ARGS__ →p1, p2 p1, p2, 2, 1, 0 ↓ 2
これを 9個まで対応してみると次のようになります
#define PP_VA_ARGS_COUNT_DUMMY 0 #define PP_VA_ARGS_COUNT_( _dummy, _9, _8, _7, _6, _5, _4, _3, _2, _1, N,... ) N #define PP_VA_ARGS_COUNT(...) PP_VA_ARGS_COUNT_ PP_LPAREN PP_VA_ARGS_COUNT_DUMMY, ##__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 PP_RPAREN
この解決方法の初出はどこだろう
__VA_NARG__ 辺りで検索すると出てくるのがそれかな?
再帰に対応した条件分岐
今までの条件分岐 PP_IF は次のような再帰的な使い方はできませんでした
#define FOO( n ) PP_IF( PP_GREATER_OR_EUQAL_TO( n, 1 ), PP_IF( PP_LESS( n, 3 ), FOO_, BAR_ ), BAZ_ )( n )
再帰に対応した PP_IF を実装してみましょう
まず PP_RECURSIVE の実装で PP_IF を使ってしまっているのでこれを PP_IF_1 など別の名前に置き換えましょう
(この PP_IF の使い方が再帰のように見えるかもしれませんが、(callback) が PP_IF の外に付いているのでこれは再帰ではありません)
#define PP_RECURSIVE_3( callback ) PP_IF_1( callback(3), PP_RECURSIVE_RETURN_3, PP_RECURSIVE_RETURN_4 )(callback) #define PP_RECURSIVE_2( callback ) PP_IF_1( callback(2), PP_RECURSIVE_RETURN_2, PP_RECURSIVE_3 )(callback) #define PP_RECURSIVE_1( callback ) PP_IF_1( callback(1), PP_RECURSIVE_RETURN_1, PP_RECURSIVE_2 )(callback)
次に呼び出し口となるマクロはオブジェクト形式のマクロにします
#define PP_IF PP_CONCAT(PP_IF_,PP_RECURSIVE(PP_IF_RECURSIVE))
再帰判定には PP_IF マクロ自身を使います
#undef PP_IF_RECURSIVE_UNUSED #define PP_IF_RECURSIVE( n ) PP_CONCAT(PP_IF_RECURSIVE_, PP_CONCAT(PP_IF_,n)(1,PP_IF_RECURSIVE_UNUSED(),PP_IF_RECURSIVE_UNUSED()))
展開後に必要なマクロを用意します
#define PP_IF_RECURSIVE_PP_IF_1(...) 0 #define PP_IF_RECURSIVE_PP_IF_2(...) 0 #define PP_IF_RECURSIVE_PP_IF_3(...) 0 #define PP_IF_RECURSIVE_PP_IF_4(...) 0 #define PP_IF_RECURSIVE_PP_IF_5(...) 0 #define PP_IF_RECURSIVE_PP_IF_6(...) 0 #define PP_IF_RECURSIVE_PP_IF_RECURSIVE_UNUSED(...) 1
個々の実体を用意します
#define PP_IF_1_0(then_,else_) else_ #define PP_IF_1_1(then_,else_) then_ #define PP_IF_2_0(then_,else_) else_ #define PP_IF_2_1(then_,else_) then_ #define PP_IF_3_0(then_,else_) else_ #define PP_IF_3_1(then_,else_) then_ #define PP_IF_4_0(then_,else_) else_ #define PP_IF_4_1(then_,else_) then_ #define PP_IF_1( cond, then_, else_ ) PP_CONCAT(PP_IF_1_,PP_BOOL(cond))(then_,else_) #define PP_IF_2( cond, then_, else_ ) PP_CONCAT(PP_IF_2_,PP_BOOL(cond))(then_,else_) #define PP_IF_3( cond, then_, else_ ) PP_CONCAT(PP_IF_3_,PP_BOOL(cond))(then_,else_) #define PP_IF_4( cond, then_, else_ ) PP_CONCAT(PP_IF_4_,PP_BOOL(cond))(then_,else_)
これだけです。簡単ですね
使用例
#define PP_LESS_0_0 0 #define PP_LESS_1_0 0 #define PP_LESS_2_0 0 #define PP_LESS_3_0 0 #define PP_LESS_4_0 0 #define PP_LESS_0_1 1 #define PP_LESS_1_1 0 #define PP_LESS_2_1 0 #define PP_LESS_3_1 0 #define PP_LESS_4_1 0 #define PP_LESS_0_2 1 #define PP_LESS_1_2 1 #define PP_LESS_2_2 0 #define PP_LESS_3_2 0 #define PP_LESS_4_2 0 #define PP_LESS_0_3 1 #define PP_LESS_1_3 1 #define PP_LESS_2_3 1 #define PP_LESS_3_3 0 #define PP_LESS_4_3 0 #define PP_LESS_0_4 1 #define PP_LESS_1_4 1 #define PP_LESS_2_4 1 #define PP_LESS_3_4 1 #define PP_LESS_4_4 0 #define PP_LESS_( m, n ) PP_LESS_ ## m ## _ ## n #define PP_LESS( m, n ) PP_LESS_( m, n ) #define PP_NOT( cond ) PP_IF( cond, 0, 1 ) #define PP_GREATER_OR_EUQAL_TO( m, n ) PP_NOT( PP_LESS( m, n )) #define BAZ_( n ) debug_printf( #n " < 1\n" ); #define BAR_( n ) debug_printf( #n " >= 1\n" ); #define FOO_( n ) debug_printf( #n " >= 1 && " #n " < 3\n" ); #define FOO( n ) PP_IF( PP_GREATER_OR_EUQAL_TO( n, 1 ), PP_IF( PP_LESS( n, 3 ), FOO_, BAR_ ), BAZ_ )( n ) int main(){ FOO( 0 ); FOO( 1 ); FOO( 2 ); FOO( 3 ); FOO( 4 ); return 0; }