I'm trying to precompute random values using C++11's random
library at compile time. I'm mostly following examples. What am I doing wrong here?
using namespace std;
#include <iostream>
#include <vector>
#include <random>
vector<double> rands;
typedef std::mt19937_64 RNG;
uint64_t seed_val;
RNG rng;
void initialize() {
rng.seed(seed_val);
}
constexpr vector<double> generate_random( ) //size_t numbers)
{
int numbers = 1000;
std::uniform_real_distribution<double> zero_one(0.0, 1.0);
for (unsigned int i = 0; i < numbers; i++) {
double rand_num = zero_one(rng);
rands.push_back( rand_num );
}
return rands;
}
int main()
{
cout << "TMP rands";
for_each( rands.begin(), rands.end(), [] (double value)
{
cout<<value<<endl;
});
}
Here's an example compile-time random number generator shamelessly stolen from here, but thought it might be useful for anyone who looks this up:
template<u32 S, u32 A = 16807UL, u32 C = 0UL, u32 M = (1UL<<31)-1>
struct LinearGenerator {
static const u32 state = ((u64)S * A + C) % M;
static const u32 value = state;
typedef LinearGenerator<state> next;
struct Split { // Leapfrog
typedef LinearGenerator< state, A*A, 0, M> Gen1;
typedef LinearGenerator<next::state, A*A, 0, M> Gen2;
};
};
Only constexpr
functions and constant expressions may be evaluated at compile time. That rules out <chrono>
and <random>
.
What you can do is access the __TIME__
preprocessor macro and define your own PRNG composed of one-line, constexpr
functions.
I would try pulling it in from an external source. A very simple example is to compile your program with defined macro variables in the compile command. Here $RANDOM
is a special built-in variable in unix/linux systems that automatically returns a random 16-bit number.
g++ -D__RANDOM__=$RANDOM yourprog.cpp -o yourprog
//yourprog.cpp
#include <iostream>
int main() {
std::cout << "Random variable " << __RANDOM__ << std::endl;
return 0;
}
You can also write your own script or executable to assign to your macro variable.
//DevRandomGenerator.cpp
#include <iostream>
#include <fstream>
class DevRandom {
private:
std::ifstream stream;
public:
DevRandom() {
stream.open("/dev/urandom",std::ios::in|std::ios::binary);
}
unsigned int unsignedInt() {
unsigned int u = 0;
stream.read((char*)&u, sizeof(unsigned int));
return u;
}
};
int main() {
DevRandom rand;
std::cout << rand.unsignedInt() << std::endl;
return 0;
}
then compile as:
g++ DevRandomGenerator.cpp -o DevRandomGenerator
g++ -D__RANDOM__="$(./DevRandomGenerator)" yourprog.cpp -o yourprog
A better random generator would be to write a program that uses audio and visual inputs.
Not just is system_clock::now()
not compile-time knowable, but your function is labeled as returning a bool, but has no return statement anywhere.
There is a research paper on the topic: Random number generator for C++ template metaprograms
containing code snippet for the __TIME__
trick. It also talks about supporting different random number engines and distributions as orthogonal choices.
I know this question is five years old, and already has an accepted answer. Even so, I would like to add that it certainly is possible to generate random numbers at compile time, with the understanding that you'll get the same sequence of random numbers each time you run the program. To put it simply, if the seed is known at compile time, the compiler is allowed to figure out what random numbers will be output, and just turn the program into "output this sequence of numbers."
Compilers will have limits to how aggressively they optimize, so I can't promise that they will always make this substitution, and I doubt any compiler would be able to make the substitution for something as complex as the Mersenne Twister, but something simpler like linear_congruential_engine
has a chance (also, the only way to be sure that it happened would be to have the compiler output assembly code, and then you look at the assembly code).
I know this is possible because I implemented a random generator modeled after random_device
that used Marsaglia's Xorshift algorithm. Since Marsaglia's paper actually included multiple related algorithms, I had the class take a template parameter to select which shift pattern to use. I wanted to know if the compiler would optimize out the switch
statement I used. I forgot to pass a seed, so the compiler used the default, i.e., the seed was known at compile time. When I looked at the assembly code, not only was the switch
gone, but GCC had optimized the program into "output these three numbers."
The final version of the program listed in the question never actually called the functions to generate the sequence of numbers, and never called the function to seed the generator. This version will do that, but I doubt it will be turned into "print this sequence of random numbers."
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <random>
int get_seed()
{
int hour = std::atoi(__TIME__);
int min = std::atoi(__TIME__ + 3);
int sec = std::atoi(__TIME__ + 6);
return 10000 * hour + 100 * min + sec;
}
int main()
{
// get_seed() returns an int based on __TIME__ (a string literal
// set by the preprocessor), which is known at compile time.
//
// Also, w/r/t the engines in <random>: not setting a seed explicitly
// will use a default seed, which is known at compile time. So if
// you're OK getting the same sequence of numbers for any compilation,
// then "std::mt19937_64 rng;" may be all you need.
std::mt19937_64 rng(get_seed());
std::uniform_real_distribution<double> zero_one(0.0, 1.0);
const int COUNT = 1000;
std::generate_n(std::ostream_iterator<double>(std::cout, "\n"), COUNT,
[&rng, &zero_one]() { return zero_one(rng); });
return 0;
}
As per the error message:
cpp11tmprands.cpp:22:15: error: ‘rands’ was not declared in this scope
The variable rands
is not declared in the scope of main
. Make it a global variable instead of local in generate_random
and that error will go away.