Passing a closure as a parameter to a constructor

2019-07-31 04:19发布

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.

4条回答
爷的心禁止访问
2楼-- · 2019-07-31 04:32

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)...);
}
查看更多
聊天终结者
3楼-- · 2019-07-31 04:40

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.

查看更多
来,给爷笑一个
4楼-- · 2019-07-31 04:48

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

查看更多
Evening l夕情丶
5楼-- · 2019-07-31 04:51

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.

查看更多
登录 后发表回答