2008-01-16

BoostのMPLへのいざない part 1

 不幸にも(幸いにも?)私はプログラマにならない道を進むわけだけれど、私がC++を学んだ証を残しておきたいと思った。趣味としてのプログラミングは、まあ恵まれている。自分が好きなように書いていいのだから。  世に、Boostなる一群のライブラリがある。中でもMPLは極めてすばらしいのだが、広く世間一般に知られていない。思うに、これはきっかけがないからなのだろう。第一、日本語の文献もほとんど存在しない。ここはひとつ、チュートリアルのようなものを書いてみるべきか。  ここにすばらしい本がある。C++ Templatesと、C++ Template Metaprogrammingである。前者はC++のテンプレート全般について、深く解説している本で、後者は、Boost、とくにMPLに対する本だ。http://www.boost.org/libs/mpl/doc/index.htmlで、その重要な導入部分を読むことができる。この本のすばらしいことは、実に具体的で分かりやすいことだ。翻訳ではないが、この文章を参考に、チュートリアルを書いてみる。なお、このチュートリアルは、具体的なことしか説明しないつもりだ。その背景にあるテンプレートやSTLのコンセプトは説明しないし、個々の文法についていちいち解説するつもりもない。 1. 数値計算 1.1 2進数リテラル  C++には、2進数リテラルが存在しない。なぜ存在しないかの議論はさておくとして、C++の規格にないものはない。あるいは、コンパイラ独自の拡張昨日を使えるかもしれないが、それは移植性の問題を引き起こす。なんとか、C++の規格の中で、2進数を扱う方法はないものか。 1.2 実行時計算  確かに2進数リテラルはない。しかし、2進数としてかかれた10進数リテラルを、10進数に直すことは、特に難しくない。C言語の授業の初日の課題ぐらいにはなるだろう。
unsigned int binary(unsigned int value) { unsigned int ret = 0 ; for (int i = 0; value != 0; ++i ) { ret |= value%10 << i; value /= 10 ; } return ret ; } int main() { std::cout << binary(101010) << std::endl ; // 42 char buf[ binary(101010) ] ; // エラー }
 しかし、実行時に計算するのは、問題がある。C99でないと、配列を確保できないからだ。ここで必要なのは、コンパイル時の定数なのだ。 1.3 コンパイル時計算  そこで、テンプレートの出番だ。
template < unsigned int N > struct binary { static unsigned int const value = binary< N/10 >::value << 1 | N%10 ; } ; template < > struct binary<0> { static unsigned int const value = 0 ; } ; int main() { std::cout << binary<101010>::value << std::endl ; char buf[ binary<101010>::value ] ; }
 テンプレートを、再帰的に呼んでいる。どのように実体化されるかというと、 binary<101010> binary<10101> binary<1010> binary<101> binary<10> binary<1> binary<0>  0になったところで、特殊化が働き、この再帰的なループは終わる。そう、コンパイラに、テンプレートで計算させているわけだ。ループには再帰を、条件分岐には特殊化を使う。再帰に親しいプログラマならば、簡単にこのコードが理解できるはずだ。この手法を用いれば、フィボナッチ数や素数も計算できる。これは、数値に対する計算だ。  さて、とりあえずここまで書いた。この次は型に対する計算を書かなければ。具体的に説明するには、やはりiter_swapのようなものを実装していくのが一番だろう。C++ Template Metaprogrammingは実によくできている。  

1 comment:

Anonymous said...

good job.
part2も楽しみにしてます。