In my original question: Generically encapsulating random generators and distributions into a class template I was given some excellent suggestions from two very informative users. I will give a basic outline of what each user had suggested by topic. I will finally demonstrate what changes I have made to the class.
User - bipll: https://codereview.stackexchange.com/users/159614/bipll
- Visibility
- Initialization
- Overloading
- Return Values
- Too Many Copies
User - papagaga: https://codereview.stackexchange.com/users/157553/papagaga
- Scoped Enumerations
- Universal References And Perfect Forwarding
- The Interface Is Perfectible
- Various
Refactored Class
#ifndef GENERATOR_H
#define GENERATOR_H
#include <limits>
#include <chrono>
#include <random>
#include <type_traits>
#include <functional>
enum class Seed { SeedValue, ChronoClock, RandomDevice, SeedSeq, UserDefined };
template<class Engine, class Type, template<typename> class Distribution>
class Generator {
public:
typedef class Distribution<Type> Distribution;
private:
Engine _engine;
Distribution _distribution;
Type _value;
public:
template<class SeedValue, class... Params>
explicit Generator( Engine&& engine, Seed seedType, SeedValue seedValue, Params&&... params ) : _engine( std::move( engine ) ) {
_distribution = Distribution( std::forward<Params>( params )... );
seed( seedType, seedValue );
}
Type generate() {
return _value = _distribution( _engine );
}
Distribution getDistribution() const {
return _distribution;
}
Type lastGeneratedValue() const {
return _value;
}
private:
template<class SeedValue = int>
void seed( Seed seedType, SeedValue value = 0 ) {
if( seedType == Seed::SeedValue )
_engine.seed( value );
}
// This overload has to choose between one or the other based on type passed
void seed( Seed seedType ) {
if( seedType == Seed::ChronoClock )
_engine.seed( getTimeNow() );
if( seedType == Seed::RandomDevice ) {
std::random_device rd{};
_engine.seed( rd() );
}
}
void seed( Seed seedType, std::initializer_list<std::size_t>&& list ) {
if( seedType == Seed::SeedSeq ) { // expects initializer_list<size_t>
std::seed_seq seq( std::move( list ) );
_engine.seed( seq );
}
}
// NOTE: Not sure if this is correct; or how one would call it from current constructor
template<class Ret, class... Args>
void seed( Seed seedType, Args&&... ) {
if( seedType == Seed::UserDefined ) { // can be any user defined method, lambda function<>, etc.
std::function<Ret( std::forward<Args>... )> func;
_engine.seed( func );
}
}
std::size_t getTimeNow() {
using Clock = std::conditional_t<std::chrono::high_resolution_clock::is_steady,
std::chrono::high_resolution_clock,
std::chrono::steady_clock>;
std::size_t now = static_cast<std::size_t>(Clock::now().time_since_epoch().count());
return now;
}
};
#endif // !GENERATOR_H
Simple Use
#include <iostream>
#include "Generator.h"
int main() {
std::mt19937 engine;
Generator<std::mt19937, int, std::uniform_int_distriubtion>
gen( std::move( engine ), Seed::ChronoClock, 0, 1, 100 );
for ( std::size_t i = 0; i < 100; i++ )
std::cout << gen.generate() << '\n';
std::cout << "\nPress any key and enter to quit.\n";
std::cin.get();
return 0;
}
Overview Of Changes Made From Each User's Suggestions
User - bipll:
Visibility:
I moved theusing Clock =...
out of the public scope of class declaration and moved it specifically to the function body of the membergetTimeNow()
which is now a private method instead of a public one. As for thetypedef
ofDistribution<Type>
I added a publictypedef
to the class.
Initialization:
I no longer have the construction of the engine type that is chosen by the user being constructed first then forcing them to have to explicitly call the seed function. The seed function is no longer public it is now private and is called by the class's constructor. I combined both construction and initialization into one process.
Overloading:
I took the advise of overloading which came from both users and also allowed for an alternative overload that would accept a user defined mechanism to seed the generators.
Return Values:
Before I had posted my original question and I had already though about having the generate function return the value, but I never incorporated that since I was only testing the class's functionality of proper construction and output. Now thegenerate()
method returns a value. As for my previous method that use to get the generated value, I renamed it to be more intuitivelastGeneratedValue()
.
Too many copies:
I have now incorporated the mechanics to use either perfect forwarding and or move semantics. I now move theengine
into the constructor. This also reflects on userpapagaga's
section ofUniversal References And Perfect Forwarding
User - papagaga:
Scoped Enumerations:
I changed myenum
to be aclass enum
and also changed the field's case fromSOME_FIELD
toSomeField
.
Universal References And Perfect Forwarding:
Same as above; Using References & Perfect Forwarding I now forward the variadic parameters into the constructor.
The Interface Is Perfectible:
This section is in reflection to the overload section seen from userbipll
. I chose to use function overloads, and function template overloads where needed. I however did not include anystatic asserts
.
Various:
This pertained to theswitch
statement's braces being superfluous. This should not be an issue any more since I'm no long using aswitch
in replace of function overloading.
Questions - Concerns
I would like to know from both users who have provided a solid, well detailed, and informative answer if the changes I've made reflect what was suggested. Is the code accurate or does it still need some improvement?
As for my next question; it deals with the seed()
overloaded function template
. It is supposed to allow a user to define their own function object
to be passed to the seed function in order to seed the generators or engines. This can be answered by anyone who feels that they have critical feedback. The function is untested, the code does compile and run without trying to use it. Does the syntax
for the function declaration look correct? If so, kind of a stack overflow question but, how would I integrate the needed parameter(s) for this function into the this class's constructor? This particular function overload has me held up.
Note - Concerning my original post: I am divided and can not choose between either of the two users mentioned in this post as to who to accept for an accepted answer; both were extremely helpful. I up voted both users answers, yet I'm still undetermined on who to accept.