Search code examples
c++gccfizzbuzz

Fizz-Buzz at C++ compile time


We talked about the “fizz buzz” programming test today, I thought about implementing this with in C++ but with meta-programming. Ideally it would generate the output already during compilation time.

My current code uses templates, but it still has to be executed in order to produce the output. See it on Ideone.

I still use std::cout << in order to print the output on the screen. The FizzBuzz<i>::value will give either i, -1 (Fizz), -2 (Buzz), or -3 (FizzBuzz). Then word<value> is defined for the negative numbers and gives a char const *:

template <int i>
struct Print {
    static void print() {
        Print<i - 1>::print();
        auto const value = FizzBuzz<i>::value;
        if (value < 0) {
            std::cout << word<value>() << std::endl;
        } else {
            std::cout << value << std::endl;
        }
    }
};

The loop is gone due to recursion, there is a Print<0> which stops it.

Is there some way to print out word<value>() or value, which are known at compile time during compilation? This will probably not work with every compiler, so I am happy with a solution that works with GCC and/or Clang.


Solution

  • While making the string at compilation time is possible with some template trickery, printing it at compilation time doesn't seem reasonable.

    Here's the code that just makes the string:

    #include <iostream>
    #include <type_traits>
    #include <utility>
    
    // Compile time string
    template <char ...C> struct str_lit
    {
        static constexpr char value[] {C..., '\0'};
    };
    
    // Integer to str_lit
    constexpr std::size_t cexpr_pow(std::size_t x, std::size_t y)
    {
        std::size_t ret = 1;
        while (y--)
            ret *= x;
        return ret;
    }
    template <std::size_t N, std::size_t X, typename = std::make_index_sequence<X>> struct num_to_sl_impl;
    template <std::size_t N, std::size_t X, std::size_t ...Seq> struct num_to_sl_impl<N, X, std::index_sequence<Seq...>>
    {
        static constexpr auto func()
        {
            if constexpr (N >= cexpr_pow(10,X))
                return num_to_sl_impl<N, X+1>::func();
            else
                return str_lit<(N / cexpr_pow(10,X-1-Seq) % 10 + '0')...>{};
        }
    };
    template <std::size_t N> using num_to_sl = decltype(num_to_sl_impl<N,1>::func());
    
    // str_lit concatenation
    template <typename F, typename ...P> struct sl_cat_impl {using type = typename sl_cat_impl<F, typename sl_cat_impl<P...>::type>::type;};
    template <char ...C1, char ...C2> struct sl_cat_impl<str_lit<C1...>,str_lit<C2...>> {using type = str_lit<C1..., C2...>;};
    template <typename F, typename ...P> using sl_cat = typename sl_cat_impl<F, P...>::type;
    
    // The FizzBuzz
    template <std::size_t N> struct fizzbuzz_impl
    {
        using fizz = std::conditional_t<N % 3 == 0,
                                        str_lit<'f','i','z','z'>,
                                        str_lit<>>;
        using buzz = std::conditional_t<N % 5 == 0,
                                        str_lit<'b','u','z','z'>,
                                        str_lit<>>;
        using type = sl_cat<typename fizzbuzz_impl<N-1>::type,
                            std::conditional_t<N % 3 == 0 || N % 5 == 0,
                                               sl_cat<fizz, buzz>,
                                               num_to_sl<N>>,
                            str_lit<'\n'>>;
    };
    template <> struct fizzbuzz_impl<0>
    {
        using type = str_lit<>;
    };
    template <std::size_t N> using fizzbuzz = typename fizzbuzz_impl<N>::type;
    

    Example usage:

    int main()
    {
        std::cout << fizzbuzz<15>::value;
    }
    

    Output:

    1
    2
    fizz
    4
    buzz
    fizz
    7
    8
    fizz
    buzz
    11
    fizz
    13
    14
    fizzbuzz