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