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で書いて通ったけど、このテンプレートマシマシのコード書いたらどういう反応するんだろうか……

2023/04/03更新: プラグイン変更に伴うデザイン崩れに対応

コメントを残す

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