Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I use this document as a basis for my mini-library:

Motivation

The std::rand friends are discouraged in C++14, so we want:

  • A direct replacement to the std::rand friends. Despite of the security issues, std::rand is considered both handy and useful as a global uniform random number generator.

  • To expose the most widely-used combo in C++11 <random> without pushing the users to learn the whole design. Smoothing the learning curve can usually optimize the acceptance.that using rdtsc

Design Decisions

std::rand is a self-contained interface, so its replacement should be independent as well. In addition, I expect the interface to correctly expose the functionalities of <random> and lead to more robust and secure programs. The proposed replacement is

  • Distribution based. RNG must be used with a distribution; std::rand is a wrong design.

  • Randomly seeded before used. Improper seeding like rand(time(0)) may result in vulnerabilities.

  • Per-thread engine. Minimal interface should minimize astonishment, [with respect to] thread-safety and performance.

  • Manually seedable. User can observe repeatability in a given thread, which is a typical demand for debugging.

  • Type-safe. No integral promotion, no loss of distribution property during down-casting. For a given invocation, the inputs and the result have the same type.

Questions:

  • Do I follow the spec well? Primarily worried about 32-bit vs 64-bit issues.

  • How can I do the ugly enable_if better?

  • How can I do the ugly macros better?

  • Good way to seed? Neither rdtsc or std::random_device are very portable.

#include <cstdint>
#include <iostream>
#include <random>
#include <type_traits>

/* rdtsc
 *
 * The instruction measures the total pseudo-cycles since the processor
 * was powered on. Given the high frequency of today's machines, it's
 * extremely unlikely that two processors will return the same value
 * even if they booted at the same time and are clocked at the same
 * speed.
 */

// GCC macros (Clang also supports these)
// https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html
#if defined(__i386__)
    std::uint_fast32_t rdtsc(void)
    {
        std::uint_fast32_t tick;
        __asm__ __volatile__("rdtsc":"=a"(tick));
        return tick;
    }

    #define RTDSC_ENTROPY rdtsc()    
#elif defined(__x86_64__)
    std::uint_fast64_t rdtsc(void)
    {
        unsigned int tickl, tickh;
        __asm__ __volatile__("rdtsc":"=a"(tickl),"=d"(tickh));
        return ((std::uint_fast64_t)tickh << 32)|tickl;
    }

    #define RTDSC_ENTROPY rdtsc()
// MSVC (Visual C++)
#elif defined(_WIN64)
    #include <intrin.h>

    // returns 64-bit unsigned integer
    #define RTDSC_ENTROPY __rdtsc()
#endif

namespace better_rand
{

namespace detail
{
    /*
     * std::default_random_engine may default to a weak PRNG. In MSVC, it's
     * std::mt19937. In libstdc++ and libc++, it's std::minstd_rand0.
     *
     * Comparison (http://www.boost.org/doc/libs/1_59_0/doc/html/boost_random/performance.html)
     * shows that std::mt19937_64 potentially performs slower     
     */
    using random_engine = std::mt19937;

    // We only use a 64-bit seed with std::mt19937_64
    template <typename Engine = random_engine>
    typename std::enable_if<
        std::is_same<Engine, std::mt19937_64>::value,
        random_engine::result_type 
    >::type
    seed()
    {
        #if defined(RTDSC_ENTROPY)
            return RTDSC_ENTROPY;
        #else
            // std::random_device's result_type is unsigned int
            std::random_device rd;
            std::uint_fast64_t value = rd();
            value = (value << 32) | rd();
            return value;
        #endif
    }

    // 32-bit engine, 32-bit seed
    template <typename Engine = random_engine>
    typename std::enable_if<
        !std::is_same<Engine, std::mt19937_64>::value,
        random_engine::result_type 
    >::type
    seed()
    {
        #if defined(RTDSC_ENTROPY)
            return RTDSC_ENTROPY;
        #else
            // std::random_device's result_type is unsigned int
            return std::random_device(){};
        #endif
    }    

    // Global generator
    random_engine& prng()
    {
        thread_local static detail::random_engine re{detail::seed()};
        return re;
    }

    template <typename IntType>
    IntType randint(IntType a, IntType b)
    {
        // does not entirely satisfy 26.5.1.1/1(e).
        static_assert(std::is_integral<IntType>(), "not an integral");

        using distribution_type = std::uniform_int_distribution<IntType>;
        using param_type = typename distribution_type::param_type;

        thread_local static distribution_type d;
        return d(detail::prng(), param_type(a, b));
    }

    void reseed()
    {
        detail::prng().seed(detail::seed());
    }

    void reseed(detail::random_engine::result_type value)
    {
        detail::prng().seed(value);
    }
}

// Public API
using detail::randint;
using detail::reseed;

}

int main()
{
    using namespace better_rand;

    for (int i = 0; i < 10; ++i)
        std::cout << randint(1, 10) << " ";
    std::cout << "\n";

    reseed(0);
    // Should output 6 6 8 9 7 9 6 9 5 7
    for (int i = 0; i < 10; ++i)
        std::cout << randint(1, 10) << " ";
    std::cout << "\n";

    reseed();
    for (int i = 0; i < 10; ++i)
        std::cout << randint(1, 10) << " ";    
    std::cout << "\n";
}
share|improve this question
    
Are you referring to open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4316.html ? –  Martin R Sep 8 at 6:20
    
rdtsc is not portable at all (only Intel x86/x86_64 chips have it, not even Itanium). std::random_device is mandated by the standard, and even on implementations that don't provide cryto-grade random_device, they're probably better than picking from a monotonous source. –  Mat Sep 8 at 18:24

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.