C++17でfizzbuzzを書いてみた

C++のメタプログラミングの練習で仕事中に暇な時間使ってizzbuzzを書いてみた。
書いた感想は「魔法かこれ?」と「何だこのクッソキモいコード……」

struct Fizz {
	constexpr static char value[5] = "Fizz";
};
struct Buzz {
	constexpr static char value[5] = "Buzz";
};
struct Fizzbuzz {
	constexpr static char value[9] = "FizzBuzz";
};
template <int num>
struct Num {
	constexpr static int value = num;
};

template <bool Cond, class Then, class Else>
struct if_c; 

template <class Then, class Else>
struct if_c<true, Then, Else> {
	using type = Then;
};

template <class Then, class Else>
struct if_c<false, Then, Else> {
	using type = Else;
};

template <int n, typename T = typename if_c <n % 15 == 0, Fizzbuzz, 
					typename if_c <n % 5 == 0, Buzz, 
					typename if_c <n % 3 == 0, Fizz, 
					Num<n> >::type>::type>::type>
struct FizzBuzz: public T {};

#include <iostream>

template <int n> void fizzbuzz() {
	fizzbuzz<n - 1>();
	std::cout << FizzBuzz<n>::value << std::endl;
}

template <> void fizzbuzz<0>() {
	std::cout << 0 << std::endl;
}

int main() {
	fizzbuzz<150>();
	return 0;
}

構造体 Fizz, Buzz,Fizzbuzz, Numは表示する値を格納する構造体。
Fizz, Buzz, Fizzbuzzはそれぞれ固定値だけど、Numはテンプレート引数でもらった値をvalueに格納。

構造体 if_cはテンプレート特殊化を使ってFizzBuzz構造体の派生元を決定するもの。
bool Condがtrueだったら、

template <class Then, class Else>
struct if_c<true, Then, Else> {
	using type = Then;
};

が適用されて、Thenの型がtypeに入ったif_cが作成される。
falseだったら

template <class Then, class Else>
struct if_c<false, Then, Else> {
	using type = Else;
};

こっちが実体化されて、Elseの型がtypeに入ったif_cが作成される。

template <int n, typename T = typename if_c <n % 15 == 0, Fizzbuzz, 
					typename if_c <n % 5 == 0, Buzz, 
					typename if_c <n % 3 == 0, Fizz, 
					Num<n> >::type>::type>::type>
struct FizzBuzz: public T {};

はテンプレート引数でもらったnでFizzかBuzzかFizzbuzzか判定してFizzBuzzの派生元Tを決定。

std::cout << FizzBuzz<n>::value << std::endl;

これで格納したvalueを出力して終了……

template <int n> void fizzbuzz() {
	fizzbuzz<n - 1>();
	std::cout << FizzBuzz<n>::value << std::endl;
}

template <> void fizzbuzz<0>() {
	std::cout << 0 << std::endl;
}

int main() {
	fizzbuzz<150>();
	return 0;
}

これで、テンプレート引数nをから1引きながらどんどんと再帰していって、n=0になったら0を出力。あとは各FizzBuzz::valueを出力することで任意の数のFizzBuzzを出力するようにした。
最終的に(一応)ifもforもないFizzBuzzが出来上がった。なんだコレ魔法か?

コンパイルは

g++ -std=c++17 fizzbuzz.cpp -o fizzbuzz.exe

注意点としては、main()のfizzbuzz<150>();の値をあまり大きくしすぎると、ネストが深くなりすぎてコンパイラがコンパイルを拒否する。
あとバイナリがでかい。

前に派遣先の面談で「Fizzbuzz書けますか?」って言われたときに普通にforとif使ってC++とPHPで書いて通ったけど、このテンプレートマシマシのコード書いたらどういう反応するんだろうか……

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください