3

I am writing a program that holds a list<T> where T is either int or double. I want to make a function, using components from the header <random>, that generates numbers of type T. I can't find a distribution that takes T as template argument.

The function is then going to be used in a call to the generate function:

generate(myList.begin(), myList.end(), RandomGenerator()) 

How do I go about doing this?

I have tried to create a class called RandomGenerator and overload the operator() for it. When i try to pass an argument to the overloaded operator() i get an error.

Function call, Random(b) is a function object with int argument b:

generate(rList.begin(), rList.end(), Random(b)); 

Code for overloading (), where n is sort of a "dummy"-variable for just telling what type it is:

int operator()(int n) { std::uniform_int_distribution<int> dist(1000, 2000); return dist(gen); } 

Error message:

Error 2 error C2440: '<function-style-cast>' : cannot convert from 'int' to 'Random' Error 3 error C2780: 'void std::generate(_FwdIt,_FwdIt,_Fn0)' : expects 3 arguments - 2 provided 
5
  • 1
    What did you try? (Hint: you need overload the function) Commented Oct 6, 2014 at 14:01
  • I think you may find a combination of std::conditional and the type-class tests such as std::is_floating_point and std::is_integral useful in this endeavor. Commented Oct 6, 2014 at 14:36
  • You claim to be calling generate with three arguments - but the compiler says you only passed two. This strongly suggests the code you have shown is not the code you are actually compiling. Show the exact line that the error message refers to. Commented Oct 6, 2014 at 14:39
  • That is the exakt line, it point to line number 111 and that is it. Commented Oct 6, 2014 at 14:45
  • Added another error message that was unintendedly missed in earlier edits. Commented Oct 6, 2014 at 14:46

3 Answers 3

3

In addition to Joey's answer, the following should play nicely with std::generate. Disclaimer: I'm terrible at metaprogramming, please do point out any mistakes or possible improvements. This is a C++11 solution, with C++14 it would be a bit shorter.

Live demo on Coliru.

#include <algorithm> #include <iostream> #include <list> #include <random> #include <type_traits> template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value>::type> class random_generator { using distribution_type = typename std::conditional< std::is_integral<T>::value, std::uniform_int_distribution<T>, std::uniform_real_distribution<T> >::type; std::default_random_engine engine; distribution_type distribution; public: auto operator()() -> decltype(distribution(engine)) { return distribution(engine); } }; template<typename Container, typename T = typename Container::value_type> auto make_generator(Container const&) -> decltype(random_generator<T>()) { return random_generator<T>(); } int main() { auto l = std::list<double>(10); std::generate(std::begin(l), std::end(l), make_generator(l)); for (auto i : l) { std::cout << i << " "; } } 
Sign up to request clarification or add additional context in comments.

1 Comment

I like this approach more than mine. Given a vote up! :)
3

Not an elegant solution but does what you want: (Modifying the code so that it can be used with generate)

template <typename Distribution> class RandomGenerator { using Type = typename Distribution::result_type; const Type getRandom(const Type& lower, const Type& upper) const { static std::default_random_engine generator; static Distribution distribution(lower, upper); return distribution(generator); } public: RandomGenerator(const Type& lowerBound, const Type& upperBound) : _lowerBound(lowerBound), _upperBound(upperBound) { } const Type operator()() const { return getRandom(_lowerBound, _upperBound); } private: Type _lowerBound; Type _upperBound; }; int main() { RandomGenerator<std::uniform_int_distribution<>> randomIntGenerator(1, 10); RandomGenerator<std::uniform_real_distribution<>> randomDoubleGenerator(0.0, 10.0); std::deque<int> intRandoms(10); std::generate(intRandoms.begin(), intRandoms.end(), randomIntGenerator); std::deque<double> doubleRandoms(5); std::generate(doubleRandoms.begin(), doubleRandoms.end(), randomDoubleGenerator); return 0; } 

Of course if we want different numbers for each run we can set the seed for default_random_engine to something like time(0) but I'll leave that one out.

5 Comments

You should just use typename Distribution::result_type.
Of course you're absolutely right; we can eliminate the extra typename Type parameter and introduce a typedef as: typedef typename Distribution::result_type Type;
If I may suggest the using syntax instead of typedef for a more C++11-ish experience. Also, both method could be made const.
Thanks for pointint it out. I completely forgot about 'using'. Code updated.
I have no possibility to try it out for a couple of hours and im courious, does this work with function generate?
0

Here's my attempt (warning: I only just wrote it). It supports bool and integral types, but not double (though that would be easy to add). It uses type deduction on the return type.

#include <iostream> #include <random> #include <cstdint> template<typename PRNG> class Random { public: Random(PRNG& prng) : m_prng(prng) {} operator bool() const { return (m_prng() & 1) != 0; } template< typename T, typename = typename std::enable_if_t<std::is_integral_v<T>> > operator T() const { return std::uniform_int_distribution<T>()(m_prng); } private: PRNG& m_prng; }; int main() { for (unsigned i = 0; i < 100; ++i) { std::mt19937_64 prng(i); bool x = Random(prng); uint8_t y = Random(prng); uint32_t z = Random(prng); std::cout << x << " " << int(y) << " " << z << std::endl; } } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.