2008-06-25

コンパイル時計算のFizzBuzz

modを使わないFizzBuzz2 - wiseler : WAR IS PEACE

コンパイル時と実行時の合わせ技によるFizzBuzzをやられたので、純粋なコンパイル時FizzBuzzを書いてみることにする。

そもそもコンパイル時とは何か。それは、コンパイル時に1から100までの、どの値がFizzで、どの値がBuzzで、FizzBuzzで、あるいは数値で表示するかを計算してしまうことだ。だから実行時にすることは、既に計算された結果を表示するだけだ。

実行時に、あらかじめ計算しておこうとすると、100個の配列を用意して、そこに答えを格納することになるだろう。テンプレートメタプログラミングでは、型の配列、すなわちシークエンスを用いる。

#include <iostream>

#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
#define BOOST_MPL_LIMIT_VECTOR_SIZE 100
#include <boost/mpl/vector.hpp>

#include <boost/cstdint.hpp>
#include <boost/type_traits.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/for_each.hpp>


namespace mpl = boost::mpl ;

struct Fizz{ static char * const value ; } ;
struct Buzz{ static char * const value ; } ;
struct FizzBuzz{static char *const value ; } ;
char * const Fizz::value = "Fizz" ;
char * const Buzz::value = "Buzz" ;
char * const FizzBuzz::value = "FizzBuzz" ;

template<int i>
struct FizzBuzzSequence
{
  typedef typename mpl::eval_if_c< (i % 3 == 0) && (i % 5 == 0), mpl::identity< FizzBuzz >
            , mpl::eval_if_c< (i % 3 == 0), mpl::identity< Fizz >
              , mpl::eval_if_c< (i % 5 == 0), mpl::identity< Buzz >, mpl::int_< i >
              >
            >
           >::type value ;
  typedef typename mpl::push_back<
     typename FizzBuzzSequence< i - 1 >::type
    , value
  >::type type ;
} ;

template < >
struct FizzBuzzSequence<1>
{
  typedef mpl::vector< mpl::int_<1> > type ;
} ;

struct print
{
  template < typename T >
  void operator () (T) const
  {
    std::cout << T::value << "\n" ;
  }
} ;

int main()
{
  mpl::for_each< FizzBuzzSequence<100>::type >( print() ) ;
}

美しい。何と単純明快なコードであることだろう。実はMPLのシークエンスを使うのは初めてだったのだが、とても簡単で楽しかった。MPLは神によって書かれたコードではなかろうか。

なお、実行には、Boost.MPLのvectorの要素数を100まで用意しないといけない。すなわち、boost/mpl/vector以下に、vector60.hppからvector100.hppまでを用意する必要がある。といっても中身は単純だ。一部を単純に置き換えるだけで、後はすべてプリプロセッサメタプログラミングがやってくれる。

また当然だが、かなり規格準拠度の高いC++コンパイラが要求される。

1 comment:

Anonymous said...

拝見しました。すごい! boost::mplでこんなにすっきりするのですね。
ただ問題はコンパイルで、やはり、自分の環境(VisualStudio2005)ですとコンパイル中にエラー落ちしてしまいました…。2008だとうまくいくと思いますので、他日、詳しく見てみたいと思います。
# まさかこんなのまでCompile-Timeでいけるとは、C++は変態ですね(ほめ言葉)