0

I'm trying to write a multi-file program including the following code related to random number generation:

in help.h file:

extern random_device rand_dev;
extern ranlux48 rand_eng;
extern uniform_real_distribution<> zero2one_dist;

in help.cpp file:

#include "help.h"
random_device rand_dev;
ranlux48 rand_eng{ rand_dev() };
uniform_real_distribution<> zero2one_dist(0, 1);

in main.cpp file

#include "help.h"
//identical as help.cpp, just for illustration of the problem
random_device rand_dev1;
ranlux48 rand_eng1{ rand_dev1() };
uniform_real_distribution<> zero2one_dist1(0, 1);
//random number generation
float rnd1 = zero2one_dist1(rand_eng1);
float rnd2 = zero2one_dist(rand_eng);
//main function
int main()
{
    //another random number generation.
    float rnd3 = zero2one_dist(rand_eng);
    //output
    cout << rnd1 << endl << rnd2 << endl << rnd3 << endl;
    return 0;
}

The desired result is outputting three random number between 0 and 1, however, rnd1 and rnd3 generates proper result but rnd2 keeps 0?!

I'm totally confused here, what's the difference between definitions in external file help.h and in main.cpp and what's the difference between a call inside and outside main()?

In my real work, I would need to write the code in the form of rnd2, but now it won't work and I don't know why.

Can anyone illustrate the difference between rnd1, rnd2 and rnd3 and get rnd2 to work? Thanks!

Wjx
  • 103
  • 1
  • 5
  • There is no guarantee for the initialization order across translation units. `rnd2` may be initialized before or after `zero2one_dist`. Use form `rnd3` if you really want an external distribution object, or a helper function. – spectras Dec 04 '17 at 03:59
  • @spectras I wrote a help function in **help.cpp** file: `float rand_zero2one() { return zero2one_dist(rand_eng); }` however, calling this function like `rnd2=rand_zero2one()` won't work either... – Wjx Dec 04 '17 at 04:04
  • @Wjx same problem, since `rnd2` is a static in one translation unit it might get initialized before help.cpp has even executed its constructors. – Mark Ransom Dec 04 '17 at 05:31

1 Answers1

3

There is no guarantee for the initialization order across translation units. rnd2 may be initialized before or after zero2one_dist. Use form rnd3 if you really want an external distribution object, or a helper function, for instance:

float rand_zero2one()
{
   static random_device rand_dev;
   static ranlux48 rand_eng{ rand_dev() };
   static uniform_real_distribution<> zero2one_dist(0, 1);
   return zero2one_dist(rand_eng);
}

This guarantees the static variables are initialized before the function is first called. Put it in your help file and make that available instead of the extern variables.

Or even better, if your intent is to make it a singleton, do so. Put those into a singleton class and then use MySingletonGenerator::instance().nextValue().

spectras
  • 13,105
  • 2
  • 31
  • 53
  • Thanks~ but here's a small additional question: I would like to be able to call a same `rand_dev` and `rand_eng` in multiple functions, is that possible? – Wjx Dec 04 '17 at 07:55
  • Wow, thanks a lot! I've read something about singleton, it proved quite useful!(I'm a beginner in C++ as you can see......) Thanks for your help a lot! ~ – Wjx Dec 04 '17 at 09:22
  • @Wjx yes, your second comment actually answers your first comment: by making your generator a singleton class you can use it from multiple places. You're then free to put whatever methods you need in it. – spectras Dec 04 '17 at 11:37
  • 1
    @Wjx don't fall in love with singletons, there are lots of arguments against them. – Mark Ransom Dec 04 '17 at 22:50