Boost.PPでFizzBuzzし、コンパイル時に逆FizzBuzzで結果を検証する
Twitterで話題にしたので実装しました。
プリプロセッサでFizzBuzzの実装に結構苦労しましたがslotを駆使してなんとかなりました。
プリプロセッサでの再帰をslot(変数として働く)の利用と自分自身のincludeを使うことで実現しています。
#ifndef INIT #define INIT #include <type_traits> #include <boost/mpl/vector_c.hpp> #include <boost/mpl/at.hpp> #include <sprout/utility/pair.hpp> #include <boost/preprocessor/slot/slot.hpp> #include <boost/preprocessor/comparison/equal.hpp> #include <boost/preprocessor/comparison/not_equal.hpp> #include <boost/preprocessor/punctuation/comma_if.hpp> #include <boost/preprocessor/arithmetic/dec.hpp> // set range #define FIRST 3 #define LAST 33 #define BOOST_PP_VALUE BOOST_PP_DEC(FIRST) #include BOOST_PP_ASSIGN_SLOT(1) namespace mpl = boost::mpl; namespace ifzbz { static constexpr auto fizz = 0; static constexpr auto buzz = 1; static constexpr auto fizzbuzz = 2; using base = mpl::vector_c<std::size_t, fizz, buzz, fizz, fizz, buzz, fizz, fizzbuzz>; template<std::size_t first, std::size_t last, std::size_t step = 0, bool finish = step >= last, std::size_t... args> struct circulate { using type = mpl::vector_c<std::size_t, args...>; }; template<std::size_t first, std::size_t last, std::size_t step, std::size_t... args> struct circulate<first, last, step, false, args...> : circulate<first, last, step + 1, first + step >= last, args..., mpl::at_c<base, (first + step) % 7>::type::value> {}; constexpr std::size_t calc_sum(const std::size_t& n, const std::size_t& size, const std::size_t& step = 0, const std::size_t& result = 0) { return step >= size ? result : (n + step) % 7 == 0 ? calc_sum(n, size, step + 1, result + 2) : (n + step) % 7 == 1 ? calc_sum(n, size, step + 1, result + 1) : (n + step) % 7 == 2 ? calc_sum(n, size, step + 1, result + 3) : (n + step) % 7 == 3 ? calc_sum(n, size, step + 1, result + 1) : (n + step) % 7 == 4 ? calc_sum(n, size, step + 1, result + 2) : (n + step) % 7 == 5 ? calc_sum(n, size, step + 1, result + 3) : calc_sum(n, size, step + 1, result + 3) ; } template<std::size_t... args> struct inverse_fizzbuzz { static constexpr auto value = sprout::pair<std::size_t, std::size_t>(0, 0); }; template<> struct inverse_fizzbuzz<fizz> { static constexpr auto value = sprout::pair<std::size_t, std::size_t>(3, 3); }; template<std::size_t... args> struct inverse_fizzbuzz<fizz, fizz, args...> { static constexpr auto value = std::is_same< typename circulate<2, sizeof...(args) + 2 + 1>::type, mpl::vector_c<std::size_t, fizz, fizz, args...> >::value ? sprout::pair<std::size_t, std::size_t>(6, calc_sum(6, sizeof...(args)) + 9) : sprout::pair<std::size_t, std::size_t>(0, 0) ; }; template<> struct inverse_fizzbuzz<fizz, buzz> { static constexpr auto value = sprout::pair<std::size_t, std::size_t>(3, 5); }; template<> struct inverse_fizzbuzz<fizz, buzz, fizz> { static constexpr auto value = sprout::pair<std::size_t, std::size_t>(3, 6); }; template<std::size_t... args> struct inverse_fizzbuzz<fizz, buzz, fizz, fizz, args...> { static constexpr auto value = std::is_same< typename circulate<0, sizeof...(args) + 0 + 3>::type, mpl::vector_c<std::size_t, fizz, buzz, fizz, fizz, args...> >::value ? sprout::pair<std::size_t, std::size_t>(3, calc_sum(3, sizeof...(args)) + 9) : sprout::pair<std::size_t, std::size_t>(0, 0) ; }; template<std::size_t... args> struct inverse_fizzbuzz<fizz, buzz, fizz, fizzbuzz, args...> { static constexpr auto value = std::is_same< typename circulate<3, sizeof...(args) + 3 + 3>::type, mpl::vector_c<std::size_t, fizz, buzz, fizz, fizzbuzz, args...> >::value ? sprout::pair<std::size_t, std::size_t>(6, calc_sum(6, sizeof...(args)) + 15) : sprout::pair<std::size_t, std::size_t>(0, 0) ; }; template<std::size_t... args> struct inverse_fizzbuzz<fizz, fizzbuzz, args...> { static constexpr auto value = std::is_same< typename circulate<5, sizeof...(args) + 5 + 1>::type, mpl::vector_c<std::size_t, fizz, fizzbuzz, args...> >::value ? sprout::pair<std::size_t, std::size_t>(12, calc_sum(6, sizeof...(args)) + 15) : sprout::pair<std::size_t, std::size_t>(0, 0) ; }; template<> struct inverse_fizzbuzz<buzz> { static constexpr auto value = sprout::pair<std::size_t, std::size_t>(5, 5); }; template<> struct inverse_fizzbuzz<buzz, fizz> { static constexpr auto value = sprout::pair<std::size_t, std::size_t>(5, 6); }; template<std::size_t... args> struct inverse_fizzbuzz<buzz, fizz, fizz, args...> { static constexpr auto value = std::is_same< typename circulate<1, sizeof...(args) + 1 + 2>::type, mpl::vector_c<std::size_t, buzz, fizz, fizz, args...> >::value ? sprout::pair<std::size_t, std::size_t>(5, calc_sum(3, sizeof...(args)) + 9) : sprout::pair<std::size_t, std::size_t>(0, 0) ; }; template<std::size_t... args> struct inverse_fizzbuzz<buzz, fizz, fizzbuzz, args...> { static constexpr auto value = std::is_same< typename circulate<4, sizeof...(args) + 4 + 2>::type, mpl::vector_c<std::size_t, buzz, fizz, fizzbuzz, args...> >::value ? sprout::pair<std::size_t, std::size_t>(10, calc_sum(6, sizeof...(args)) + 15) : sprout::pair<std::size_t, std::size_t>(0, 0) ; }; template<std::size_t... args> struct inverse_fizzbuzz<fizzbuzz, args...> { static constexpr auto value = std::is_same< typename circulate<6, sizeof...(args) + 6 + 0>::type, mpl::vector_c<std::size_t, fizzbuzz, args...> >::value ? sprout::pair<std::size_t, std::size_t>(15, calc_sum(6, sizeof...(args)) + 15) : sprout::pair<std::size_t, std::size_t>(0, 0) ; }; } int main() { using namespace ifzbz; static_assert(inverse_fizzbuzz< #endif #if BOOST_PP_SLOT(1) != LAST #undef BOOST_PP_VALUE #define BOOST_PP_VALUE BOOST_PP_SLOT(1) + 1 #include BOOST_PP_ASSIGN_SLOT(1) #undef BOOST_PP_VALUE #define BOOST_PP_VALUE BOOST_PP_SLOT(1) % 3 #include BOOST_PP_ASSIGN_SLOT(2) #undef BOOST_PP_VALUE #define BOOST_PP_VALUE BOOST_PP_SLOT(1) % 5 #include BOOST_PP_ASSIGN_SLOT(3) #undef BOOST_PP_VALUE #define BOOST_PP_VALUE BOOST_PP_SLOT(1) % 15 #include BOOST_PP_ASSIGN_SLOT(4) #undef BOOST_PP_VALUE #if BOOST_PP_SLOT(4) == 0 #define BOOST_PP_VALUE fizzbuzz BOOST_PP_VALUE BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(BOOST_PP_SLOT(1), LAST)) #elif BOOST_PP_SLOT(2) == 0 #define BOOST_PP_VALUE fizz BOOST_PP_VALUE BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(BOOST_PP_SLOT(1), LAST)) #elif BOOST_PP_SLOT(3) == 0 #define BOOST_PP_VALUE buzz BOOST_PP_VALUE BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(BOOST_PP_SLOT(1), LAST)) #else #define BOOST_PP_VALUE #endif #if BOOST_PP_EQUAL(BOOST_PP_SLOT(1), LAST) == 1 #define YIELD #endif #include __FILE__ #endif #ifdef YIELD >::value == sprout::pair<std::size_t, std::size_t>(FIRST, LAST), ""); } #undef YIELD #endif