Writing a Factory method for STL random number generators

狂风中的少年 提交于 2020-01-02 16:54:34

问题


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 doubles (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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!