为STL随机数生成器编写Factory方法

时间:2019-05-29 11:49:19

标签: c++ design-patterns random c++17

我正在尝试通过配置文件提供一个界面,供我的用户选择他们所使用的某些参数的分布。为此,我想使用STL随机数生成器算法。

让我们假设我的程序从命令行读取JSON。对于下面提供的JSON,程序需要意识到它应该根据给定的均值和标准差从正态分布生成一个随机数。 (我使用与STL库相同的参数名称来进行清除。)

{
    "dist": "normal_distribution",
    "mean": 0.1,
    "stddev": 0.5
}

到目前为止,我可以轻松解析JSON,并使用每个发行版的param_type初始化发行版。我使用名称来决定要确定param_type和分布的分布。

我不知道如何很好地实现这一目标。我知道我应该为此提供某种工厂方法,传递JSON并吐出一个函数或类。如果我想返回一个类的实例,比方说生成器的unique_ptr,则需要定义一个抽象类,例如RandDist并编写某种适配器以合并我的输入。 ...我通常不需要从该类中学到很多东西,只需一个gen()方法就足够了。

我想知道是否有人对此有所考虑。或者,如果有人知道可以做到这一点的图书馆。

P.S。输入不必是JSON对象,任何哈希表本身都可以工作。

2 个答案:

答案 0 :(得分:4)

您描述了一种处理这种情况的标准方法-仅使用一个虚拟方法RandomGenerator来创建抽象gen()类。

然后,它将具有NormalDistributionGeneratorUniformDistributionGenerator等实现,构造函数接受适当的分发参数集并将STL内容初始化为成员。 这些具体类将仅在生成器创建例程中直接使用,并在其他地方作为抽象RandomGenerator使用。

因此创建例程的外观应与此类似

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-是一个包含分发信息(map / hash_table的JSON包装器-在您的情况下效果最佳的类)的类。

因此,您肯定需要编写 some 样板代码以使其起作用,但是它将使您的RandomGenerator的使用变得简单明了,并且添加新的发行类型很容易,只需要在一个地方修改代码-工厂方法。

答案 1 :(得分:1)

我试图将样板保持在最低限度。假设:

  • 您已经预先知道了生成器的类型(如果您还需要动态生成器,则可以轻松切换)

  • 所有发行版都生成double(由于API必须返回 something 具体的对象才能正常使用,因此已经非常普及)

  • 所有分布都可以通过double参数进行构造(也可以通过代理对象进行调整,但是取决于您的实际JSON库,该工作可能已经在此处完成)

  • 我使用了GCC预处理程序扩展来处理零参数情况,但是可以肯定地将宏重写为不需要它。

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