todas - Cómo usar una función miembro de C++ como la función de devolución de llamada para un marco C
todas las funciones de c++ (6)
Hay una biblioteca C (que no puedo cambiar) que admite una función de devolución de llamada del tipo
void (*callback)(void *appContext, int eventid)
Quiero establecer una función C ++ como devolución de llamada.
Específicamente tengo las siguientes preguntas?
¿Debo declarar la función de devolución de llamada bajo el bloque
"extern C"
?¿Una función miembro debe ser estática para ser la función de devolución de llamada? ¿Es posible usar una función miembro no estática? Si es así, ¿cómo? ¿Y cuándo se recomienda utilizar una función miembro no estática?
¿Importa si la función es una función de plantilla o no?
¿Una función de estilo C no perteneciente a la clase tiene alguna ventaja sobre una función miembro de la clase?
Estoy probando estas variantes en un viejo compilador de VC ++, que no es compatible con el último estándar de C ++. Pero el código debe ser independiente de la plataforma y debería funcionar en la mayoría de los compiladores de C ++. Quiero saber cuál es la práctica recomendada con devoluciones de llamada?
Asegúrate de que esté en el alcance global
Use
extern "C"
Utilice
__cdecl
, si es necesario:void (_cdecl *callback)
Esto debería funcionar si su función miembro es estática.
Estrictamente hablando, no puedes. La devolución de llamada de AC se debe designar extern "C"
, lo cual no es posible para las funciones de miembro. Solo se pueden usar funciones independientes. Puede reenviar llamadas desde allí a cualquier función de C ++, incluidas funciones de miembro estáticas o no estáticas.
Dependiendo de la plataforma, a veces puede salirse con la omisión de la extern "C"
, pero no probaría mi suerte.
No es tan simple como declarar la función de devolución de llamada bajo un bloque extern "C"
. Debe averiguar qué convención de llamadas usa la biblioteca C para sus funciones.
Los términos que voy a utilizar son específicos de Microsoft, pero las mismas reglas también deberían aplicarse a otras plataformas.
De forma predeterminada, el compilador de Visual C ++ hace que la función C __stdcall
y C ++ funcionen __cdecl
. No puede mezclar y combinar estas convenciones de llamadas ya que cada una de estas hace suposiciones diferentes sobre quién limpia la pila. Aquí hay una explicación más detallada.
Una vez que haya coincidido con las convenciones de llamadas, el enfoque más fácil es declarar su función de devolución de llamada de C ++ como una función independiente de ámbito de espacio de nombres; o si necesita ser una función miembro, entonces una función miembro estática. En ambos casos, debería poder enlazar un puntero a la instancia de la clase usando std::bind
. Incluso podría usar std::bind
para enlazar una función miembro no estática, pero no puedo recordar la sintaxis en la parte superior de mi cabeza.
Suponiendo que appContext
es un puntero opaco que pasa a la función que realiza la devolución de llamada, puede obtener una devolución de llamada a una función miembro de un objeto específico como este:
class myclass {
void do_something() {
// call function making the callback using _event_handler
// as the callback function and the "this" pointer as appContext
}
// make sure the raw callback uses the correct calling convention (cdecl, stdcall, etc.)
static void _handle_event(void* appContext, int eventid) {
// forward the event to the actual object
static_cast<myclass *>(appContext)->handle_event(eventid);
}
void handle_event(int eventid) {
// do object-specific event handling
}
};
Varias respuestas mencionan el extern "C"
como un requisito. Esto es simplemente incorrecto extern "C"
es necesario solo cuando llamas a una función C directamente desde C ++. Se usa para indicarle al compilador de C ++ "no aplicar el cambio de nombre al generar el nombre del símbolo para esta función". Está pasando un puntero de función C ++ a una función C. Siempre que las convenciones de llamadas coincidan, funcionará bien. El nombre de la función nunca está involucrado.
¿La función de devolución de llamada debe declararse en el exterior "C"?
NO. Extern "C" es necesario solo cuando está llamando a una función de C ++ directamente, sin el uso de punteros de función, desde C. Si se utilizan punteros de función, no se requiere la opción "C" externa.
¿Puedo usar funciones miembro no estáticas como devolución de llamada?
NO. Las funciones miembro no estáticas de la clase A tienen un primer parámetro implícito correspondiente a este puntero.
¿Puedo usar funciones miembro estáticas como devolución de llamada?
SÍ, siempre que la firma coincida con la de la devolución de llamada.
¿Importa si la función es una función de plantilla o no?
NO, la función de plantilla se puede usar como devolución de llamada siempre que la firma de la plantilla instanciada coincida con la devolución de llamada.