読者です 読者をやめる 読者になる 読者になる

C++と色々

主にC++やプログラムに関する記事を投稿します。

コンパイル時ズンドコキヨシ

ズンドコキヨシ with C++14 - C++と色々

の続きです。コンパイル時にズンドコキヨシをやってみました。 static_assert で、ちゃんとシーケンスの最後の5つがズンズンズンズンドコになっているかを検証しています。Sprout.RandomとBoost.MPLを使用しています。参考にした記事は以下になります。Sprout.Randomの使い方と、call_nを参考にしました。

Sprout.Random - コンパイル時の乱数生成 - ボレロ村上 - ENiyGmaA Code

http://d.hatena.ne.jp/osyo-manga/20130413/1365860245

The MPL Reference Manual - 1.60.0

#include <iostream>
#include <type_traits>
#include <boost/mpl/advance.hpp>
#include <boost/mpl/begin_end.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/iterator_range.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/vector.hpp>
#include <sprout/random.hpp>
namespace mpl = boost::mpl;

constexpr std::size_t seed = SPROUT_UNIQUE_SEED;
constexpr auto engine = sprout::random::minstd_rand0(seed);
constexpr auto dist = sprout::random::uniform_smallint<int>(0, 1);

template <typename Gen>
constexpr int call_n(Gen&& gen, int index) {
    return index <= 0 ? *gen : call_n(gen(), index - 1);
}

struct Zun {
    static constexpr char const* value = "ズン";
};

struct Doko {
    static constexpr char const* value = "ドコ";
};

template <typename Seq, int N>
struct push_back_zundoko;

template <typename Seq>
struct push_back_zundoko<Seq, 0>
{
    using type = typename mpl::push_back<Seq, Zun>::type;
};

template <typename Seq>
struct push_back_zundoko<Seq, 1>
{
    using type = typename mpl::push_back<Seq, Doko>::type;
};

template <typename Seq, bool EnoughSize>
struct is_zundoko_impl {
    using first = typename mpl::advance_c<typename mpl::end<Seq>::type, -5>::type;
    using last = typename mpl::end<Seq>::type;
    using range = mpl::iterator_range<first, last>;
    using type = typename mpl::equal<range, mpl::vector<Zun, Zun, Zun, Zun, Doko>>::type;
};

template <typename Seq>
struct is_zundoko_impl<Seq, false> {
    using type = mpl::false_;
};

template <typename Seq>
struct is_zundoko {
    using type = typename is_zundoko_impl<Seq, mpl::size<Seq>::type::value >= 5>::type;
};

template <typename Seq, int N, typename Completion>
struct zundoko_impl {
    using next_seq = typename push_back_zundoko<Seq, call_n(dist(engine), N)>::type;
    using completion = typename is_zundoko<next_seq>::type;
    using type = typename zundoko_impl<next_seq, N + 1, completion>::type;
};

template <typename Seq, int N>
struct zundoko_impl<Seq, N, mpl::true_> {
    using type = Seq;
};

struct zundoko {
    using type = zundoko_impl<mpl::vector<>, 0, mpl::false_>::type;
};


int main() {
    using zundoko_type = zundoko::type;
    static_assert(is_zundoko<zundoko_type>::type::value, "");
    mpl::for_each<zundoko_type>([](auto const& item) {
        std::cout << std::remove_reference<decltype(item)>::type::value;
    });
    std::cout << "キ・ヨ・シ!" << std::endl;
}

実行結果例(コンパイル時に乱数列生成し終わっているので、コンパイルし直さない限り、何度やっても同じ結果になります)

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

clang++3.4以上,g++4.9.0以上で動作します。MSVCは14.0でもconstexprのサポートが弱いため動きません

追記

コードリファクタリングしました

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

#include <iostream>
#include <boost/mpl/advance.hpp>
#include <boost/mpl/begin_end.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/iterator_range.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/vector.hpp>
#include <sprout/random.hpp>
namespace mpl = boost::mpl;

constexpr std::size_t seed = SPROUT_UNIQUE_SEED;
constexpr auto engine = sprout::random::minstd_rand0(seed);
constexpr auto dist = sprout::random::uniform_smallint<int>(0, 1);

template <typename Gen>
constexpr int call_n(Gen&& gen, int index) {
    return index <= 0 ? *gen : call_n(gen(), index - 1);
}

template <typename Seq>
using end_t = typename mpl::end<Seq>::type;

template <typename Iter, int N>
using advance_c_t = typename mpl::advance_c<Iter, N>::type;

template <typename Seq1, typename Seq2>
using equal_t = typename mpl::equal<Seq1, Seq2>::type;

struct zun {
    char const* value = "ズン";
};

struct doko {
    char const* value = "ドコ";
};

template <typename Seq, int N>
struct push_back_zundoko
    : public mpl::eval_if_c<N == 0,
        mpl::push_back<Seq, zun>,
        mpl::push_back<Seq, doko>
    >::type
{};

template <typename Seq, int N>
using push_back_zundoko_t = typename push_back_zundoko<Seq, N>::type;

template <typename Seq, bool EnoughSize, typename First>
struct is_zundoko_impl
    : public equal_t<
        mpl::iterator_range<First, end_t<Seq>>,
        mpl::vector<zun, zun, zun, zun, doko>
    >
{};

template <typename Seq, typename First>
struct is_zundoko_impl<Seq, false, First>
    : public mpl::false_
{};

template <typename Seq>
struct is_zundoko
    : public is_zundoko_impl<Seq,
        mpl::size<Seq>::type::value >= 5,
        advance_c_t<end_t<Seq>, -5>
    >
{};

template <typename Seq>
using is_zundoko_t = typename is_zundoko<Seq>::type;

template <typename Seq, int N, typename Completion>
struct zundoko_impl {
private:
    using next_seq = push_back_zundoko_t<Seq, call_n(dist(engine), N)>;
    using completion = is_zundoko_t<next_seq>;

public:
    using type = typename zundoko_impl<next_seq, N + 1, completion>::type;
};

template <typename Seq, int N>
struct zundoko_impl<Seq, N, mpl::true_> {
    using type = Seq;
};

using zundoko = zundoko_impl<mpl::vector<>, 0, mpl::false_>::type;

int main() {
    static_assert(is_zundoko<zundoko>::type::value, "");
    mpl::for_each<zundoko>([](auto const& item) {
        std::cout << item.value;
    });
    std::cout << "キ・ヨ・シ!" << std::endl;
}