Search code examples
c++templatesc++17variadic-templates

Can I parse out individual functions from a C++17 fold expression


I am trying to write a code that will solve coupled Ordinary Differential Equations (i.e. ODEs). The function needs to accept an array of functions, each function is one of the coupled ODEs. I am trying to use the C++17 fold expression to pass the functions to the master function, which then individually passes the functions, one at a time to the solver. The solver needs to solve the equation, and based on the results, it will modify the input map. The map must be modified based on the results of the first function, since the modified map is the input to the next function. The general code is shown below. The code is simplified to highlight the problem I am trying to solve.

int main()
{
    ODESolver q;
    std::map<std::string, double> inputs {
        {"x", 1.0},
        {"y", 2.0},
        {"z", 3.0},
        {"a", 4.0}
    };

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

double func1(std::map<std::string, double> arr)
{
    double dcadt = -arr["x"] * arr["y"] + arr["z"] * arr["a"];
    return dcadt;
}

double func2(std::map<std::string, double> arr)
{
    double dccdt = arr["x"] * arr["y"] - arr["a"] * arr["z"];
    return dccdt;
}

The information in the class is shown here;

class ODESolver
{
public:
    template<typename ...Funcs>
    std::vector<double> framework(std::map<std::string, double> inputs, Funcs&&... funcs)
    {
        std::tuple<double, double> res;
        for (int i = 0; i < funcs.size(); i++)
        {
            // - THIS IS WHERE MY PROBLEM IS MANIFESTED, THE
            //   COMPILER WILL NOT LET ME EXTRACT AN INDIVIDUAL
            //   FUNCTION FROM A FOLD EXPRESSION!
            res = solver(inputs, funcs[i]);
            step_size = std::get<1>(res);
            inputs["x"] += 1.0;
            inputs["y"] std::get<0>(res);
        }
    }
    std::tuple<double, double>
    ODESolver::solver(std::map<std::string, double>, const std::function<double(std::map<std::string, double>)& func)
    {
        double dydt = func(inputs);
        double val = inputs["y"];
        std::tuple<double, double> values(dydt, val);
        return values;
    }
};

The above example is somewhat made up, but it highlights the problem for my actual code. I don't know how to extract an individual function from a C++17 fold expression, so I can individually pass them to another function. As you can see from the above code, I need to solve the func1 first, so that it can update the inputs map, before func2 uses inputs in its function call. Is there a way to do this with a fold expression, or is there another way that I need to pass the functions from the main program to the framework function.


Solution

  • Write a lambda to do the things you need to do:

    auto apply_next = [&](auto&& f){
        res = solver(inputs, f);
        step_size = std::get<1>(res);
        inputs["x"] += 1.0;
        inputs["y"] += std::get<0>(res);
    };
    

    And then fold that lambda invocation over a comma:

    (apply_next(funcs), ...);
    

    Expansion Statements look to be on track for C++20, which will allow the more direct thing you wanted to write to begin with:

    for ... (auto&& f : funcs) {
        res = solver(inputs, f);
        step_size = std::get<1>(res);
        inputs["x"] += 1.0;
        inputs["y"] += std::get<0>(res);
    }