Search code examples
c++templatesmersenne-twister

Is it possible to declare a variable of unknown type as a class member variable?


So I've been interested by the mersenne_twister engine and what it can do, so I decided to put the few lines of code required to initialize it inside my own class so that i simply have to create an instance of that class and can get any random numbers in any range i want without having to repeat those lines every time I need it.

I have suceeded so far but because I want my code to be as portble and efficient as possible I want to use the 64-bit engine depending on the architecture present. I would like to avoid the way of using preprocessor macros defined by the compiler as that doesn't seem like the cleanest approach to me and would also require me to use the macros every time i mention the engine in my code.

My macro for the architecture l looks like this:

#define CPU_ARCH sizeof(nullptr)*8

And I declare the engine in the private space of the class so that i can init it in the constructor like this:

engine = mt19937(seed);

and use it in my random function like this:

double Random::giveRnd() {
    return distribution(engine);
}

This looks fine right now but I have yet to find a way to implement both architectures with the same name "engine" in a way that the engine to be used is chosen at startup.

I have attempted the following:

  • Using a template to create a variable named engine that later gets
    assigned either mt19337 or mt19337_64 which results in the compiler
    complaining that

    error: data member 'engine' cannot be a member template

with the following implementation:

class Random {
      public:
      [...]

      private:
      template<typename T>
      T engine;

      [...]
};
  • Using boost::variant which requires me to tell
    my giveRnd() function which type to use when I use the engine which is not possible since the type is not known at compile time
  • Not declaring the engine in the header file at all although this results in the giveRnd() function not being able to use the engine because it is not in the same scope.
  • Using preprocessor macros in the header file and then use typeid in
    the source code to find out which engine was used, which doesn't seem to be possible like this:

    if(CPU_ARCH==32) { engine = mt19337(seed) }

    because the compiler doesn't know that the engine will always be
    32-bit in this case and complains that I cannot use the '=' operator on two different types.

Does anyone have an idea on how to make this possible in a atleast somewhat clean way? Or do I need to fall back on the preprocessor macros?


Solution

  • You can implement behaviour that depends on CPU_BITS by making a class template that takes CPU_BITS as a template argument, and is specialized for expected values. For example:

    #include <random>
    
    template<size_t N> struct CpuOpts;
    template<> struct CpuOpts<32> { using EngineType = std::mt19937; };
    template<> struct CpuOpts<64> { using EngineType = std::mt19937_64; };
    
    enum { CPU_BITS = sizeof(nullptr)*8 };
    using CurrentCpuOpts = CpuOpts<CPU_BITS>;
    
    struct Random
    {
        CurrentCpuOpts::EngineType engine;
    };
    
    int main()
    {
        Random r;
        r.engine.seed(123456);
    }