鍋あり谷あり

テーマを決めずに適当に書いています。

Boost::Wave の話かと

http://d.hatena.ne.jp/Nabetani/20050818/p1
http://d.hatena.ne.jp/Nabetani/20050823/p1
の続きで、
http://d.hatena.ne.jp/Nabetani/20050818/c#c1124649895
http://d.hatena.ne.jp/Nabetani/20050823/c#c1124987740
の返信。
コメントにアンカーがついているのは便利だね。

で。文体が変わって。

Boost.Preprocessor の話でしたか。Boost.Wave の話だと思ってました。
すいません。最初からそう書いてありますね。とほほ。

それと。

template だけで可変長引数を使用する template は作れる

というのは何でしょうか?
C++ の道具で C99 の可変長引数マクロを受ける方法は

ぐらいしか思いつきません。Boost.MPL で、っていうのはどういう手法なんでしょう??

で。(また文体が変わり)折角なので、Boost.Preprocessor で書いてみた。
ターゲットは、先日書いた、加重平均関数。
まずは Boost.Preprocessor。

#define ARG_GEN(z, n, text) BOOST_PP_COMMA_IF(n) double w##n, double v##n
#define SUM_GEN(z, n, text) BOOST_PP_IF(n,+,) w##n*v##n
#define WEI_GEN(z, n, text) BOOST_PP_IF(n,+,) w##n
#define TEMPLATE(z, n, _) \
double wave( BOOST_PP_REPEAT_ ## z( n, ARG_GEN, nil ) ){\
	double d = BOOST_PP_REPEAT_ ## z( n, WEI_GEN, nil );\
	return ( BOOST_PP_REPEAT_ ## z( n, SUM_GEN, nil ) ) / w;\
}

BOOST_PP_REPEAT_FROM_TO( 1,10, TEMPLATE, nil ) // この行に全部展開される
#undef ARG_GEN
#undef SUM_GEN
#undef WEI_GEN
#undef TEMPLATE

思ったより簡単だった。思ったより簡単だっただけで、見ての通りちっとも簡単ではない。
これを見て何をする関数なのか理解するのには相当時間がかかると思うが、どうだろう。慣れれば簡単なのか?
理解しにくかったり保守しにくかったりすることも重大な難点だが、デバッグしにくいのも困る。
ソースレベルデバッグを行うと、wave 関数内は全部「この行に全部展開される」とコメントしたところに入ってしまう。関数が長くなったらかなり悲惨な状況に陥る。

一方、ruby で書いたら

def gen(i,jo)
  (0..i).map{ |n| yield n }.join( jo )
end

puts (1..10).map{ |i| 
<<"FUNC"
double wave( #{gen(i, ', ' ){|n| "double w#{n}, double v#{n}"}} )
{
  double w = #{gen(i, '+'){|n| "w#{n}"}};
  return #{gen(i, '+'){|n| "w#{n}*v#{n}"}}/w;
}
FUNC
}.join("\n")

こんな感じ。もうちょっと簡単になるような気もするが、まあこんなところで。
比べるのも申し訳ないぐらい、理解しやすく保守しやすいように思うが、慣れの問題の可能性もゼロではない。
ruby がわからないとどうしようもないっていうのは難点だが、Boost.Preprocessor よりは知っていて得することが多いと思うので、勉強してくれと言いたい。
実行して作られたソースがデバッグの対象になるので、ソースレベルデバッグは楽ちん。