Passing a closure as a parameter to a constructor

2019-07-31 04:21发布

问题:

In the code below, I'd like to get rid of the weird constructors taking various types of function pointers, and the parameter lists that have to be saved off (along with all the member variables that are required to hold all this), and instead use a closure to do all of that, leaving Event with a single member variable that is something like Closure closure;. Is there a way to do this using closures in C++0x?

#include <iostream>
#include <list>
#include <functional>
#include <algorithm>

class Event
{
    std::function<double(double, double)> func1;
    bool func1Defined;
    double double1, double2;

    std::function<double(int, int, int)> func2;
    bool func2Defined;
    int int1, int2, int3;
public:
    void setFalse()
    {
        func1Defined = false;
        func2Defined = false;
    }
    Event(std::function<double(double, double)> func, double double1, double double2)
    {
        setFalse();
        func1 = func;
        func1Defined = true;
        this->double1 = double1;
        this->double2 = double2;
    }

    Event(std::function<double(int, int, int)> func, int int1, int int2, int int3)
    {
        setFalse();
        func2 = func;
        func2Defined = true;
        this->int1 = int1;
        this->int2 = int2;
        this->int3 = int3;
    }

    void run()
    {
        double ret = 0;
        if (func1Defined) ret = func1(double1, double2);
        else if (func2Defined) ret = func2(int1, int2, int3);
        /* do something with ret */
        std::cout << ret << "\n";
    }
};

double twoDoubleParam(double a, double b)
{
    return a + b;
}

double threeIntParam(int a, int b, int c)
{
    return (a + b) / c;
}

int main(int argc, char* argv[])
{
    std::list<Event> events;
    events.push_back(Event(*twoDoubleParam, 1.0, 3.5));
    events.push_back(Event(*threeIntParam, 2, 4, 2));
    std::for_each(events.begin(), events.end(), [&](Event event)
    {
        event.run();
    });

    int y;
    std::cin >> y;
    return 0;
}

Prefer no boost, but if boost is best/only easy way to do this I'd like to know how.

回答1:

It seems your Event should just hold a std::function<double()> in the first place: independent on using lambda or not, you want to bind the arguments to create a nullary function (as far as I can tell; if there are actual parameters to each call, you'd obviously use a corresponding signature in std::function<Signature>.

Assuming you Event has a member fun of type std::function<double()> and a constructor like

template <typename Fun>
Event::Event(Fun fun): fun(fun) {}

you could, e.g., use

events.emplace_back([](){ return twoDoubleParam(1.0, 3.5); });
events.emplace_back(std::bind(&threeIntParam, 2, 4, 2));

Both approaches could use std::bind() or lambda. I just mixed the use to get one of each. Obviously, if you use the lambda and the parameters are not constants but rather values, you'd use [=](){ ... } to capture the closure values by value.



回答2:

Use std::bind to do this. See here: http://en.cppreference.com/w/cpp/utility/functional/bind

It's in VS2012/13, and the newer GCC as well. The return value can be assigned directly to a std::function<void()> if you want as well.



回答3:

Looks like you want std::bind:

std::function<void()> func;

...

Event(std::function<double(double, double)> func, double d1, double d2) {
    func = std::bind(func, d1, d2);
}

// Repeat for the three ints

Alternatively you can just have the user be able to give any functor/args combo and bind it for them, like the constructor of std::thread:

template<typename F, typename... Args>
Event(F&& f, Args&&... args) {
    func = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
}

or

template<typename F, typename... Args>
Event(Args&&... args) {
    static_assert(sizeof...(Args) > 1, "Event() requires more than 1 argument");
    func = std::bind(std::forward<Args>(args)...);
}


回答4:

You can use std::bind and std::function :

class Event {
    function<double()> func;
public:
    template <typename H, typename... Args>
    Event(H &&f, Args&&... args) 
          : func(bind(std::forward<H>(f), std::forward<Args>(args)...)) {}

    void run()
    {
        double ret = func();
        std::cout << ret << "\n";
    }
};

double twoDoubleParam(double a, double b) {
    return a + b;
}

double threeIntParam(int a, int b, int c) {
    return (a + b) / c;
}

int main(int argc, char* argv[]) {
    std::list<Event> events;
    events.push_back(Event(twoDoubleParam, 1.0, 3.5));
    events.push_back(Event(threeIntParam, 2, 4, 2));
    std::for_each(events.begin(), events.end(), [&](Event &event)
    {
        event.run();
    });
}

Output

4.5
3

Live code