using std::function, std::bind, perfect forwarding

A

Andrew Tomazos

I want to write a class that will wrap an arbitrary function and bind
a set of arguments to it so that I can execute it later (perhaps in
another thread, perhaps repeatedly):

void f(const A& a) { ... }

int main()
{
A a = ...;
PFunction pf = MakeFunction(f,a);
...
pf->call(); // executes f(a);
}

I've implemented something using std::function and std::bind, and it
seems to work - but im having trouble getting the arguments to forward
perfectly.

The output of the below test program (which tracks the argument A) is:

constructed at 0x7fff7f008d7f
copied from 0x7fff7f008d7f to 0x7fff7f008a98 <----- HERE
moved from 0x7fff7f008a98 to 0x20ce068
desctructed at 0x7fff7f008a98
desctructed at 0x7fff7f008d7f
used at 0x20ce068
desctructed at 0x20ce068

Is there a way to modify MakeFunction and FunctionT (perhaps using
std::forward or std::decay or something) such that the temporary A is
moved into the Function object, and not copied?

Thanks,
Andrew.

#include <iostream>
#include <memory>
#include <functional>

using namespace std;

class Function;

typedef shared_ptr<Function> PFunction;

class Function
{
public:
virtual void call() = 0;
};

template<class F, class... Args>
class FunctionT : public Function
{
public:
FunctionT(F f, const Args&... args)
: m_f(bind(f,args...))
{}

void call() { m_f(); }

private:
function<void()> m_f;
};

template<class F, class... Args>
shared_ptr<Function> MakeFunction(F&& f, Args&&... args)
{
auto f2 = make_shared<FunctionT<F, Args...>>(f, args...);
return dynamic_pointer_cast<Function> (f2);
}

class A
{
public:
A()
{
cout << "constructed at " << this << endl;
}

A(const A& that)
{
cout << "copied from " << &that << " to " << this << endl;
}

A(A&& that)
{
cout << "moved from " << &that << " to " << this << endl;
}

~A()
{
cout << "desctructed at " << this << endl;
}
};

void f(const A& a)
{
cout << "used at " << &a << endl;
}

PFunction pf;

void f1()
{
pf = MakeFunction(f, A());
}

void f2()
{
pf->call();
}

int main()
{
f1();
f2();
return 0;
}
 
A

Andrew Tomazos

I figured it out. problem was I wasnt passing template parameter to
std::forward (ie forward(args) instead of forward<Args>(args)).

This version moves into place:

class Function;

typedef shared_ptr<Function> PFunction;

class Function
{
public:
virtual void call() = 0;
virtual ~Function() {};
};

template<class F, class... Args>
class FunctionT : public Function
{
public:
FunctionT(const F& f, Args&&... args)
: m_f(bind(f, forward<Args>(args)...))
{}

void call() { m_f(); }

private:
function<void()> m_f;
};

template<class F, class... Args>
PFunction MakeFunction(F f, Args&&... args)
{
auto f2 = make_shared<FunctionT<F, Args...>>(f,
forward<Args>(args)...);
return dynamic_pointer_cast<Function> (f2);
}

-Andrew.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top