ボレロ村上 - ENiyGmaA Code

中3女子です。

コンパイル時 FizzBuzz を読んでの Sprout C++ Library 改善

id:osyo-manga さんの Sprout を使った記事↓


[C++][Sprout]Sprout でコンパイルFizzBuzz
http://d.hatena.ne.jp/osyo-manga/20111207/1323187352


を読んで、「ああ、Sprout にこんな機能が足りなかったな」という部分に思い当たったので、
それを追加した上で件のコードを勝手に書き直してみました。


なお、元コードのロジック自体には手を入れていません。

#define SPROUT_CONFIG_SUPPORT_TEMPORARY_CONTAINER_ITERATION
#include <sprout/algorithm/transform.hpp>
#include <sprout/array.hpp>
#include <sprout/string.hpp>
#include <sprout/string/alias.hpp>
#include <sprout/operation.hpp>
#include <sprout/numeric/iota.hpp>
#include <sprout/pit.hpp>
#include <iostream>


// → sprout::make_string を使う
template<typename T, typename Result = sprout::string<15> >
constexpr Result
itoa_impl(T n){
     return n <= 10 ? sprout::make_string(char('0' + n))
          : sprout::realign_to<Result>(itoa_impl(n/10) + sprout::make_string(char('0' + n%10)));
}

template<typename T, typename Result = sprout::string<16> >
constexpr Result
itoa(T n){
     return n < 0 ? ("-" + itoa_impl(-n))
          : itoa_impl(n);
}


struct fizzbuzz{
     typedef sprout::string<8> result_type;

     constexpr result_type
     operator ()(int n) const{
          return n % 15 == 0 ? sprout::to_string("FizzBuzz")
               : n %  3 == 0 ? sprout::to_string("Fizz")
               : n %  5 == 0 ? sprout::to_string("Buzz")
               : sprout::realign_to<result_type>(itoa(n));
     }
};



int
main(){
     typedef fizzbuzz::result_type string;

     // 関数のテスト
     static_assert(fizzbuzz()(1) == "1", "");
     static_assert(fizzbuzz()(2) == "2", "");
     static_assert(fizzbuzz()(3) == "Fizz", "");
     static_assert(fizzbuzz()(5) == "Buzz", "");
     static_assert(fizzbuzz()(15) == "FizzBuzz", "");

     // 値を定義、[1..15]
     // → sprout::iota を使う
     constexpr auto source = sprout::iota(
          sprout::pit<sprout::array<int, 15> >(),
          1
          );

     // 値から FizzBuzz の出力へ変換
     // → 空のテンポラリの代わりに sprout::pit を使う
     constexpr auto result = sprout::transform(
          source.begin(),
          source.end(),
          sprout::pit<sprout::array<string, 15> >(),
          fizzbuzz()
          );

     // コンパイル時に計算結果を確認
     // → string の暗黙の変換が使える
     constexpr auto fizzbuzz_result = sprout::make_array<string>(
          sprout::to_string("1"),
          sprout::to_string("2"),
          sprout::to_string("Fizz"),
          sprout::to_string("4"),
          sprout::to_string("Buzz"),
          sprout::to_string("Fizz"),
          sprout::to_string("7"),
          sprout::to_string("8"),
          sprout::to_string("Fizz"),
          sprout::to_string("Buzz"),
          sprout::to_string("11"),
          sprout::to_string("Fizz"),
          sprout::to_string("13"),
          sprout::to_string("14"),
          sprout::to_string("FizzBuzz")
     );
     static_assert(result == fizzbuzz_result, "");

     // 実行時に出力
     for(auto&& str : result){
          std::cout << str << ", ";
     }
     std::cout << std::endl;

     return 0;
}


まず追加したのは、sprout::make_string です。
これは、文字型のパラメータパックから文字列を構築する関数です。

static_assert(sprout::make_string('f', 'o', 'o', 'b', 'a', 'r') == "foobar", "");


次に、sprout::basic_string の暗黙の変換です。
string → string (N < M) の暗黙の変換ができます。

string<10> str = sprout::to_string("foobar");


また、これは以前からあったものですが sprout::iota が使えます。

#include <sprout/numeric/iota.hpp>


sprout::iota(c, v) は、コンテナ c の要素を v, v+1, v+2,... で充填します。


Sprout のアルゴリズムを使う際、テンポラリ変数が必要になるケースでは sprout::pit が使えます。

#include <sprout/pit.hpp>


sprout::pit は、それ自体は要素を持たないけれど、Sprout のアルゴリズムに渡すと
Container がそのアルゴリズムの結果型になるという性質を持ちます。


これを使うことでテンポラリ変数の使用を回避できます。