Convert Std::Bind to Function Pointer

convert std::bind to function pointer

Is there any way I can pass the member to the function?

Unless your class object is some kind of global object - it is not possible. Because objects may contain some data, while function pointer is just pointer to function - it doesn't contain any runtime context, only compile-time one.

If you accept having compile-time unique IDs for each callback passing, then you can use following generalized approach.

Usage:

void test(void (*fptr)())
{
fptr();
}

struct SomeStruct
{
int data;
void some_method()
{
cout << data << endl;
}
void another_method()
{
cout << -data << endl;
}
};

int main()
{
SomeStruct local[] = { {11}, {22}, {33} };

test(get_wrapper<0>( boost::bind(&SomeStruct::some_method,local[0]) ));
test(get_wrapper<1>( boost::bind(&SomeStruct::another_method,local[0]) ));

test(get_wrapper<2>( boost::bind(&SomeStruct::some_method,local[1]) ));
test(get_wrapper<3>( boost::bind(&SomeStruct::another_method,local[1]) ));

test(get_wrapper<4>( boost::bind(&SomeStruct::some_method,local[2]) ));
test(get_wrapper<5>( boost::bind(&SomeStruct::another_method,local[2]) ));
}

It may not require Unique ID's for each invocation, for instance because Functors may already have different types, or runtime scope of their usage do not overlap. But it is safer to use unique ID each time.

Implementation:

live demo

#include <boost/optional.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <ostream>
using namespace std;

template<unsigned ID,typename Functor>
boost::optional<Functor> &get_local()
{
static boost::optional<Functor> local;
return local;
}

template<unsigned ID,typename Functor>
typename Functor::result_type wrapper()
{
return get_local<ID,Functor>().get()();
}

template<typename ReturnType>
struct Func
{
typedef ReturnType (*type)();
};

template<unsigned ID,typename Functor>
typename Func<typename Functor::result_type>::type get_wrapper(Functor f)
{
(get_local<ID,Functor>()) = f;
return wrapper<ID,Functor>;
}

// ----------------------------------------------------------------------

void test(void (*fptr)())
{
fptr();
}

struct SomeStruct
{
int data;
void some_method()
{
cout << data << endl;
}
void another_method()
{
cout << -data << endl;
}
};

int main()
{
SomeStruct local[] = { {11}, {22}, {33} };

test(get_wrapper<0>( boost::bind(&SomeStruct::some_method,local[0]) ));
test(get_wrapper<1>( boost::bind(&SomeStruct::another_method,local[0]) ));

test(get_wrapper<2>( boost::bind(&SomeStruct::some_method,local[1]) ));
test(get_wrapper<3>( boost::bind(&SomeStruct::another_method,local[1]) ));

test(get_wrapper<4>( boost::bind(&SomeStruct::some_method,local[2]) ));
test(get_wrapper<5>( boost::bind(&SomeStruct::another_method,local[2]) ));
}

P.S. Beaware of multi-thread access - in such cases you should use some kind of Thread-local storage data.

Can I use std::bind to convert a pointer to member function into a pointer to function?

NathanOliver's comment is correct, and your suspicion is mostly correct. Exactly how pointers to member functions work is not specified, but including this as a hidden argument mostly works. You just need a bit of extra work for inheritance and pointers to virtual functions (yes, you can take their address too).

Now, often callbacks include a void* parameter under your control, which you can use to pass a A*. In those cases, you can write a wrapper (static) function that casts the void* back to A* and does the actual call to &A::callback.

That's not the case here. Registration takes a single function, without data. To get this to work in real-life situations, you have to resort to drastic solutions - not portable C++. One such method is to dynamically generate assembly (!). You create - at runtime - the compiled equivalent of

void __trampoline_0x018810000 (int i)
{
A* __this = reinterpret_cast<A*>(0x018810000);
__this->callback(i);
}

As you can see, you have to generate one trampoline for every A* value, and managing lifetimes of these is a major pain.

How can I store a std::bind function pointer?

You can make use of std::function. In particular, replace void (*function)() with std::function<void ()> function as shown below:

class Context {
private:
//other code here

std::function<void ()> function;

};

Working demo

Get function pointer from std::function when using std::bind

This is quite impossible. The whole reason that std::function exists is that function pointers suck horrifically and should never, ever, be used by anyone, ever again, except for the doomed souls bearing the Burning Standards of Hell C interoperation, because they cannot handle functions with state.

A std::function<void()> cannot, in the general case, be converted to a void(*)(). The only reason this works in the first example is because it happens to be a void(*)() originally.

how to pass a member function as a function pointer?

You can accomplish this using std::function and std::bind:

#include <functional>
#include <iostream>
#include <string>

class Work {
public:
std::function<void(const std::string&)> logger;
void do_sth() { logger("on log"); }
};

class P {
public:
void log(const std::string& s) { std::cout << s; }
};

int main() {
Work w;
P p;
w.logger = std::bind(&P::log, p, std::placeholders::_1);
w.do_sth();
}

Note that function and bind may not be in your implementation's standard library yet; you can also get them from the Boost libraries.

Is it possible to change the member function pointer from std::bind to lambda?

It would be possible to do it with your current code by using

func1 = [=](int x, int y){ (pClass->*pFunc)(x,y); };

In my opinion it's probably better to write out the lambda at the calling site instead.
To do that you need to modify setCallback.

class A
{
public:
template<class T>
void setCallback(T callback);

private:
std::function<void(int, int)> func1;
}

template<class T>
inline void A::setCallback(T callback)
{
func1 = callback;
}

Then we can call from Bs constructor with a lambda.

class B
{
public:
void bFoo(int x, int y);

private:
A a; // instance of class A
}

// Class B - B.cpp
B::B()
{
a.setCallback([&](int x, int y){ bFoo(x, y); }); // & will capture this
}


Related Topics



Leave a reply



Submit