コンパイル時乱数の生成

と、いうものは Sprout にも実装されているんですが、ちょっと自分でも書いてみました。
まぁちと怪しいですが。

[ソース]

#include <utility>
#include <iterator>


struct random{
    using seed_type = std::size_t;
    
    constexpr random() : seed(2531011L){}
    constexpr random(seed_type seed) : seed(seed * 214013L + 2531011L){}
    constexpr random(random const& r) : random(r.seed){}

    constexpr seed_type
    operator ()() const{
        return ((seed) >> 16) & 0x7fff;
    }
    seed_type seed;
};


template<typename T = random>
struct random_sampling_iterator{

    typedef std::input_iterator_tag iterator_category;
    typedef decltype(std::declval<T>()()) value_type;
    typedef std::ptrdiff_t difference_type;
    typedef value_type* pointer;
    typedef value_type& reference;
    typedef T random_type;

    constexpr random_sampling_iterator<random_type>
    next() const{
        return { random_type{ gen } };
    }
    
    constexpr value_type
    operator *() const{
        return gen();
    }

    constexpr bool
    operator !=(random_sampling_iterator const& it) const{
        return *(*this) != *it;
    }
    
    random_type gen;
};

constexpr random_sampling_iterator<random>
make_random_sampling_iterator(random::seed_type seed){
    return {{ seed }};
}

template<typename T>
constexpr random_sampling_iterator<T>
next(random_sampling_iterator<T> const& it){
    return it.next();
}


#include <boost/mpl/integral_c.hpp>
#include <boost/mpl/print.hpp>
#include <sprout/array.hpp>
#include <sprout/pit.hpp>
#include <sprout/algorithm/copy_n.hpp>
#include <sprout/weed.hpp>


template<typename T, T Value>
void
print(){
    typedef typename boost::mpl::print<
        boost::mpl::integral_c<
            T,
            Value
        >
    >::type type;
}


int
main(){
    // 乱数
    constexpr random r{};
    static_assert(r() == 38, "");
    
    // 次の乱数を取得する場合は自分を使用して新しく作る
    constexpr random r2 = r;
    static_assert(r2() == 7719, "");

    // iterator にするとこんな感じ
    constexpr auto iter = make_random_sampling_iterator(0);
    static_assert(*iter == 38, "");

    constexpr auto iter2 = next(iter);
    static_assert(*iter2 == 7719, "");

    constexpr auto iter3 = next(iter2);
    static_assert(*iter3 == 21238, "");
    
    
    // コンパイル時間(秒数)をパースして乱数の種に
    namespace w = sprout::weed;
    constexpr auto time_sec_parser = w::omit[w::int_] >> ':' >> w::omit[w::int_] >> ':' >> w::int_;
    constexpr auto time = sprout::to_string(__TIME__);
    constexpr auto sec = w::parse(time.begin(), time.end(), time_sec_parser).attr();
    
    // sprout::copy_n を使用して10個の乱数を取得する
    constexpr auto randoms = sprout::copy_n(
        make_random_sampling_iterator(sec),
        10,
        sprout::pit<sprout::array<random_sampling_iterator<>::value_type, 10>>{}
    );

    // コンパイルした時間に依存してランダムな値が返ってくる
    print<std::decay<decltype(randoms[0])>::type, randoms[0]>();
    print<std::decay<decltype(randoms[1])>::type, randoms[1]>();
    print<std::decay<decltype(randoms[2])>::type, randoms[2]>();
    print<std::decay<decltype(randoms[3])>::type, randoms[3]>();
    print<std::decay<decltype(randoms[4])>::type, randoms[4]>();
    print<std::decay<decltype(randoms[5])>::type, randoms[5]>();
    print<std::decay<decltype(randoms[6])>::type, randoms[6]>();
    print<std::decay<decltype(randoms[7])>::type, randoms[7]>();
    print<std::decay<decltype(randoms[8])>::type, randoms[8]>();
    print<std::decay<decltype(randoms[9])>::type, randoms[9]>();
    print<std::decay<decltype(randoms[9])>::type, randoms[9]>();

    return 0;
}

[コンパイル時出力(イメージ)]

boost/boost_1_48_0/boost/mpl/print.hpp: In instantiation of 'struct boost::mpl::print<mpl_::integral_c<unsigned int, 26954u> >'
boost/boost_1_48_0/boost/mpl/print.hpp: In instantiation of 'struct boost::mpl::print<mpl_::integral_c<unsigned int, 5491u> >'
boost/boost_1_48_0/boost/mpl/print.hpp: In instantiation of 'struct boost::mpl::print<mpl_::integral_c<unsigned int, 14890u> >'
boost/boost_1_48_0/boost/mpl/print.hpp: In instantiation of 'struct boost::mpl::print<mpl_::integral_c<unsigned int, 15588u> >'
boost/boost_1_48_0/boost/mpl/print.hpp: In instantiation of 'struct boost::mpl::print<mpl_::integral_c<unsigned int, 23753u> >'
boost/boost_1_48_0/boost/mpl/print.hpp: In instantiation of 'struct boost::mpl::print<mpl_::integral_c<unsigned int, 21712u> >'
boost/boost_1_48_0/boost/mpl/print.hpp: In instantiation of 'struct boost::mpl::print<mpl_::integral_c<unsigned int, 13767u> >'
boost/boost_1_48_0/boost/mpl/print.hpp: In instantiation of 'struct boost::mpl::print<mpl_::integral_c<unsigned int, 1834u> >' 
boost/boost_1_48_0/boost/mpl/print.hpp: In instantiation of 'struct boost::mpl::print<mpl_::integral_c<unsigned int, 2008u> >' 
boost/boost_1_48_0/boost/mpl/print.hpp: In instantiation of 'struct boost::mpl::print<mpl_::integral_c<unsigned int, 29246u> >'

constexpr で乱数を生成する場合、色々と制限があって生成が手間なんですが iterator と sprout::copy_n を利用して、一括で生成を行っています。
これならまだ扱いやすそうですね。
コンパイル時乱数生成は比較的実用性がありそうな気がするのでなんか試してみたいですね。
実行時に固定されているけど乱数なデータってありそうな気がする。
それよりも constexpr printf が欲しいのですわ…。

[コンパイラ]

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