una todas tipos retorno programacion predefinidas lenguaje las funciones funcion estructura ejemplos con c++ function struct callback

c++ - todas - tipos de funciones en programacion c



¿El mejor método para llamar a una función miembro C++ mediante una devolución de llamada C? (9)

Dada una clase típica:

struct Whatever { void Doit(); }; Whatever w;

¿Cuál es la mejor manera de llamar a la función miembro mediante una devolución de llamada basada en C void * como pthread_create () o un manejador de señal?

pthread_t pid; pthread_create(&pid, 0, ... &w.Doit() ... );


¿Es la función miembro privada? Si no, usa la expresión estándar:

void* pthread_foo_caller(void* arg) { Foo* foo = static_cast<Foo*>(arg); foo->bar(); return NULL; }

Si la función miembro es privada, puede declarar un método estático en la clase que toma un puntero "this" y llama al método apropiado. Por ejemplo:

class Foo { public: static pthread_foo_caller(void* arg); ... }; void* Foo::pthread_foo_caller(void* arg) { Foo* foo = static_cast<Foo*>(arg); foo->private_bar(); return NULL; }


La función miembro DEBE ser estática. No estático tiene un argumento "este" implícito. Pase el puntero a su instancia Whatever como void * para que el miembro estático pueda acceder a la instancia.


La mayoría de las devoluciones de llamadas de C permiten especificar un argumento, por ejemplo

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);

Entonces podrías tener

void myclass_doit(void* x) { MyClass* c = reinterpret_cast<MyClass*>(x); c->doit(); } pthread_create(..., &myclass_doit, (void*)(&obj));


La solución más concisa es definir, en un archivo de cabecera compartido por todo su código:

template <typename T, void (T::*M)()> void* thunk( void* p) { T* pt = static_cast<T*>(p); (pt->*M)(); return 0; }

Es probable que desee definir 4 versiones: una donde el thunk devuelve void y void * , y otra donde la función miembro devuelve void y void * . De esta forma, el compilador puede coincidir con el mejor, según las circunstancias (y de hecho, se quejará si todo no coincide).

Entonces, todo lo que tiene que escribir cada vez que se encuentre con una de estas situaciones es:

pthread_create (& pid, 0, y thunk <Whatever, & Whatever :: doit>, & w);

Esto incluso funcionará cuando el método sea privado, siempre que se haga referencia al método desde el código de la clase. (De lo contrario, me pregunto por qué el código hace referencia a un método privado).


Si bien no lo he usado de C, para hacer devoluciones de llamada, recomiendo consultar libsigc ++ . Ha sido exactamente lo que he necesitado varias veces al hacer devoluciones de llamada en C ++.


Una cosa que debes tener en cuenta es que si escribes código como este:

try { CallIntoCFunctionThatCallsMeBack((void *)this, fCallTheDoItFunction); } catch (MyException &err) { stderr << "badness."; } void fCallTheDoItFunction(void *cookie) { MyClass* c = reinterpret_cast<MyClass*>(cookie); if (c->IsInvalid()) throw MyException; c->DoIt(); }

Puede encontrarse con serios problemas dependiendo de su compilador. Resulta que en algunos compiladores mientras optimizan, ven una sola llamada C en un bloque try / catch y exclaman con alegría: "Estoy llamando a una función C que, debido a que es una buena C pasada de moda, ¡no puede arrojar! ! Eliminaré todos los vestigios del try / catch ya que nunca se alcanzará.

Tonto compilador

No llame a C que le devuelve la llamada y espera poder atrapar.


Use una envoltura de función C como esta:

struct Whatever { void Doit(); }; extern "C" static int DoItcallback (void * arg) { Whatever * w = (Whatever *) arg; w->DoIt(); return something; }

Solo funciona si puedes pasar el puntero a la clase de alguna manera. La mayoría de los mecanismos de devolución de llamada lo permiten.

Afaik este es el único método para hacer esto. No puede llamar directamente a un método desde C sin muchos ataques de piratería.


Ver este enlace

Básicamente, no es directamente posible, porque: "Los punteros a miembros no estáticos son diferentes a los punteros de función C normales, ya que necesitan el puntero this de un objeto de clase para pasar. Por lo tanto, los punteros de función ordinarios y [punteros a] no estáticos las funciones miembro tienen firmas diferentes e incompatibles "


Aquí hay una manera simple de hacerlo, no olvide administrar la vida útil de su objeto "MemberFunction" correctamente.

#include class MyClass { public: void DoStuff() { printf("Doing Stuff!"); } }; struct MemberFunction { virtual ~MemberFunction(){} virtual void Invoke() = 0; }; void InvokeMember(void *ptr) { static_cast(ptr)->Invoke(); } template struct MemberFunctionOnT : MemberFunction { typedef void (T::*function_t)(); public: MemberFunctionOnT(T* obj, function_t fun) { m_obj = obj; m_fun = fun; } void Invoke() { (m_obj->*m_fun)(); } private: T *m_obj; function_t m_fun; }; template MemberFunction* NewMemberFunction(T *obj, void (T::*fun)()) { return new MemberFunctionOnT(obj, fun); } //simulate a C-style function offering callback functionality. void i_will_call_you_later(void (*fun)(void*), void *arg) { fun(arg); } int main() { //Sample usage. MyClass foo; MemberFunction *arg = NewMemberFunction(&foo, &MyClass::DoStuff); i_will_call_you_later(&InvokeMember, arg); return 0; }