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

Writing a Factory method for STL random number generators

本文关键字:工厂 方法 STL 随机数生成器      更新时间:2023-10-16

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

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

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

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

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

我想知道是否有人对此有想法。或者,如果有人知道可以做到这一点的库。

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

您描述了一种非常标准的方式来处理这种情况 - 只有一个虚拟方法的抽象RandomGeneratorgen()

然后,它将具有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- 是一个保存分布信息的类(一些 JSON 包装器,映射/hash_table - 任何最适合您的情况)。

因此,您肯定需要编写一些样板代码来使其工作,但它会使RandomGenerator的使用变得简单明了,并且添加新类型的发行版将非常简单,并且只需要在单个地方修改代码 - 工厂方法。

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

  • 您提前知道发电机的类型(如果您需要发电机也是动态的,则可以轻松切换)

  • 所有发行版都生成doubles(这几乎是烘焙的,因为 API 必须返回一些具体的东西才能体面地使用)

  • 所有分布都可以从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};
}

在Wandbox上观看直播