0

I just wanted to create a basic RNG class using mersenne twister that can return random numbers of predefined ranges. I came up with the following code:

rng.h

#pragma once
#include <random>

class RNG
{
public:
    static float getRand01();
    static float getRand11();
    static int getRand(const int min, const int max);

private:
    static std::random_device rd;
    static std::mt19937 generator;

    static std::uniform_real_distribution<float> dis01;
    static std::uniform_real_distribution<float> dis11;
    static std::uniform_int_distribution<int> disAB;
};

rng.cpp

#include "RNG.h"

std::random_device RNG::rd{};
std::mt19937 RNG::generator{ rd() };
std::uniform_real_distribution<float> RNG::dis01{0.0f, 1.0f};
std::uniform_real_distribution<float> RNG::dis11{-1.0f, 1.0f};
std::uniform_int_distribution<int> RNG::disAB{1, 1};

float RNG::getRand01()
{
    return dis01(generator);
}

float RNG::getRand11()
{
    return dis11(generator);
}

int RNG::getRand(const int min, const int max)
{
    disAB.param(std::uniform_int_distribution<int>::param_type(min, max));
    return disAB(generator);
}

My main-file looks similar to this

Object someObject;
int main (){ ... }

So someObject is declared in global scope and it makes use of the RNG in its constructor. However the RNG always returns 0 during this time. I think this is because the member are not initialized as they cannot be constant initialised (not sure about the correct terminology here). This is not a big problem, because I can reset the state of someObject in main(). However I am interested if there is a workaround to this while still being able to use the RNG like a "static" class or is this design simply flawed?

Neran
  • 55
  • 7
  • I think you should take a look at [Static Initialization Order](https://stackoverflow.com/q/1005685/631266). You might possibly consider making RNG a singleton rather than all static members. Also maybe checkout "Meyer's Singleton. – Avi Berger Aug 05 '22 at 20:19

1 Answers1

1

The problem you are facing is caused by the fact that the order of initialization of static objects is undefined across translation units and in your case Object is trying to initialize before the static members of RND causing an error because Object's constructor uses RND.

You have a few options:

  1. Make the random number generator class's member variables not be static.
  2. Make the random number generator class's member variables be static but initialize on first use.
  3. Wrap the random number generator in a singleton in which the actual instance of the object is created on first use.
jwezorek
  • 8,592
  • 1
  • 29
  • 46
  • So using approach 1. also means that the functions have to be non-static? Objects then need to have a reference to a RNG object resulting in having more than a central mt. Also whats the difference between 2. and 3. ? For 2. I could use some sort of wrapper that takes a pointer to an initialisation function which creates and returns a mt19937. In getRand i will then use something similar to dis(generator.get()) which checks whether it is initialised and if not it inits itself with the provided init-function. This however looks like a singleton to me. – Neran Aug 05 '22 at 21:36
  • for 2. you could just have a static boolean, has_been_initialized, or whatever that is initiially false then in each of the RNG generation calls before polling for a random number, you call an "initialize" member function that tests the boolean and does nothing if it is true but if it is false it sets all the distribution etc. variables and changes the boolean to true. For 3. you make another class, RandomNumberGeneratorManager (or whatever) that holds a pointer to an intance of RNG that is initially a nullptr but on first use you allocate an RNG. – jwezorek Aug 06 '22 at 12:14
  • Personally I'd either go with 1. or 3. Either make it not use static calls and have to pass a reference to it around, or make it a singleton . I mean, by having it use static calls and have its own state, it pretty much *is* a singleton the way you have it. – jwezorek Aug 06 '22 at 12:18