问题
I'm trying to provide an interface — through a config file — for my users to choose a distribution for some of the parameters that they are using. I would like to use STL random number generator algorithms for this purpose.
Let's assume that my program reads a JSON from a command line. For the JSON provided below, the program needs to realize that it should generate a random number from the normal distribution with given mean and standard variation. (I'm using the same parameter names as STL library for clearance.)
{
"dist": "normal_distribution",
"mean": 0.1,
"stddev": 0.5
}
So far, I can parse the JSON easily, and use each distribution's param_type
to initialize the distribution. I use the name to decide which distribution to decide the param_type
and the distribution.
What I don't know is how to implement this nicely. I know that I should provide some sort of factory method for this, pass the JSON, and spit out a function or a class. If I want to return an instance of a class, let's say a unique_ptr
of the generator, I need to define an abstract class, e.g., RandDist
and write some sort of adaptor to incorporate my input, .... I generally don't need a lot from the class, just a gen()
method should be enough.
I'm wondering if anyone have thoughts on this. Or, if anyone knows a library that can do this.
P.S. Input doesn't have to be a JSON object, any hash table would work per se.
回答1:
I've tried to keep the boilerplate to a bare minimum. Assumptions:
You know the type of your generator in advance (that's easily switchable if you need your generators to be dynamic as well)
All of the distributions generate
double
s (that's pretty much baked in since the API has to return something concrete to be decently usable)All of the distributions are constructible from
double
parameters (that's tweakable as well with a proxy object, but depending on your actual JSON library the work may already be done there)I've used a GCC preprocessor extension to handle the zero-parameter case, but the macro can certainly be rewritten to not need it.
using Generator = std::mt19937;
using Distribution = std::function<double(Generator &)>;
using Json = std::map<std::string, std::string>;
template <class DistributionType, class... Parameters>
Distribution make_distribution_impl(Json const &json, Parameters... parameters) {
return DistributionType{std::stod(json.at(parameters))...};
}
Distribution make_distribution(Json const &json) {
auto const &distributionName = json.at("dist");
#define generate_distribution_factory(name_, ...) \
if(distributionName == #name_) \
return make_distribution_impl<std::name_<double>>(json, ## __VA_ARGS__)
generate_distribution_factory(uniform_real_distribution, "a", "b");
generate_distribution_factory(normal_distribution, "mean", "stddev");
// ...
#undef generate_distribution_factory
throw std::runtime_error{"Unknown distribution " + distributionName};
}
See it live on Wandbox
回答2:
You described a pretty standard way to deal with this situation - to have abstract RandomGenerator
class with just one virtual method gen()
.
Then, it will have implementations like NormalDistributionGenerator
, UniformDistributionGenerator
etc. with constructors accepting appropriate distribution parameter set and initializing STL stuff as members.
These concrete classes will be used directly only in generator creation routine, and used in other places as abstract RandomGenerator
.
So creation routine will look something like this
std::unique_ptr<RandomGenerator> CreateRandomGenerator(const Info& info) {
switch (info.type) {
case Type::Normal:
return std::make_unique<NormalDistributionGenerator>(info.mean(), info.stddev());
case Type::Uniform:
return std::make_unique<UniformDistributionGenerator>(info.a(), info.b());
// ...
}
}
Info
- is a class which holds distribution info (some JSON wrapper, of map/hash_table - whatever works best in your case).
So you will definitely need to write some boilerplate code to make it work, but it will make a usage of your RandomGenerator
simple and clear, and adding new types of distributions will be easy enough and require code modification in single place only - factory method.
来源:https://stackoverflow.com/questions/56359951/writing-a-factory-method-for-stl-random-number-generators