Передача нескольких функций в другую функцию в векторе в C ++ - PullRequest
1 голос
/ 23 мая 2019

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

В некоторых случаях может быть только две функции, в других - три, четыре или более функций. Я пытаюсь сделать это путем инкапсуляции связанных функций в вектор, и я хотел бы вызвать в процессе обертку std :: function. Ниже я показываю упрощенную версию аналогичного кода. В настоящее время я пытаюсь определить способ инкапсуляции функций в массиве, чтобы его можно было передать в функцию solver. Я использую компилятор C ++ 17 с Clang. Есть ли способ передать эти функции в массиве, или есть лучший способ сделать это, о котором я не знаю?

double func1(std::map<std::string, double> arr)
{
    dydt = arr["one"] * 3.0 - arr["two"];
    return dydt;
}

double func2(std::map<std::string, double> arr)
{
    dydt = 1 / arr["three"] + 3.0 * arr["two"];
    return dydt;
}

double solver(std::vector<std::function<double(std::map<std::string, double>) &funcs, std::map<std::string, double> inputs)
{
    double res;
    std::vector<double> data;
    for (int i = 0; i < funcs.size(); i++)
    {
        // - The ODE solver is not shown in this example, 
        //   but it highlights the general intent
        res = funcs[i](inputs);
        data.push_back(res);
    }
    return data;
}

int main()
{
    std::map<std::string, double> inputs;
    inputs["one"] = 1.0;
    inputs["two"] = 2.0;
    inputs["three"] = 3.0;
    inputs["four"] = 4.0;

    // The line of code below does not work
    std::vector<std::function<double(std::map<std::string, double>)>> funcs = {func1, func2};
    std::vector<double> dat;

    // - Due to the fact that the array of funcs does not work, 
    //  this function call also does not work
    dat = solver(funcs, inputs);
    return 0;
}

Ответы [ 2 ]

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

Поскольку вы пометили свой вопрос как C ++ 17, вы можете использовать шаблон функции переменной с кратным выражением :

template<typename ...Funcs>
std::vector<double> solver(const std::map<std::string, double>& inputs, Funcs&&... funcs)
{
    std::vector<double> results;
    (results.push_back(funcs(inputs)), ...);
    return results;
}

Я изменил тип аргумента на const std::map<std::string, double>&по причинам производительности.Передача по ссылке (&) рекомендуется здесь.Передача по значению будет копировать std::map при каждом вызове функции.Так что функции должны быть объявлены как:

double func1(const std::map<std::string, double>& arr);

Теперь вам больше не нужен вектор для передачи функций.Вы можете просто передать их непосредственно в качестве аргументов solver:

int main()
{
    std::map<std::string, double> inputs {
        {"one", 1.0},
        {"two", 2.0},
        {"three", 3.0},
        {"four", 4.0}
    };

    auto results = solver(inputs, func1, func2);
}

Обратите внимание, что шаблон функции solver принимает все вызываемые.Например, лямбды также работают:

solver(inputs,
    [](auto arr){ return arr["one"] * 3.0 - arr["two"]; },
    [](auto arr){ return 1 / arr["three"] + 3.0 * arr["two"]; }
);

(Вы можете использовать const std::map<std::string, double>& arr вместо auto arr, если это более читабельно для вас. Обратите внимание, что auto в лямбде означает, что лямбда становится шаблоном.)

0 голосов
/ 23 мая 2019

Вы можете сделать это, используя комбинацию std::function, std::any и пакета параметров:

#include <functional>
#include <string>
#include <map>
#include <any>
#include <vector>

using my_f_type = std::function<int(std::map<std::string, int>&)>;

template<typename...T>
int solver(const T& ... functions) {
    std::vector<std::any> vec = { functions... };
    std::map<std::string, int> foo = { { "test", 37 } };
    for (auto& f : vec) {
        try {
            return std::any_cast<my_f_type>(f)(foo);
        } catch (const std::bad_any_cast& e) {}
    }
    return 0;
}

int main() {
    std::function<int(int)> a = [](int a) { return a; };
    std::function<double(double)> b = [](double b) {return b; };
    my_f_type c = [](std::map<std::string, int>& c) { return c["test"]; };
    return solver(a, b, c);
}

В main у вас есть список функций для передачи на solver, которыепринимает n аргументов.Затем переданные вами функции сохраняются в std::vector из std::any с.Попытка перехвата в этом примере просто случай использования, здесь я просто хочу вызвать функцию c.Возвращение main равно 37.

Я хотел показать здесь различные типы функций.Но если ваши функции всегда имеют один и тот же тип, вы можете отказаться, используя std::any (и теперь передавая foo):

#include <string>
#include <map>

template<typename...T>
int solver(std::map<std::string, int>& foo, const T& ... functions) {
    return (functions(foo) + ...);
}

int main() {
    std::map<std::string, int> foo = { { "a",1 },{ "b",2 },{ "c",3 } };
    auto a = [](std::map<std::string, int>& a) { return a["a"]; };
    auto b = [](std::map<std::string, int>& b) { return b["b"]; };
    auto c = [](std::map<std::string, int>& c) { return c["c"]; };
    solver(foo, a, b, c); // returns 6
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...