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で書いて通ったけど、このテンプレートマシマシのコード書いたらどういう反応するんだろうか……
2023/04/03更新: プラグイン変更に伴うデザイン崩れに対応