2008-09-25

プリプロセッサメタプログラミングで25個以上のデータを扱いたい場合

せっかくいい機会なので、Boost.Preprocessorのその他のData Typeも紹介してみる。前回の例では、enumのメンバは25個までしか使えない。それはarrayやtupleの実装が25個までだからだ。しかし現実的には、enumのサイズは、32bitや64bitであったりする。25個しか使えないというのはすこし問題がある。

まず、sequenceを使ってみよう。これは現在、256個までの要素が使える。これを使って先ほどのコードを書き直すと、次のようになる。

#include <boost/preprocessor/repetition/enum.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/seq/elem.hpp>

#define HITO_PP_ENUM_MEMBER( z, n, data ) \
BOOST_PP_SEQ_ELEM(n, data) = 1 << n

#define HITO_PP_SHIFT_ENUM( name, array ) \
enum name { \
BOOST_PP_ENUM( BOOST_PP_SEQ_SIZE( array ), HITO_PP_ENUM_MEMBER, array ) \
}


#define HITO_PP_SEQ_ENUM_IDENTIFIERS \
(ONE)(TWO)(THREE)(FOUR)(FIVE)(SIX)(SEVEN)(EIGHT)(NINE)(TEN) \
(ELEVEN)(THIRTEEN)(FOURTEEN)(FIFTEEN)(SIXTEEN)(SEVENTEEN)(EIGHTEEN)(NINETEEN)(TWENTY) \
(TWENTYONE)(TWENTYTWO)(TWENTYTHREE)(TWENTYFOUR)(TWENTYFIVE)(TWENTYSIX)(TWENTYSEVEN)(TWENTYEIGHT)(TWENTYNINE)(THIRTY)


HITO_PP_SHIFT_ENUM( foo, HITO_PP_SEQ_ENUM_IDENTIFIERS ) ;

#undef HITO_PP_SEQ_ENUM_IDENTIFIERS

これは256個までいけるのだが、ひとつひとつ括弧が必要なのが、ちょっと格好悪い。sequenceを使う利点として、その数を明示する必要がないことがあるが、しかしこの括弧の読みにくさを見れば、そんな利点はどうでもよくなってしまう。

さて、listだが、これは次回に回そう。とても面倒だ。

と思ったけれど、どうしてもこの問題の場合、数字が絡むので、256個以上は難しそうだ。とりあえず、sequenceからlistを生成するメタプログラミングでお茶を濁しておこう。

#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/seq/elem.hpp>
#include <boost/preprocessor/seq/fold_right.hpp>

#define HITO_PP_SEQ_ENUM_IDENTIFIERS \
(ONE)(TWO)(THREE)(FOUR)(FIVE)(SIX)(SEVEN)(EIGHT)(NINE)(TEN) \
(ELEVEN)(THIRTEEN)(FOURTEEN)(FIFTEEN)(SIXTEEN)(SEVENTEEN)(EIGHTEEN)(NINETEEN)(TWENTY) \
(TWENTYONE)(TWENTYTWO)(TWENTYTHREE)(TWENTYFOUR)(TWENTYFIVE)(TWENTYSIX)(TWENTYSEVEN)(TWENTYEIGHT)(TWENTYNINE)(THIRTY)

#define op(s, state, elem) (elem, state)
BOOST_PP_SEQ_FOLD_RIGHT(op, BOOST_PP_NIL, HITO_PP_SEQ_ENUM_IDENTIFIERS)
#undef op

このメタコードは、プリプロセスの結果、

(ONE , (TWO , (THREE , (FOUR , (FIVE , (SIX , (SEVEN , (EIGHT , (NINE , (TEN , (ELEVEN , (THIRTEEN , (FOURTEEN , (FIFTEEN , (SIXTEEN , (SEVENTEEN , (EIGHTEEN , (NINETEEN , (TWENTY , (TWENTYONE , (TWENTYTWO , (TWENTYTHREE , (TWENTYFOUR , (TWENTYFIVE , (TWENTYSIX , (TWENTYSEVEN , (TWENTYEIGHT , (TWENTYNINE , (THIRTY , BOOST_PP_NIL)))))))))))))))))))))))))))))

となる。これがlistである。

2 comments:

Anonymous said...

マクロで大量に生成するなら外部のスクリプト処理系でコード生成するようにした方がいいかなと思います。
これなら要素数の制限もありませんし、より柔軟にかつわかりやすく書けますからね。
インテリセンスとかは確実に死にますけど…
ごく単純なものならCのマクロでも十分なんですけど、
反復や複雑な分岐を含んだものを無理にCの範囲だけでやる必要性ってあるんでしょうかね?

江添亮 said...

プログラムのビルドには、コードを生成するのには、必要なツールをすべて含めなければなりません。だからもしコードの生成にPythonを用いたら、ビルドにはPythonの実行環境が必須になりますし、自作のプログラムを用いたら、そのプログラムが必須になってしまいます。
プリプロセッサを使えば、必要なのはコンパイラだけですみます。それに、大抵の場合、生成したい機械的なパターンのコードというのは、数十から数百ぐらいですからね。プリプロセッサで十分用が足ります。

ただ、メタプログラミングを話せない人には暗号のようなコードになるのが欠点ですが。