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 はもうちょっとマシにしたいですね…。