Как избежать разветвления для вызова, где количество аргументов зависит от условных выражений? - PullRequest
2 голосов
/ 29 мая 2019

Я преобразовываю некоторый код из CreateProcess для использования .Мне нужно заменить мои CreateProcess использования на boost::process::child.Проблема в том, что у меня несовместимые способы сказать «Я хочу использовать значение по умолчанию».То, что раньше было командой из одной строки, превратилось в шестнадцать if операторов.

Используемая в настоящее время функция работает следующим образом (упрощенно):

void CreateProcess(int, float);

Каждый параметр имеет значение, которое вы можетеиспользуйте, чтобы указать, что вы хотели по умолчанию.В этом примере я буду использовать 0:

int int_param = ...;
float float_param = ...;

CreateProcess(0, 0); //Use both defaults
CreateProcess(int_param, 0); //Use default float
CreateProcess(0, float_param); //Use default int
CreateProcess(int_param, float_param); //No defaults

Это обычный дизайн.Вы знаете, о чем я говорю.Хотел ли я использовать значение по умолчанию или нет, можно было решить с помощью простого условия, такого как (... ? 0 : int_param).Это позволило каждому вызову CreateProcess быть одной строкой кода.

CreateProcess( (... ? 0 : int_param), (... ? 0 : float_param) );

Там, где я раньше звонил CreateProcess, теперь я хочу создать класс child.Конструктор для child работает следующим образом:

template<typename ...Args>
explicit child(Args&&...args);

Вместо того, чтобы передавать конкретное значение для использования по умолчанию, я должен ничего не передавать .

int int_param = ...;
float float_param = ...;

child c; //Use both defaults
child c(int_param); //Use default float
child c(float_param); //Use default int
child c(int_param, float_param); //No defaults
child c(float_param, int_param); //Argument order is irrelevant

Пока что мое единственное «решение» - это использование if ветвей.

if (...) {
    if (...)
        child c;
    else
        child c(float_param);
} else {
    if (...)
        child c(int_param);
    else
        child c(int_param, float_param);
}

В этом примере есть только два возможных аргумента, и он становится четырьмя ветвями. Реальное child имеет четыре возможных аргумента, поэтому у каждого экземпляра есть шестнадцать ветвей.

Мне бы хотелось, чтобы был какой-то способ построения вызова, чтобы избежать этого ветвления.Также подходит решение, специфичное для boost::process.

К вашему сведению, последние два аргумента для child могут иметь или не быть одного типа.

Ответы [ 2 ]

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

Если вы хотите иметь возможность пропустить параметры в вызове и делать такие вещи, как:

create_child();
create_child(std::make_optional(int_param));
create_child(std::make_optional(float_param),
             (... ? std::optional<int>{} : int_param);

Вы можете использовать некоторые шаблоны и рекурсивную функцию.Следующий код является примером.Есть способы сделать это лучше, например, создать только один child и не возвращать пакет, или использовать идеальную пересылку.

//Recursion base case
//Recieves values to pass to child in a tuple
//Returns created child
template <typename... Ts>
child create_child_detail(std::tuple<Ts...> t)
{
    //Is there a better way to apply a tuple to a ctor?
    return std::apply([](Ts... ts){ return child(ts...); }, t);
}

//Recursive function
//Removes arguments from variadic template and adds arguments to tuple
template <typename... Ts, typename O, typename... Os>
child create_child_detail(std::tuple<Ts...> t, std::optional<O> o, std::optional<Os>... os)
{
    if (o) //Valid optional, add value to tuple to send to child
        return create_child_detail(std::tuple_cat(t, std::make_tuple(*o)), os...);
    else //Invalid optional, value should not be added to child
        return create_child_detail(t, os...);
}

//Called by user
//Just calls create_child_detail
template <typename... Ts>
child create_child(std::optional<Ts>... ts)
{
    return create_child_detail(std::tuple<>{}, ts...);
}

Пустые опции удаляются.Аргументы передаются child в том порядке, в котором вы их написали.Это может принимать любое количество аргументов.Я тестировал на Coliru здесь , чтобы вы могли поиграть с ним.

1 голос
/ 29 мая 2019

Используйте функцию для создания child объектов, которые содержат ветви. Передайте std::optional в качестве параметров:

child make_child(const std::optional<int>& int_param,
                 const std::optional<float>& float_param)
{
    if (int_param && float_param) {
        return child(std::to_string(int_param.value()),
                     std::to_string(float_param.value()));
    }

    // Rest of the branches ...
}

Это позволяет вам создать child объект, подобный этому:

child c = make_child(... ? std::optional<int>() : int_param,
                     ... ? std::optional<float>() : float_param);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...