constexpr で FizzBuzz

ひねくれたやり方です。
そこら辺に転がっていたソースを色々と詰め込んだので無駄に長いんですが…。
出力結果を配列にしています。

[ソース]

#include <cstddef>
#include <limits>

namespace ce{

template<std::size_t... Indices>
struct index_tuple{};

template<
    std::size_t Start,
    std::size_t Finish,
    std::size_t Step = 1,
    typename Acc = index_tuple<>,
    bool Break = (Start >= Finish)
>
struct index_range{
    typedef Acc type;
};

template<
    std::size_t Start, std::size_t Finish, std::size_t Step,
    std::size_t... Indices
>
struct index_range<Start, Finish, Step, index_tuple<Indices...>, false>
    : index_range<Start + Step, Finish, Step, index_tuple<Indices..., Start>>
{};


template<typename T, std::size_t Index = 0>
struct const_iterator{
    constexpr typename T::value_type
    operator *() const{
        return array[Index];
    }
    T const& array;
};

template<typename T, std::size_t Index>
constexpr const_iterator<T, Index+1>
next(const_iterator<T, Index> const& iter){
    return { iter.array };
}

template<typename T, std::size_t N>
constexpr bool
operator !=(const_iterator<T, N>, const_iterator<T, N>){
    return false;
}

template<typename T, std::size_t N, std::size_t M>
constexpr bool
operator !=(const_iterator<T, N>, const_iterator<T, M>){
    return true;
}

template<typename Iter1, typename Iter2>
struct distance;

template<typename T, std::size_t N, std::size_t M>
struct distance<const_iterator<T, N>, const_iterator<T, M>>{
    static std::size_t const value = M - N;
};


template<typename T, std::size_t N>
struct array{
    typedef T value_type;
    typedef T const* const_iterator;

    constexpr const_iterator
    begin() const{
        return &elems[0];
    }

    constexpr const_iterator
    end() const{
        return &elems[N];
    }

    constexpr T const&
    operator [](std::size_t n) const{
        return elems[n];
    }

    T elems[N ? N : 1];
};

template<typename T, typename ...Args>
constexpr array<T, sizeof...(Args)+1>
make_array(T t, Args... args){
    return {{ t, args... }};
}

template<typename T, std::size_t N, std::size_t ...Indices>
constexpr array<T, N>
make_array_impl(T const (&elems)[N], index_tuple<Indices...>){
    return {{ elems[Indices]... }};
}

template<typename T, std::size_t N>
constexpr array<T, N>
make_array(T const (&elems)[N]){
    return make_array_impl(elems, typename index_range<0, N>::type());
}


template<typename Char, std::size_t N>
struct c_string{
    typedef Char value_type;

    constexpr const_iterator<c_string<Char, N>, 0>
    begin() const{
        return { *this };
    }
    constexpr const_iterator<c_string<Char, N>, N-1>
    end() const{
        return { *this };
    }
    
    constexpr Char const&
    operator [](std::size_t n) const{
        return elems[n];
    }
    constexpr Char const* c_str() const{
        return &elems[0];
    }

    Char elems[N ? N : 1];
};

template<typename T, typename ...Args>
constexpr c_string<T, sizeof...(Args)+1>
make_c_string(T t, Args... args){
    return {{ t, args... }};
}

template<typename T, std::size_t N, std::size_t ...Indices>
constexpr c_string<T, N>
make_c_string_impl(T const (&elems)[N], index_tuple<Indices...>){
    return {{ elems[Indices]... }};
}

template<typename T, std::size_t N>
constexpr c_string<T, N>
make_c_string(T const (&elems)[N]){
    return make_c_string_impl(elems, typename index_range<0, N>::type());
}


template<typename T, std::size_t N, std::size_t ...Indices>
constexpr array<T, N+1>
push_back_impl(T const (&elems)[N], index_tuple<Indices...>, T value){
    return {{ elems[Indices]..., value }};
}

template<typename T, std::size_t N>
constexpr array<T, N+1>
push_back(T const (&elems)[N], T value){
    return push_back_impl(elems, typename index_range<0, N>::type(), value);
}

template<typename T, std::size_t N>
constexpr array<T, N+1>
push_back(array<T, N> const& v, T value){
    return push_back(v.elems, value);
}

template<typename T>
constexpr array<T, 1>
push_back(array<T, 0> const& v, T value){
    return {{ value }};
}


template<typename T, std::size_t N>
constexpr const_iterator<array<T, N>, 0>
begin(array<T, N> const& v){
    return { v };
}

template<typename T, std::size_t N>
constexpr const_iterator<array<T, N>, N>
end(array<T, N> const& v){
    return { v };
}


template<typename Iter, typename Func, typename T, std::size_t N>
constexpr array<T, N>
transform_impl(Iter, Iter, Func, array<T, N> const& stack){
    return stack;
}

template<typename Iter1, typename Iter2, typename Func, typename T, std::size_t N>
constexpr array<T, distance<Iter1, Iter2>::value + N>
transform_impl(Iter1 iter, Iter2 end, Func func, array<T, N> const& stack){
     return transform_impl(next(iter), end, func, push_back(stack, func(*iter)));
}

template<typename Iter1, typename Iter2, typename Func>
constexpr auto
transform(Iter1 begin, Iter2 end, Func func)
->array<decltype(func(*begin)), distance<Iter1, Iter2>::value>{
    return transform_impl(begin, end, func, array<decltype(func(*begin)), 0>{{}});
}

template<typename Range, typename Func>
constexpr auto
transform(Range const& range, Func func)
->decltype(transform(begin(range), end(range), func)){
    return transform(begin(range), end(range), func);
}


constexpr char
itoc(int n){
    return 0 <= n && n <= 9 ? '0' + n
        : '\0';
}

template<typename Char, std::size_t N, std::size_t ...Indices>
constexpr c_string<Char, N>
itoa_push_front_impl(Char const (&str)[N], index_tuple<Indices...>, Char c){
    return {{ c, str[Indices]..., '\0' }};
}

template<typename Char, std::size_t N>
constexpr c_string<Char, N>
itoa_push_front(Char const (&str)[N], Char c){
    static_assert(true, "");
    return itoa_push_front_impl(str, typename index_range<0, N-2>::type(), c);
}

template<typename Char, std::size_t N>
constexpr c_string<Char, N>
itoa_push_front(c_string<Char, N> const& str, Char c){
    return itoa_push_front(str.elems, c);
}

template<typename T, typename String>
constexpr String
itoa_impl(T n, String const& str){
    return n == 0 ? str
        : itoa_impl(n/10, itoa_push_front(str, itoc(n%10)));
}

template<typename T, typename String>
constexpr auto
itoa(T n, String const& str)->decltype(itoa_impl(n, str)){
    return n < 0 ? itoa_push_front(itoa_impl(n*-1, str), '-')
         : itoa_impl(n, str);
}

template<typename T>
constexpr c_string<char, std::numeric_limits<T>::digits10+3>
itoa(T n){
    return itoa(n, c_string<char, std::numeric_limits<T>::digits10+3>{{}});
}


}  // namespace ce


#include <iostream>
#include <type_traits>


struct to_fizzbuzz{
    template<
        typename T,
        typename Result = decltype(ce::itoa(T()))
    >
    constexpr Result
    operator ()(T n) const{
        return n % 15 == 0 ? Result{ "FizzBuzz" }
             : n %  3 == 0 ? Result{ "Fizz" }
             : n %  5 == 0 ? Result{ "Buzz" }
             : ce::itoa(n);
    }
};

int
main(){
    constexpr auto v = ce::make_array(
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
    );
    constexpr auto result = ce::transform(v, to_fizzbuzz());
    for(auto n : result){
        std::cout << n.c_str() << ", ";
    }
    return 0;
}

[出力]

1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16, 17, Fizz, 19, Buzz, 

constexpr な関数はアレなんですが、やっていることはそんなに難しくないはず。
iterator とか transform はもうちょっとマシにしたいですね…。

[コンパイラ]

  • g++ (GCC) 4.7.0 20110924 (experimental)