0

Story: Using rand() function in C++, I can get random numbers, and using % I can set the range. However, If I also want to add bias, then I also have to add bias to the result. This is too much work to do, so I decided to write my own function to handle this. However I am stuck at one point.

I know that, I have to feed a new sequence(srand(time(NULL))) each time the program runs, otherwise I will get same numbers all the time.

The obvious way to do this is, to insert srand(time(NULL)) in the main() function. However, I don't want to do that, I want somehow it gets done automatically when I include my .h file.

Suppose myFunctions.h:

#include <iostream>
#include <string>
#include <vector>
#include <cstdio>
#include <ctime>

int randint(int start, int end);

and myFunctions.cpp:

#include "myFunctions.h"

/* [start,end) */
int randint(int start, int end)
{

    return (rand() % (end - start)) + start;

}

Now, I am confused where I should add srand(). If I do it in randint() definition, I assume, because of the fact that time difference will be too low, time(NULL) will evaluate the same value for each step of the loop and it will feed the same seed all the time when I want to get random numbers in a very short time, like:

for(int i = 0; i < 50; i++){
    std::cout << randint(0, 3) << std::endl;
}

The output is the same number, 50 times. So it confirms my suspicion.

I have tried something like this in my randint() definition,

int randint(int start, int end)
{

    #ifndef SEED
        srand(time(NULL));
        #define SEED
    #endif 

    return (rand() % (end - start)) + start;

}

However, it did not work too, because, I assume, the #ifndef only executes once at preprocessing stage.

So, after these attemps, I tried to call srand() right at the beginning of my .h file, but I came across with the fact that, actually you cannot call functions outside any function(main() for example.)

In short, I am stuck right now, any help will be appreciated.

Community
  • 1
  • 1
mya
  • 11
  • 4

2 Answers2

1

If I were you, I would implement a class to wrap all the functions in. You could call it randomGen or something like that and then include all the functions you want to use inside of that class. Then include a constructor that calls srand(). Then you can create a randomGen object in main and then you can go ahead and generate your random numbers.

  • If calling the constructor in the main was desirable, then I would simply put everything in a config() function and would call it rather than creating a class. Thanks for the answer, though – mya Apr 11 '20 at 10:59
  • I don't think there are any other ways to achieve this, you may be able to do something with pre-processor commands, but the solution would be messy. Thanks! – Ender_The_Xenocide Apr 11 '20 at 11:01
0

Use the call_once function available from C++11 onward. This requires the use of <mutex>. You must create a once_flag helper object. f must be static so it persists through function calls. This example uses a lambda, but you could just as easily create a regular old function above this called seedrand() or whatever and pass that in as a parameter.

Example lifted from https://en.cppreference.com/w/cpp/thread/call_once and modified to fit your program.

#include <iostream>
#include <mutex>

int randint(int start, int end)
{
    static std::once_flag f;
    std::call_once(f, []() {srand(time(NULL)); std::cout << "Random Seeded\n"; });

    return (rand() % (end - start)) + start;
}

int main(int argc, char** argv)
{
    for (int i = 0; i < 10; i++)
    {
        std::cout << i << ": " << randint(1, 100) << "\n";
    }
}

If you want to pass srand directly as the function, you have to pass its arguments as additional parameters. Note the lack of () after srand. You don't want to pass the RESULT of srand to call_once, you want to pass the function itself. call_once will call it for you.

static std::once_flag f;
std::call_once(f, srand, time(NULL)); // notice the lack of () after srand

Edit: std::call_once is overkill

The fact that you need to compile this with pthread is ridiculous. std::call_once should only be used to do something like this if you have multiple threads calling the same piece of code and only want it to execute once at all.

The much simpler solution would be to just use a static boolean:

int randint(int start, int end)
{
    static bool seeded = false;
    if (!seeded)
    {
        srand(time(NULL));
        seeded = true;
    }

    return (rand() % (end - start)) + start;
}
JohnFilleau
  • 4,045
  • 1
  • 15
  • 22
  • excellent answer! thanks a lot. However, I could not understand one part, why I cannot do directly `std::call_once(f, srand(time(NULL)))` ? I actually don't know what that `[]() {...}` is, what should I google it as? – mya Apr 11 '20 at 15:17
  • Here the problem message: `no instance of function template "std::call_once" matches the argument list -- argument types are: (std::once_flag, void)`. I guess it does not like the `void` return type of `srand()`. But I cannot find what type of a return does it want – mya Apr 11 '20 at 15:27
  • That is called a "lambda". It's an in-place function. – JohnFilleau Apr 11 '20 at 15:36
  • If you want to invoke `srand`, or any other function that takes arguments, you need to pass those arguments as additional paramters to the `call_once` function. I edited my answer with this approach. – JohnFilleau Apr 11 '20 at 15:42
  • Hey, since I asked you that, I have been dealing with it and as you just said, I figured out that I have to use it as you wrote in your answer. Thanks again btw. However, did you try the code with that formation? Because when I try it, it throws an error saying: `terminate called after throwing an instance of 'std::system_error' what(): Unknown error -1 Aborted (core dumped)`. If you tried, can you please tell me whether it works or not in your case? – mya Apr 11 '20 at 16:01
  • Yes I tested before posting. This compiles in both debug and release mode, and runs with no error in Visual Studio. – JohnFilleau Apr 11 '20 at 16:04
  • I made a simpler example and tested using `std::call_once` with a basic function which just prints something to cout. However, same error appeared. I assume the problem is resuting from something different. Maybe I should ask it as another question. Thanks for your helps, if I ask a new one, I will also mention it here just in case you might want to look at it. – mya Apr 11 '20 at 16:11
  • I look forward to it. – JohnFilleau Apr 11 '20 at 16:11
  • Actually, someone already did it before me... https://stackoverflow.com/a/51585101/13146555 I was using Visual Studio **Code** and compiling via terminal by myself. I assume it worked with you because Visual Studio does that thing automatically. You might want to add this to your answer so that future comers will not have that hard time as me. – mya Apr 11 '20 at 16:14
  • I never would have guessed that. Glad the answer was searchable. – JohnFilleau Apr 11 '20 at 16:19
  • The use of pthreads for this is ridiculous. I didn't realize that `call_once` invoked the threading library (although the fact it was in `` should have tipped me off). Use the `static bool` option I edited in. – JohnFilleau Apr 11 '20 at 16:37
  • Yes, it was not looking worth doing it. The new solution is way cooler, I should've guessed something like this when I saw your usage of `static` keyword in the first place. – mya Apr 11 '20 at 16:40