An overloaded random number function template using modern C++

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
5
down vote

favorite
1












I don't mess around a lot with templates (or with GTK), but today I wrote something and would like to know how it can be improved, and hopefully clarify a few things in the process.



So, Randolfi() is a function template which returns a random number. A range can be specified as arguments.



It uses features new to C++11, based on these:



  • How to generate a random number in C++?


  • Random number generation in C++11 , how to generate , how do they work?


(The name "Randolfi" is a joke made by a few colleagues regarding my last name, Ranolfi.)



randolfi.hpp



#ifndef RANDOLFI_HPP
#define RANDOLFI_HPP

#include <random>

template <typename T>
T Randolfi()

std::mt19937 rng;
rng.seed( std::random_device()() );
std::uniform_int_distribution<T> dist;
return dist(rng);


template <typename T>
T Randolfi(T range_start, T range_end)

std::mt19937 rng;
rng.seed( std::random_device()() );
std::uniform_int_distribution<T> dist(range_start, range_end);
return dist(rng);


#endif //RANDOLFI_HPP


randolfi-test.cpp



#include <iostream>
#include <string>
#include <gtkmm.h>
#include "randolfi.hpp"

using namespace std;

int main()

auto app = Gtk::Application::create();

uint a_random_number = Randolfi<uint>();

Gtk::MessageDialog dialog( to_string(a_random_number) );
dialog.run();


return 0;




Notes:



  1. Somehow it doesn't feel right to have such a large proportion of redundant lines across the function overloads. I think there might be a template-related trick to help here, but searching for it proved useless since all the results I found (like this one) are concerned about type parameter overloading, which is not my case.


  2. I though about declaring it as Randolfi(T range_start = NULL, T range_end = NULL) and then checking if (range_start != NULL && range_end != NULL), but that does not save me any lines, and also appears to be frowned upon.



  3. As it is, the function can be called without type argument for the second overload, but not for the first one:



    Randolfi(0, 100); // type parameter ommited for template
    Randolfi(); // Fails - compiler has no clue on know how to instantiate template


    I have a feeling this inconsistency is bad practice.



  4. I'm mostly concerned about the template header, but I did include my testing code (randolfi-test.cpp) to this question so maybe someone can let me know if I did something terribly wrong. From my part, I'd like to avoid calling Gtk::Application::create() but that doesn't seem to be possible.


Thanks!







share|improve this question





















  • There are numerous reviews of such code. See for instance: codereview.stackexchange.com/questions/190552/…
    – papagaga
    Apr 13 at 8:01
















up vote
5
down vote

favorite
1












I don't mess around a lot with templates (or with GTK), but today I wrote something and would like to know how it can be improved, and hopefully clarify a few things in the process.



So, Randolfi() is a function template which returns a random number. A range can be specified as arguments.



It uses features new to C++11, based on these:



  • How to generate a random number in C++?


  • Random number generation in C++11 , how to generate , how do they work?


(The name "Randolfi" is a joke made by a few colleagues regarding my last name, Ranolfi.)



randolfi.hpp



#ifndef RANDOLFI_HPP
#define RANDOLFI_HPP

#include <random>

template <typename T>
T Randolfi()

std::mt19937 rng;
rng.seed( std::random_device()() );
std::uniform_int_distribution<T> dist;
return dist(rng);


template <typename T>
T Randolfi(T range_start, T range_end)

std::mt19937 rng;
rng.seed( std::random_device()() );
std::uniform_int_distribution<T> dist(range_start, range_end);
return dist(rng);


#endif //RANDOLFI_HPP


randolfi-test.cpp



#include <iostream>
#include <string>
#include <gtkmm.h>
#include "randolfi.hpp"

using namespace std;

int main()

auto app = Gtk::Application::create();

uint a_random_number = Randolfi<uint>();

Gtk::MessageDialog dialog( to_string(a_random_number) );
dialog.run();


return 0;




Notes:



  1. Somehow it doesn't feel right to have such a large proportion of redundant lines across the function overloads. I think there might be a template-related trick to help here, but searching for it proved useless since all the results I found (like this one) are concerned about type parameter overloading, which is not my case.


  2. I though about declaring it as Randolfi(T range_start = NULL, T range_end = NULL) and then checking if (range_start != NULL && range_end != NULL), but that does not save me any lines, and also appears to be frowned upon.



  3. As it is, the function can be called without type argument for the second overload, but not for the first one:



    Randolfi(0, 100); // type parameter ommited for template
    Randolfi(); // Fails - compiler has no clue on know how to instantiate template


    I have a feeling this inconsistency is bad practice.



  4. I'm mostly concerned about the template header, but I did include my testing code (randolfi-test.cpp) to this question so maybe someone can let me know if I did something terribly wrong. From my part, I'd like to avoid calling Gtk::Application::create() but that doesn't seem to be possible.


Thanks!







share|improve this question





















  • There are numerous reviews of such code. See for instance: codereview.stackexchange.com/questions/190552/…
    – papagaga
    Apr 13 at 8:01












up vote
5
down vote

favorite
1









up vote
5
down vote

favorite
1






1





I don't mess around a lot with templates (or with GTK), but today I wrote something and would like to know how it can be improved, and hopefully clarify a few things in the process.



So, Randolfi() is a function template which returns a random number. A range can be specified as arguments.



It uses features new to C++11, based on these:



  • How to generate a random number in C++?


  • Random number generation in C++11 , how to generate , how do they work?


(The name "Randolfi" is a joke made by a few colleagues regarding my last name, Ranolfi.)



randolfi.hpp



#ifndef RANDOLFI_HPP
#define RANDOLFI_HPP

#include <random>

template <typename T>
T Randolfi()

std::mt19937 rng;
rng.seed( std::random_device()() );
std::uniform_int_distribution<T> dist;
return dist(rng);


template <typename T>
T Randolfi(T range_start, T range_end)

std::mt19937 rng;
rng.seed( std::random_device()() );
std::uniform_int_distribution<T> dist(range_start, range_end);
return dist(rng);


#endif //RANDOLFI_HPP


randolfi-test.cpp



#include <iostream>
#include <string>
#include <gtkmm.h>
#include "randolfi.hpp"

using namespace std;

int main()

auto app = Gtk::Application::create();

uint a_random_number = Randolfi<uint>();

Gtk::MessageDialog dialog( to_string(a_random_number) );
dialog.run();


return 0;




Notes:



  1. Somehow it doesn't feel right to have such a large proportion of redundant lines across the function overloads. I think there might be a template-related trick to help here, but searching for it proved useless since all the results I found (like this one) are concerned about type parameter overloading, which is not my case.


  2. I though about declaring it as Randolfi(T range_start = NULL, T range_end = NULL) and then checking if (range_start != NULL && range_end != NULL), but that does not save me any lines, and also appears to be frowned upon.



  3. As it is, the function can be called without type argument for the second overload, but not for the first one:



    Randolfi(0, 100); // type parameter ommited for template
    Randolfi(); // Fails - compiler has no clue on know how to instantiate template


    I have a feeling this inconsistency is bad practice.



  4. I'm mostly concerned about the template header, but I did include my testing code (randolfi-test.cpp) to this question so maybe someone can let me know if I did something terribly wrong. From my part, I'd like to avoid calling Gtk::Application::create() but that doesn't seem to be possible.


Thanks!







share|improve this question













I don't mess around a lot with templates (or with GTK), but today I wrote something and would like to know how it can be improved, and hopefully clarify a few things in the process.



So, Randolfi() is a function template which returns a random number. A range can be specified as arguments.



It uses features new to C++11, based on these:



  • How to generate a random number in C++?


  • Random number generation in C++11 , how to generate , how do they work?


(The name "Randolfi" is a joke made by a few colleagues regarding my last name, Ranolfi.)



randolfi.hpp



#ifndef RANDOLFI_HPP
#define RANDOLFI_HPP

#include <random>

template <typename T>
T Randolfi()

std::mt19937 rng;
rng.seed( std::random_device()() );
std::uniform_int_distribution<T> dist;
return dist(rng);


template <typename T>
T Randolfi(T range_start, T range_end)

std::mt19937 rng;
rng.seed( std::random_device()() );
std::uniform_int_distribution<T> dist(range_start, range_end);
return dist(rng);


#endif //RANDOLFI_HPP


randolfi-test.cpp



#include <iostream>
#include <string>
#include <gtkmm.h>
#include "randolfi.hpp"

using namespace std;

int main()

auto app = Gtk::Application::create();

uint a_random_number = Randolfi<uint>();

Gtk::MessageDialog dialog( to_string(a_random_number) );
dialog.run();


return 0;




Notes:



  1. Somehow it doesn't feel right to have such a large proportion of redundant lines across the function overloads. I think there might be a template-related trick to help here, but searching for it proved useless since all the results I found (like this one) are concerned about type parameter overloading, which is not my case.


  2. I though about declaring it as Randolfi(T range_start = NULL, T range_end = NULL) and then checking if (range_start != NULL && range_end != NULL), but that does not save me any lines, and also appears to be frowned upon.



  3. As it is, the function can be called without type argument for the second overload, but not for the first one:



    Randolfi(0, 100); // type parameter ommited for template
    Randolfi(); // Fails - compiler has no clue on know how to instantiate template


    I have a feeling this inconsistency is bad practice.



  4. I'm mostly concerned about the template header, but I did include my testing code (randolfi-test.cpp) to this question so maybe someone can let me know if I did something terribly wrong. From my part, I'd like to avoid calling Gtk::Application::create() but that doesn't seem to be possible.


Thanks!









share|improve this question












share|improve this question




share|improve this question








edited Apr 13 at 3:28
























asked Apr 13 at 3:22









Marc.2377

1326




1326











  • There are numerous reviews of such code. See for instance: codereview.stackexchange.com/questions/190552/…
    – papagaga
    Apr 13 at 8:01
















  • There are numerous reviews of such code. See for instance: codereview.stackexchange.com/questions/190552/…
    – papagaga
    Apr 13 at 8:01















There are numerous reviews of such code. See for instance: codereview.stackexchange.com/questions/190552/…
– papagaga
Apr 13 at 8:01




There are numerous reviews of such code. See for instance: codereview.stackexchange.com/questions/190552/…
– papagaga
Apr 13 at 8:01










2 Answers
2






active

oldest

votes

















up vote
4
down vote



accepted










The constructor of uniform_int_distribution has default arguments:



template<typename T>
explicit uniform_int_distribution(T a = 0,
T b = std::numeric_limits<T>::max());


(I don't know why a defaults to 0 rather than to the minimum T, but there you go).



You can reduce your duplication by using the same default arguments, and passing them through to this constructor:



template <typename T>
T Randolfi(T range_start = 0,
T range_end = std::numeric_limits<T>::max())

std::uniform_int_distribution<T> dist(range_start, range_end);
return dist(rng);



Or, we can use perfect forwarding to create the distribution with the same arguments as passed in:



template <typename T, typename... Args>
T Randolfi(Args&&... args)

std::uniform_int_distribution<T> dist(std::forward<Args>(args)...);
return dist(rng);



We'll need to do this if we want to support a variety of distributions, so it's probably the way to go.




Don't recreate a random generator every call. You could make it static, which will reduce that to once per instantiation, but it's better to keep it outside of the function, so you only create and seed it once per process.



In the test program, you should exercise more than one type T, and test with implicit and explicit range. Also, avoid using namespace std;, and prefer tests which don't require a GUI environment (so they can be more easily run on build servers).




Modified code



#include <limits>
#include <random>
#include <utility>

static auto rng =
std::mt19937 rng;
rng.seed(std::random_device()());
return rng;
();

template <typename T,
template<typename> typename Distribution
= std::uniform_int_distribution,
typename... Args>
T Randolfi(Args&&... args)

Distribution<T> dist(std::forward<Args>(args)...);
return dist(rng);


#include <iostream>

int main()

std::clog << Randolfi<int>(-100, +100) << 'n'
<< Randolfi<char>('a', 'z') << 'n'
<< Randolfi<double, std::normal_distribution>(100, 15) << 'n'
<< Randolfi<std::size_t>() << std::endl;






share|improve this answer























  • I'm not sure of what you meant with "We'll need to [ use perfect forwarding ] if we want to support a variety of distributions". As is, perfect forwarding integers seems curious.
    – papagaga
    Apr 13 at 11:40










  • your code won't compile btw. Randolfi signatures overlap (ambiguous).
    – papagaga
    Apr 13 at 11:41










  • Thanks @papagaga - copy/paste error - I've now removed the accidental extra copy of the uniform_int_distribution version.
    – Toby Speight
    Apr 13 at 12:34










  • As to your comment - not all distributions take the same number of arguments. For example, std::poisson_distribution accepts only one argument, so couldn't be called with 0 and max in the same way. Forwarding a parameter pack is the only way to support the variable number of arguments required. The perfect part of it is probably now a habit - although I got it wrong (writing (Args...) instead of (Args&&...)), and I got no compiler warning for using std::forward with an argument not of universal-reference type - gcc wishlist!
    – Toby Speight
    Apr 13 at 12:39











  • About the first idea, I had thought about that, but didn't feel too confortable with duplicating code from the standard library. But in retrospect, well, that's not really such a big problem, I think. About the second idea (perfect forwarding), that's awesome. Didn't know that. Good insight with your suggestion about not recreating the random generator on every function call. (continues)
    – Marc.2377
    May 23 at 4:36


















up vote
3
down vote













As it stands right now, every time you need a random number, you're creating a new instance of std::mt19937, then using std::random_device to seed this instance, then (finally) getting a number and returning it.



What you (nearly always) want to do is create one instance of std::mt19937, seed it once, and then re-use the same generator throughout the remainder of the program.



As it stands now, you'd be much better off just using std::random_device to generate the number (without using std::mt19937 at all):



template <typename T>
T Randolfi()

return std::random_device()();



...at which point, the code might as well not exist at all, because it's not adding anything to what std::random_device provides out of the box.






share|improve this answer





















  • About your last sentence, it was supposed to be a convenience function with type guarantee. At least that makes for an advantage, no?
    – Marc.2377
    May 23 at 4:45











Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191928%2fan-overloaded-random-number-function-template-using-modern-c%23new-answer', 'question_page');

);

Post as a guest






























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
4
down vote



accepted










The constructor of uniform_int_distribution has default arguments:



template<typename T>
explicit uniform_int_distribution(T a = 0,
T b = std::numeric_limits<T>::max());


(I don't know why a defaults to 0 rather than to the minimum T, but there you go).



You can reduce your duplication by using the same default arguments, and passing them through to this constructor:



template <typename T>
T Randolfi(T range_start = 0,
T range_end = std::numeric_limits<T>::max())

std::uniform_int_distribution<T> dist(range_start, range_end);
return dist(rng);



Or, we can use perfect forwarding to create the distribution with the same arguments as passed in:



template <typename T, typename... Args>
T Randolfi(Args&&... args)

std::uniform_int_distribution<T> dist(std::forward<Args>(args)...);
return dist(rng);



We'll need to do this if we want to support a variety of distributions, so it's probably the way to go.




Don't recreate a random generator every call. You could make it static, which will reduce that to once per instantiation, but it's better to keep it outside of the function, so you only create and seed it once per process.



In the test program, you should exercise more than one type T, and test with implicit and explicit range. Also, avoid using namespace std;, and prefer tests which don't require a GUI environment (so they can be more easily run on build servers).




Modified code



#include <limits>
#include <random>
#include <utility>

static auto rng =
std::mt19937 rng;
rng.seed(std::random_device()());
return rng;
();

template <typename T,
template<typename> typename Distribution
= std::uniform_int_distribution,
typename... Args>
T Randolfi(Args&&... args)

Distribution<T> dist(std::forward<Args>(args)...);
return dist(rng);


#include <iostream>

int main()

std::clog << Randolfi<int>(-100, +100) << 'n'
<< Randolfi<char>('a', 'z') << 'n'
<< Randolfi<double, std::normal_distribution>(100, 15) << 'n'
<< Randolfi<std::size_t>() << std::endl;






share|improve this answer























  • I'm not sure of what you meant with "We'll need to [ use perfect forwarding ] if we want to support a variety of distributions". As is, perfect forwarding integers seems curious.
    – papagaga
    Apr 13 at 11:40










  • your code won't compile btw. Randolfi signatures overlap (ambiguous).
    – papagaga
    Apr 13 at 11:41










  • Thanks @papagaga - copy/paste error - I've now removed the accidental extra copy of the uniform_int_distribution version.
    – Toby Speight
    Apr 13 at 12:34










  • As to your comment - not all distributions take the same number of arguments. For example, std::poisson_distribution accepts only one argument, so couldn't be called with 0 and max in the same way. Forwarding a parameter pack is the only way to support the variable number of arguments required. The perfect part of it is probably now a habit - although I got it wrong (writing (Args...) instead of (Args&&...)), and I got no compiler warning for using std::forward with an argument not of universal-reference type - gcc wishlist!
    – Toby Speight
    Apr 13 at 12:39











  • About the first idea, I had thought about that, but didn't feel too confortable with duplicating code from the standard library. But in retrospect, well, that's not really such a big problem, I think. About the second idea (perfect forwarding), that's awesome. Didn't know that. Good insight with your suggestion about not recreating the random generator on every function call. (continues)
    – Marc.2377
    May 23 at 4:36















up vote
4
down vote



accepted










The constructor of uniform_int_distribution has default arguments:



template<typename T>
explicit uniform_int_distribution(T a = 0,
T b = std::numeric_limits<T>::max());


(I don't know why a defaults to 0 rather than to the minimum T, but there you go).



You can reduce your duplication by using the same default arguments, and passing them through to this constructor:



template <typename T>
T Randolfi(T range_start = 0,
T range_end = std::numeric_limits<T>::max())

std::uniform_int_distribution<T> dist(range_start, range_end);
return dist(rng);



Or, we can use perfect forwarding to create the distribution with the same arguments as passed in:



template <typename T, typename... Args>
T Randolfi(Args&&... args)

std::uniform_int_distribution<T> dist(std::forward<Args>(args)...);
return dist(rng);



We'll need to do this if we want to support a variety of distributions, so it's probably the way to go.




Don't recreate a random generator every call. You could make it static, which will reduce that to once per instantiation, but it's better to keep it outside of the function, so you only create and seed it once per process.



In the test program, you should exercise more than one type T, and test with implicit and explicit range. Also, avoid using namespace std;, and prefer tests which don't require a GUI environment (so they can be more easily run on build servers).




Modified code



#include <limits>
#include <random>
#include <utility>

static auto rng =
std::mt19937 rng;
rng.seed(std::random_device()());
return rng;
();

template <typename T,
template<typename> typename Distribution
= std::uniform_int_distribution,
typename... Args>
T Randolfi(Args&&... args)

Distribution<T> dist(std::forward<Args>(args)...);
return dist(rng);


#include <iostream>

int main()

std::clog << Randolfi<int>(-100, +100) << 'n'
<< Randolfi<char>('a', 'z') << 'n'
<< Randolfi<double, std::normal_distribution>(100, 15) << 'n'
<< Randolfi<std::size_t>() << std::endl;






share|improve this answer























  • I'm not sure of what you meant with "We'll need to [ use perfect forwarding ] if we want to support a variety of distributions". As is, perfect forwarding integers seems curious.
    – papagaga
    Apr 13 at 11:40










  • your code won't compile btw. Randolfi signatures overlap (ambiguous).
    – papagaga
    Apr 13 at 11:41










  • Thanks @papagaga - copy/paste error - I've now removed the accidental extra copy of the uniform_int_distribution version.
    – Toby Speight
    Apr 13 at 12:34










  • As to your comment - not all distributions take the same number of arguments. For example, std::poisson_distribution accepts only one argument, so couldn't be called with 0 and max in the same way. Forwarding a parameter pack is the only way to support the variable number of arguments required. The perfect part of it is probably now a habit - although I got it wrong (writing (Args...) instead of (Args&&...)), and I got no compiler warning for using std::forward with an argument not of universal-reference type - gcc wishlist!
    – Toby Speight
    Apr 13 at 12:39











  • About the first idea, I had thought about that, but didn't feel too confortable with duplicating code from the standard library. But in retrospect, well, that's not really such a big problem, I think. About the second idea (perfect forwarding), that's awesome. Didn't know that. Good insight with your suggestion about not recreating the random generator on every function call. (continues)
    – Marc.2377
    May 23 at 4:36













up vote
4
down vote



accepted







up vote
4
down vote



accepted






The constructor of uniform_int_distribution has default arguments:



template<typename T>
explicit uniform_int_distribution(T a = 0,
T b = std::numeric_limits<T>::max());


(I don't know why a defaults to 0 rather than to the minimum T, but there you go).



You can reduce your duplication by using the same default arguments, and passing them through to this constructor:



template <typename T>
T Randolfi(T range_start = 0,
T range_end = std::numeric_limits<T>::max())

std::uniform_int_distribution<T> dist(range_start, range_end);
return dist(rng);



Or, we can use perfect forwarding to create the distribution with the same arguments as passed in:



template <typename T, typename... Args>
T Randolfi(Args&&... args)

std::uniform_int_distribution<T> dist(std::forward<Args>(args)...);
return dist(rng);



We'll need to do this if we want to support a variety of distributions, so it's probably the way to go.




Don't recreate a random generator every call. You could make it static, which will reduce that to once per instantiation, but it's better to keep it outside of the function, so you only create and seed it once per process.



In the test program, you should exercise more than one type T, and test with implicit and explicit range. Also, avoid using namespace std;, and prefer tests which don't require a GUI environment (so they can be more easily run on build servers).




Modified code



#include <limits>
#include <random>
#include <utility>

static auto rng =
std::mt19937 rng;
rng.seed(std::random_device()());
return rng;
();

template <typename T,
template<typename> typename Distribution
= std::uniform_int_distribution,
typename... Args>
T Randolfi(Args&&... args)

Distribution<T> dist(std::forward<Args>(args)...);
return dist(rng);


#include <iostream>

int main()

std::clog << Randolfi<int>(-100, +100) << 'n'
<< Randolfi<char>('a', 'z') << 'n'
<< Randolfi<double, std::normal_distribution>(100, 15) << 'n'
<< Randolfi<std::size_t>() << std::endl;






share|improve this answer















The constructor of uniform_int_distribution has default arguments:



template<typename T>
explicit uniform_int_distribution(T a = 0,
T b = std::numeric_limits<T>::max());


(I don't know why a defaults to 0 rather than to the minimum T, but there you go).



You can reduce your duplication by using the same default arguments, and passing them through to this constructor:



template <typename T>
T Randolfi(T range_start = 0,
T range_end = std::numeric_limits<T>::max())

std::uniform_int_distribution<T> dist(range_start, range_end);
return dist(rng);



Or, we can use perfect forwarding to create the distribution with the same arguments as passed in:



template <typename T, typename... Args>
T Randolfi(Args&&... args)

std::uniform_int_distribution<T> dist(std::forward<Args>(args)...);
return dist(rng);



We'll need to do this if we want to support a variety of distributions, so it's probably the way to go.




Don't recreate a random generator every call. You could make it static, which will reduce that to once per instantiation, but it's better to keep it outside of the function, so you only create and seed it once per process.



In the test program, you should exercise more than one type T, and test with implicit and explicit range. Also, avoid using namespace std;, and prefer tests which don't require a GUI environment (so they can be more easily run on build servers).




Modified code



#include <limits>
#include <random>
#include <utility>

static auto rng =
std::mt19937 rng;
rng.seed(std::random_device()());
return rng;
();

template <typename T,
template<typename> typename Distribution
= std::uniform_int_distribution,
typename... Args>
T Randolfi(Args&&... args)

Distribution<T> dist(std::forward<Args>(args)...);
return dist(rng);


#include <iostream>

int main()

std::clog << Randolfi<int>(-100, +100) << 'n'
<< Randolfi<char>('a', 'z') << 'n'
<< Randolfi<double, std::normal_distribution>(100, 15) << 'n'
<< Randolfi<std::size_t>() << std::endl;







share|improve this answer















share|improve this answer



share|improve this answer








edited Apr 13 at 12:40


























answered Apr 13 at 10:46









Toby Speight

17.5k13489




17.5k13489











  • I'm not sure of what you meant with "We'll need to [ use perfect forwarding ] if we want to support a variety of distributions". As is, perfect forwarding integers seems curious.
    – papagaga
    Apr 13 at 11:40










  • your code won't compile btw. Randolfi signatures overlap (ambiguous).
    – papagaga
    Apr 13 at 11:41










  • Thanks @papagaga - copy/paste error - I've now removed the accidental extra copy of the uniform_int_distribution version.
    – Toby Speight
    Apr 13 at 12:34










  • As to your comment - not all distributions take the same number of arguments. For example, std::poisson_distribution accepts only one argument, so couldn't be called with 0 and max in the same way. Forwarding a parameter pack is the only way to support the variable number of arguments required. The perfect part of it is probably now a habit - although I got it wrong (writing (Args...) instead of (Args&&...)), and I got no compiler warning for using std::forward with an argument not of universal-reference type - gcc wishlist!
    – Toby Speight
    Apr 13 at 12:39











  • About the first idea, I had thought about that, but didn't feel too confortable with duplicating code from the standard library. But in retrospect, well, that's not really such a big problem, I think. About the second idea (perfect forwarding), that's awesome. Didn't know that. Good insight with your suggestion about not recreating the random generator on every function call. (continues)
    – Marc.2377
    May 23 at 4:36

















  • I'm not sure of what you meant with "We'll need to [ use perfect forwarding ] if we want to support a variety of distributions". As is, perfect forwarding integers seems curious.
    – papagaga
    Apr 13 at 11:40










  • your code won't compile btw. Randolfi signatures overlap (ambiguous).
    – papagaga
    Apr 13 at 11:41










  • Thanks @papagaga - copy/paste error - I've now removed the accidental extra copy of the uniform_int_distribution version.
    – Toby Speight
    Apr 13 at 12:34










  • As to your comment - not all distributions take the same number of arguments. For example, std::poisson_distribution accepts only one argument, so couldn't be called with 0 and max in the same way. Forwarding a parameter pack is the only way to support the variable number of arguments required. The perfect part of it is probably now a habit - although I got it wrong (writing (Args...) instead of (Args&&...)), and I got no compiler warning for using std::forward with an argument not of universal-reference type - gcc wishlist!
    – Toby Speight
    Apr 13 at 12:39











  • About the first idea, I had thought about that, but didn't feel too confortable with duplicating code from the standard library. But in retrospect, well, that's not really such a big problem, I think. About the second idea (perfect forwarding), that's awesome. Didn't know that. Good insight with your suggestion about not recreating the random generator on every function call. (continues)
    – Marc.2377
    May 23 at 4:36
















I'm not sure of what you meant with "We'll need to [ use perfect forwarding ] if we want to support a variety of distributions". As is, perfect forwarding integers seems curious.
– papagaga
Apr 13 at 11:40




I'm not sure of what you meant with "We'll need to [ use perfect forwarding ] if we want to support a variety of distributions". As is, perfect forwarding integers seems curious.
– papagaga
Apr 13 at 11:40












your code won't compile btw. Randolfi signatures overlap (ambiguous).
– papagaga
Apr 13 at 11:41




your code won't compile btw. Randolfi signatures overlap (ambiguous).
– papagaga
Apr 13 at 11:41












Thanks @papagaga - copy/paste error - I've now removed the accidental extra copy of the uniform_int_distribution version.
– Toby Speight
Apr 13 at 12:34




Thanks @papagaga - copy/paste error - I've now removed the accidental extra copy of the uniform_int_distribution version.
– Toby Speight
Apr 13 at 12:34












As to your comment - not all distributions take the same number of arguments. For example, std::poisson_distribution accepts only one argument, so couldn't be called with 0 and max in the same way. Forwarding a parameter pack is the only way to support the variable number of arguments required. The perfect part of it is probably now a habit - although I got it wrong (writing (Args...) instead of (Args&&...)), and I got no compiler warning for using std::forward with an argument not of universal-reference type - gcc wishlist!
– Toby Speight
Apr 13 at 12:39





As to your comment - not all distributions take the same number of arguments. For example, std::poisson_distribution accepts only one argument, so couldn't be called with 0 and max in the same way. Forwarding a parameter pack is the only way to support the variable number of arguments required. The perfect part of it is probably now a habit - although I got it wrong (writing (Args...) instead of (Args&&...)), and I got no compiler warning for using std::forward with an argument not of universal-reference type - gcc wishlist!
– Toby Speight
Apr 13 at 12:39













About the first idea, I had thought about that, but didn't feel too confortable with duplicating code from the standard library. But in retrospect, well, that's not really such a big problem, I think. About the second idea (perfect forwarding), that's awesome. Didn't know that. Good insight with your suggestion about not recreating the random generator on every function call. (continues)
– Marc.2377
May 23 at 4:36





About the first idea, I had thought about that, but didn't feel too confortable with duplicating code from the standard library. But in retrospect, well, that's not really such a big problem, I think. About the second idea (perfect forwarding), that's awesome. Didn't know that. Good insight with your suggestion about not recreating the random generator on every function call. (continues)
– Marc.2377
May 23 at 4:36













up vote
3
down vote













As it stands right now, every time you need a random number, you're creating a new instance of std::mt19937, then using std::random_device to seed this instance, then (finally) getting a number and returning it.



What you (nearly always) want to do is create one instance of std::mt19937, seed it once, and then re-use the same generator throughout the remainder of the program.



As it stands now, you'd be much better off just using std::random_device to generate the number (without using std::mt19937 at all):



template <typename T>
T Randolfi()

return std::random_device()();



...at which point, the code might as well not exist at all, because it's not adding anything to what std::random_device provides out of the box.






share|improve this answer





















  • About your last sentence, it was supposed to be a convenience function with type guarantee. At least that makes for an advantage, no?
    – Marc.2377
    May 23 at 4:45















up vote
3
down vote













As it stands right now, every time you need a random number, you're creating a new instance of std::mt19937, then using std::random_device to seed this instance, then (finally) getting a number and returning it.



What you (nearly always) want to do is create one instance of std::mt19937, seed it once, and then re-use the same generator throughout the remainder of the program.



As it stands now, you'd be much better off just using std::random_device to generate the number (without using std::mt19937 at all):



template <typename T>
T Randolfi()

return std::random_device()();



...at which point, the code might as well not exist at all, because it's not adding anything to what std::random_device provides out of the box.






share|improve this answer





















  • About your last sentence, it was supposed to be a convenience function with type guarantee. At least that makes for an advantage, no?
    – Marc.2377
    May 23 at 4:45













up vote
3
down vote










up vote
3
down vote









As it stands right now, every time you need a random number, you're creating a new instance of std::mt19937, then using std::random_device to seed this instance, then (finally) getting a number and returning it.



What you (nearly always) want to do is create one instance of std::mt19937, seed it once, and then re-use the same generator throughout the remainder of the program.



As it stands now, you'd be much better off just using std::random_device to generate the number (without using std::mt19937 at all):



template <typename T>
T Randolfi()

return std::random_device()();



...at which point, the code might as well not exist at all, because it's not adding anything to what std::random_device provides out of the box.






share|improve this answer













As it stands right now, every time you need a random number, you're creating a new instance of std::mt19937, then using std::random_device to seed this instance, then (finally) getting a number and returning it.



What you (nearly always) want to do is create one instance of std::mt19937, seed it once, and then re-use the same generator throughout the remainder of the program.



As it stands now, you'd be much better off just using std::random_device to generate the number (without using std::mt19937 at all):



template <typename T>
T Randolfi()

return std::random_device()();



...at which point, the code might as well not exist at all, because it's not adding anything to what std::random_device provides out of the box.







share|improve this answer













share|improve this answer



share|improve this answer











answered Apr 13 at 3:44









Jerry Coffin

27.4k360123




27.4k360123











  • About your last sentence, it was supposed to be a convenience function with type guarantee. At least that makes for an advantage, no?
    – Marc.2377
    May 23 at 4:45

















  • About your last sentence, it was supposed to be a convenience function with type guarantee. At least that makes for an advantage, no?
    – Marc.2377
    May 23 at 4:45
















About your last sentence, it was supposed to be a convenience function with type guarantee. At least that makes for an advantage, no?
– Marc.2377
May 23 at 4:45





About your last sentence, it was supposed to be a convenience function with type guarantee. At least that makes for an advantage, no?
– Marc.2377
May 23 at 4:45













 

draft saved


draft discarded


























 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191928%2fan-overloaded-random-number-function-template-using-modern-c%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

Greedy Best First Search implementation in Rust

Function to Return a JSON Like Objects Using VBA Collections and Arrays

C++11 CLH Lock Implementation