convert std :: bind to function puntero

Tengo una biblioteca de terceros que tiene un método que toma un puntero de función como primer parámetro:

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

Quiero pasar un puntero al método de una clase que se declara de la siguiente manera:

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

Traté de pasar esta función de la siguiente manera:

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

Sin embargo, esto no comstack:

 Conversion of parameter 1 from 'std::tr1::_Bind' 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,TestClass *,std::tr1::_Ph,std::tr1::_Ph,std::tr1::_Ph,std::tr1::_Ph,std::tr1::_Ph> ] 

¿Hay alguna forma de que pueda pasar el miembro a la función?

¿Hay alguna forma de que pueda pasar el miembro a la función?

A menos que su objeto de clase sea algún tipo de objeto global, no es posible. Debido a que los objetos pueden contener algunos datos, mientras que el puntero de función es solo un puntero a la función, no contiene ningún contexto de tiempo de ejecución, solo uno de tiempo de comstackción.

Si acepta tener identificadores únicos en tiempo de comstackción para cada paso de callback, entonces puede usar el siguiente enfoque generalizado.

Uso:

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

Es posible que no requiera ID únicos para cada invocación, por ejemplo, porque los Funtores ya pueden tener diferentes tipos, o el ámbito de tiempo de ejecución de su uso no se superpone. Pero es más seguro usar una identificación única cada vez.

Implementación:

demo en vivo

 #include  #include  #include  #include  using namespace std; template boost::optional &get_local() { static boost::optional local; return local; } template typename Functor::result_type wrapper() { return get_local().get()(); } template struct Func { typedef ReturnType (*type)(); }; template typename Func::type get_wrapper(Functor f) { (get_local()) = f; return wrapper; } // ---------------------------------------------------------------------- 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]) )); } 

PS Beaware de acceso a múltiples subprocesos; en tales casos, debe utilizar algún tipo de datos de almacenamiento Thread-local .

No se comstack porque la función de terceros espera un puntero a función, pero está intentando pasarle una función de puntero a miembro . Los dos tipos son fundamentalmente diferentes y no pueden intercambiarse. De hecho, las funciones de punteros a miembros a menudo son animales extraños .

Aquí hay un SSCCE que ilustra el problema que está teniendo:

 #include  #include  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 } 

No puede llamar a SpeakUsing con una función de puntero a miembro porque no es convertible a puntero a función.

Use una función de miembro estático en su lugar, como:

 class Alligator { public: static void Speak() { cout << "YAWWW" << endl; } typedef void(*AlligatorSpeakFn)(void); void SpeakUsing(AlligatorSpeakFn fn) { fn(); } }; 

Como otras personas mencionaron, no tienen más opción que usar datos globales o estáticos para proporcionar el contexto de llamada de enlace como función sin formato. Pero siempre que la solución no sea general, está atascada con la lista de parámetros vacíos de functor. Tendrá que escribir el wrapper manual, get_wrapper y Func para cada firma de función diferente que quiera enlazar y darles diferentes nombres.

Me gustaría proponer una solución más general para el enlace sin formato:

 #include  #include  #include  #include  // Raw Bind - simulating auto storage behavior for static storage data template  class scoped_raw_bind { public: typedef scoped_raw_bind this_type; // Make it Move-Constructible only scoped_raw_bind(const this_type&) = delete; this_type& operator=(const this_type&) = delete; this_type& operator=(this_type&& rhs) = delete; scoped_raw_bind(this_type&& rhs): m_owning(rhs.m_owning) { rhs.m_owning = false; } scoped_raw_bind(BindFunctor b): m_owning(false) { // Precondition - check that we don't override static data for another raw bind instance if(get_bind_ptr() != nullptr) { assert(false); return; } // Smart pointer is required because bind expression is copy-constructible but not copy-assignable get_bind_ptr().reset(new BindFunctor(b)); m_owning = true; } ~scoped_raw_bind() { if(m_owning) { assert(get_bind_ptr() != nullptr); get_bind_ptr().reset(); } } decltype(&FuncWrapper::call) get_raw_ptr() { return &FuncWrapper::call; } static BindFunctor& get_bind() { return *get_bind_ptr(); } private: bool m_owning; static std::unique_ptr& get_bind_ptr() { static std::unique_ptr s_funcPtr; return s_funcPtr; } }; // Handy macro for creating raw bind object // W is target function wrapper, B is source bind expression #define RAW_BIND(W,B) std::move(scoped_raw_bind>(B)); // Usage /////////////////////////////////////////////////////////////////////////// // Target raw function signature typedef void (*TargetFuncPtr)(double, int, const char*); // Function that need to be called via bind void f(double d, int i, const char* s1, const char* s2) { std::cout << "f(" << d << ", " << i << ", " << s1 << ", " << s2 << ")" << std::endl; } // Wrapper for bound function // id is required to generate unique type with static data for // each raw bind instantiation. // THE ONLY THING THAT YOU NEED TO WRITE MANUALLY! template  struct fWrapper { static void call(double d, int i, const char* s) { scoped_raw_bind>::get_bind()(d, i, s); } }; int main() { using namespace std::placeholders; auto rf1 = RAW_BIND(fWrapper, std::bind(&f, _1, _2, _3, "This is f trail - 1")); TargetFuncPtr f1 = rf1.get_raw_ptr(); f1(1.2345, 42, "f1: Bind! Bind!"); auto rf2 = RAW_BIND(fWrapper, std::bind(&f, _1, _2, _3, "This is f trail - 2")); TargetFuncPtr f2 = rf2.get_raw_ptr(); f2(10.2345, 420, "f2: Bind! Bind!"); auto rf3 = RAW_BIND(fWrapper, std::bind(&f, _1, _2, _3, "This is f trail - 3")); TargetFuncPtr f3 = rf3.get_raw_ptr(); f3(100.2345, 4200, "f3: Bind! Bind!"); } 

Fue probado – ver acción en vivo aquí

No, no es fácil. El problema es que un puntero de función tiene un fragmento de información: la dirección de la función. Un método necesita tanto eso como la dirección del objeto, o alternativamente se puede pasar la dirección de los objetos como un parámetro.

Hay maneras extremadamente hack para hacer esto, pero van a ser específicos de la plataforma. Y extremadamente hackey. Tanto que recomendaré variables globales en lugar de usarlas.

Usted sabe cómo hacer esto si hay una única instancia global de la clase, ¿verdad?