[ver 1] @Galik's post effectively demonstrated the basic technique of creating a polymorphic hierarchy, which I explain below.
The post was intended as a demonstration (rather than an implementation) of the technique involved. As such, it doesn't compile:
http://coliru.stacked-crooked.com/a/0465c2a11d3a0558
I have corrected the underlying issues and the following version works [ver 2]:
http://coliru.stacked-crooked.com/a/9bb0f47251e6dfed
The technique involved is important. I have voted for @Galik's post.
However, there was 1 issue with @Galik's solution: The random_device() engine was hard-coded in the base class itself. As such, it was always used, regardless of which engine was passed as argument in the sub-class. In fact, the engine passed as argument to the sub-class should have been used as the source of random numbers.
I have corrected this and also changed some of the names in the following version [ver 3]:
http://coliru.stacked-crooked.com/a/350eadb55a4bafe7
#include <vector>
#include <memory> /// unique_ptr
#include <random>
#include <iostream>
/// Declarations ...
class RndBase; /// Random number base class
/// Generic Random number sub-class
/// takes advantage of the different Engines' similar interfaces
template<typename Eng>
class RndSub;
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args);
/// Implementation ...
/// Random number base class
class RndBase
{
public:
using dist_type = std::uniform_int_distribution<int>;
virtual ~RndBase() = default;
virtual int operator() (int min, int max) = 0;
protected:
dist_type dist;
/// factory method
template<typename Eng>
static Eng& eng()
{
static Eng eng {Eng {}};
return eng;
}
}; // RndBase
/// Generic Random number sub-class
/// takes advantage of the different Engines' similar interfaces
template<typename Eng>
class RndSub : public RndBase
{
public:
/// Generate a random number.
int operator() (int min, int max) override
{
return dist(eng<Eng>(), dist_type::param_type(min, max));
}
};
int main()
{
using Eminstd_rand = RndSub<std::minstd_rand>;
using Emt19937 = RndSub<std::mt19937>;
using Eranlux24 = RndSub<std::ranlux24>;
/// smart pointers because of polymorphism
using pRndBase = std::unique_ptr<RndBase>;
/// A vector of smart base pointers to typed sub-classes
std::vector<pRndBase> prndbases;
/// Add whatever generators you want
prndbases.push_back(make_unique<Eminstd_rand> ());
prndbases.push_back(make_unique<Emt19937> ());
prndbases.push_back(make_unique<Eranlux24> ());
/// random numbers between 10 and 50
for(auto const& prb : prndbases)
std::cout << (*prb) (10, 50) << std::endl;
}
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
return std::unique_ptr<T> {new T {args...}};
} // make_unique()
The explanation of the "Polymorphic Hierarchy" pattern is as follows:
1) We know all the engines have different types, though the same interface.
2) We create an abstract base class (RndBase), which includes this interface. It also defines a parameterized (static) factory method named eng() that creates an object of the parameter Eng and returns a reference to it. It is expected that an engine would be used as the parameter.
3) We create a parameterized sub-class named RndSub that derives from the base class RndBase. This class defines a call operator which returns the random number obtained by invoking the distribution.
4) Effectively, what we have done is as follows:
a) The heterogeneous engines are abstracted by the parameterized sub-class RndSub. Each sub-class is different.
b) However, they now have a single common base-class RndBase.
c) Since there is only a single base class (RndBase), we can now create a vector<RndBase>
, which is homogeneous. The sub-classes of RndBase are heterogeneous.
d) Since the interface is common, we can use the interface defined in the base class to invoke the implementation in the sub-class. This implementation invokes the factory method eng() in the base class to obtain an engine, which is passed as argument to the distribution. This returns the random number.
This solution is specifically for Random numbers. I am trying to make a solution for any classes (that have a similar interface).