Написание метода Factory для генераторов случайных чисел STL - PullRequest
2 голосов
/ 29 мая 2019

Я пытаюсь предоставить интерфейс - через файл конфигурации - чтобы мои пользователи выбирали дистрибутив для некоторых параметров, которые они используют.Я хотел бы использовать для этой цели алгоритмы генератора случайных чисел STL.

Предположим, что моя программа читает JSON из командной строки.Для JSON, представленного ниже, программе необходимо понимать, что она должна генерировать случайное число из нормального распределения с заданным средним и стандартным отклонением.(Я использую те же имена параметров, что и библиотека STL для очистки.)

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

До сих пор я мог легко анализировать JSON и использовать param_type каждого распределения для инициализации распределения.Я использую имя, чтобы решить, какой дистрибутив решать param_type и дистрибутив.

Чего я не знаю, так это как это реализовать.Я знаю, что должен предоставить какой-то фабричный метод для этого, передать JSON и выплюнуть функцию или класс.Если я хочу вернуть экземпляр класса, скажем, unique_ptr генератора, мне нужно определить абстрактный класс, например, RandDist и написать какой-то адаптер для включения моего ввода, .... Iкак правило, многим не нужно от класса, достаточно просто метода gen().

Мне интересно, есть ли у кого-нибудь мысли по этому поводу.Или, если кто-то знает библиотеку, которая может это сделать.

PS Ввод необязательно должен быть объектом JSON, любая хеш-таблица будет работать сама по себе.

Ответы [ 2 ]

5 голосов
/ 29 мая 2019

Вы описали довольно стандартный способ справиться с этой ситуацией - иметь абстрактный класс RandomGenerator только с одним виртуальным методом gen().

Тогда он будет иметь реализации типа NormalDistributionGenerator, UniformDistributionGenerator и т. д. с конструкторами, принимающими соответствующий набор параметров распределения и инициализирующими 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-оболочка, map / hash_table - что бы ни работало лучше в вашем случае).

Так что вам определенно потребуется написать некоторый шаблонный код, чтобы сделатьэто работает, но это сделает использование вашего RandomGenerator простым и понятным, а добавление новых типов дистрибутивов будет достаточно легким и потребует модификации кода только в одном месте - фабричный метод.

2 голосов
/ 29 мая 2019

Я пытался свести шаблон к минимуму.Допущения:

  • Вы заранее знаете тип вашего генератора (это легко переключается, если вам нужны динамические генераторы)

  • Все дистрибутивы генерируют double с (это довольно сложно, поскольку 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

...