Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

convert std::bind to function pointer

I have a third-party library which has a method that takes a function pointer as the first parameter:

int third_party_method(void (*func)(double*, double*, int, int, double*), ...); 

I want to pass a pointer to a class' method that is declared as follows:

class TestClass {     public:         void myFunction (double*, double*, int, int, void*); 

I tried to pass this function as follows:

TestClass* tc = new TestClass(); using namespace std::placeholders; third_party_method(std::bind(&TestClass::myFunction, tc, _1, _2, _3, _4, _5), ...); 

However, this does not compile:

Conversion of parameter 1 from 'std::tr1::_Bind<_Result_type,_Ret,_BindN>' to 'void (__cdecl *)(double *,double *,int,int,void *)' is not possible with [     _Result_type=void,     _Ret=void,     _BindN=std::tr1::_Bind6<std::tr1::_Callable_pmf<void (__thiscall TestClass::* const )(double *,double *,int,int,void *),TestClass,false>,TestClass *,std::tr1::_Ph<1>,std::tr1::_Ph<2>,std::tr1::_Ph<3>,std::tr1::_Ph<4>,std::tr1::_Ph<5>> ] 

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

like image 480
Nico Schertler Avatar asked Nov 05 '12 18:11

Nico Schertler


People also ask

Can we bind function to a pointer?

Yes, bind does it the way you want. You would just bind all the parameters to the function, then you can call it with no arguments.

Is std :: function a function pointer?

No. One is a function pointer; the other is an object that serves as a wrapper around a function pointer. They pretty much represent the same thing, but std::function is far more powerful, allowing you to do make bindings and whatnot.


2 Answers

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.

like image 111
Evgeny Panasyuk Avatar answered Oct 14 '22 08:10

Evgeny Panasyuk


It doesn't compile because the third-party function is expecting a pointer-to-function, but you are trying to pass it a pointer-to-member-function. The two types are fundamentally different, and cannot be interchanged. In fact, pointers-to-member-functions are very often strange animals.

Here's an SSCCE illustrating the problem you're having:

#include <iostream> #include <iomanip> using namespace std;  typedef void(*SpeakFn)(void);  void Bark() {     cout << "WOOF" << endl; }  void Meow() {     cout << "meeow" << endl; }  void SpeakUsing(SpeakFn fn) {     fn(); }  class Alligator { public:     void Speak()     {         cout << "YAWWW" << endl;     }     typedef void(Alligator::*AlligatorSpeakFn)(void);      void SpeakUsing(AlligatorSpeakFn fn)     {         (this->*fn)();     } };  int main() {     SpeakUsing(&Bark); // OK      Alligator a;     Alligator::AlligatorSpeakFn mem_fn = &Alligator::Speak;     a.SpeakUsing(mem_fn);   // OK      SpeakUsing(mem_fn); // NOT OK -- can't cvt from fn-ptr to mem-fn-ptr } 

You can't call SpeakUsing with a pointer-to-member-function because it's not convertible to pointer-to-function.

Use a static member function instead, such as:

class Alligator { public:     static void Speak()     {         cout << "YAWWW" << endl;     }     typedef void(*AlligatorSpeakFn)(void);      void SpeakUsing(AlligatorSpeakFn fn)     {         fn();     } }; 
like image 34
John Dibling Avatar answered Oct 14 '22 08:10

John Dibling