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.
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.
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.
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)...);
}
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