C++と色々

主にC++やプログラムに関する記事を投稿します。

BOOST_PP_ITERATE

以前の記事でBOOST_PP_REPEATについて書きました。 BOOST_PP_REPEATは似たような記述を繰り返す際に利用できます。例えば、以下の様です(このコード自体に意味はありませんが…)

#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/repeat.hpp>

#define VALUE(z, n, d) const int BOOST_PP_CAT(VALUE_, n) = n;
BOOST_PP_REPEAT(4, VALUE, _)

展開後

const int VALUE_0 = 0; const int VALUE_1 = 1; const int VALUE_2 = 2; const int VALUE_3 = 3;

水平反復と垂直反復

BOOST_PP_REPEATは1つの行に反復したコードを生成します。プリプロセッサと同じ行に反復展開することを水平反復といいます。上記のような例では特に問題はありませんが、クラス定義などを水平反復で定義すると実際に生成されるコードは1行なので見づらく、正しく書けているか確かめたい時や、インテリセンスなどで展開コード見た時や、デバッグをする時に同じ位置に飛んだりして見づらく不便です。そういった場合、複数行に展開できる垂直反復が有効になります。そして、垂直反復する方法の1つにBOOST_PP_ITERATEがあります。

サンプル file.h

#ifndef BOOST_PP_IS_ITERATING

#   ifndef FILE_H
#       define FILE_H
#       include <boost/preprocessor/iteration/iterate.hpp>
#       include <boost/preprocessor/cat.hpp>

#       define BOOST_PP_ITERATION_LIMITS (0, 4)
#       define BOOST_PP_FILENAME_1 "file.h"
#       include BOOST_PP_ITERATE()
#   endif

#else

#   define n BOOST_PP_ITERATION()

const int BOOST_PP_CAT(VALUE_, n) = n;

#   undef n

#endif

展開後

const int VALUE_0 = 0;
const int VALUE_1 = 1;
const int VALUE_2 = 2;
const int VALUE_3 = 3;
const int VALUE_4 = 4;

と複数行に反復されています。


今回は自己反復という方法を使っています。自分自身のファイルを繰り返しincludeすることで垂直反復を実現しています。BOOST_PP_ITERATEによってファイルがincludeされた場合、反復中ということを表すためにBOOST_IS_ITERATINGが定義されています。反復中でない場合は定義されていません。

#ifndef BOOST_PP_IS_ITERATING

//反復中でない(1回目のinclude)
//ここで反復前の初期化や設定を行う

#else

//反復中
//反復中の処理を書く

#endif

#ifndef BOOST_PP_IS_ITERATING節を見てみましょう。

#   ifndef FILE_H
#       define FILE_H

...

#   endif

#ifndef FILE_Hから#endifまでは普通の多重インクルードガードです。ここに垂直反復したいコードの初期状態を書いたりします。垂直反復でクラステンプレートの特殊化を書きたい場合はここにプライマリーテンプレートクラスを書いたり、などです。

#ifndef BOOST_PP_IS_ITERATING

#   ifndef FILE_H
#       define FILE_H
#       include <boost/preprocessor/iteration/iterate.hpp>
#       include <boost/preprocessor/cat.hpp>

#       define BOOST_PP_ITERATION_LIMITS (0, 4)
#       define BOOST_PP_FILENAME_1 "file.h"
#       include BOOST_PP_ITERATE()
#   endif

BOOST_PP_ITERATEはboost/preprocessor/iteration/iterate.hppにあります。BOOST_PP_ITERATEを使うため定義しなければならないマクロが2つあります。
1つはBOOST_PP_ITERATION_LIMITSというタプルです。これは、何回繰り返したいのかの数を(<最初の数>, <終わりの数>)と定義します。2つめはBOOST_PP_FILENAME_1です。繰り返しincludeするファイルを指定します。今回は自分自身なので"file.h"と書きます。そして最後に#include BOOST_PP_ITERATE()と呼び出すことで反復開始です。
次に反復中に展開される#else節を見てみましょう。

#   define n BOOST_PP_ITERATION()

const int BOOST_PP_CAT(VALUE_, n) = n;

#   undef n

現在繰り返しの何回目かはBOOST_PP_ITERATION()で取得出来ます。今回の場合は0から4のトークンになります。そのまま使うこともできますが、見づらいに書くの面倒なのでnというマクロにtypedefします。*1 次のループで新しい繰り返しインデックスを新しいnに再定義したいのでnをundefします。*2


一見複雑そうに見えますが、使うだけなら難しことはしていません。水平反復と垂直反復、適切に使い分けていきたいです。

*1:一般にマクロにこういった名前付けをつけるのは良くないとされていますが、それはグローバルマクロの話で、ローカルマクロで最後undefされ、このヘッダをインクルードしたファイルで使えないので問題ありません。

*2:undefしないと場合によっては「定義済みのマクロを再定義しようとしています」と警告が出るかもしれませんので