2010-03-10 22 views
9

Por lo tanto, estoy usando la api FMOD y realmente es una API C.Retrollamadas de la función C API en C++ código de función de miembro

No es malo o lo que sea. Es solo que no se combina bien con el código C++.

Por ejemplo, el uso de

FMOD_Channel_SetCallback(channel, callbackFunc) ; 

Quiere una función de estilo C para callbackFunc, pero quiero pasarlo a una función miembro de una clase.

Terminé usando el truco de Win32 para esto, haciendo que la función de miembro estética. Luego funciona como una devolución de llamada a FMOD.

Ahora tengo que hackear mi código para hacer algunos de los miembros estáticos, solo para dar cuenta de la C-dad de FMOD.

Me pregunto si es posible en FMOD o si hay un problema para vincular la devolución de llamada a una función de miembro de instancia de objeto de C++ específica (no una función estática). Sería mucho más suave.

Respuesta

11

No se puede pasar directamente a una función de miembro. Una función miembro tiene el parámetro implícito this y las funciones C no.

Tendrá que crear un trampolín (no estoy seguro de la firma de la devolución de llamada, así que simplemente estoy haciendo algo al azar aquí).

extern "C" int fmod_callback(... args ...) 
{ 
    return object->member(); 
} 

Un problema es de dónde viene ese puntero de objeto. Con suerte, fmod le proporciona un valor de contexto genérico que se le proporcionará cuando realice la devolución de llamada (puede pasar el puntero del objeto).

Si no, solo tendrá que convertirlo en global para acceder a él.

+1

+1 Sí, tienes que hacer un trampolín, pero son tan retorcidos (si quieres evitar los globales, y todo eso). :-(Si la API se diseñó correctamente, en primer lugar ... –

+1

También debe preocuparse por qué hacer si las funciones miembro de C++ arrojan. ¿Y de dónde viene este término "trampolín"? –

+1

@NeilButterworth - I No recuerdo dónde escuché por primera vez que se describía como un "trampolín", pero una referencia para mi uso es Wikipedia (http://en.wikipedia.org/wiki/Trampoline_%28computers%29) 'Cuando se interconectan piezas de código con convenciones de llamadas incompatibles, un trampolín se usa para convertir la convención de la persona que llama en la convención de la persona que llama. –

1

Usar un puntero de función (y ningún puntero de objeto separado adicional) para una devolución de llamada C es un diseño roto, en mi humilde opinión.

Si la función fue, en cambio, FMOD_Channel_SetCallback(channel, callbackFunc, callbackObj), su método estático solo toma una instancia del objeto, luego llama a callbackObj->func() (que obviamente puede ser no estático).

+1

¡Sugerir esto a la gente de FMOD! ¡Por favor! – bobobobo

+1

@bobobobo: ver la respuesta de Denis. Creo que la API ya lo admite (obviamente ha investigado FMOD más de lo que tengo :-P). Mantendré mi respuesta como una advertencia para otros diseñadores de API, pero al menos tienes una solución. –

0

es necesario utilizar un trampolín y almacenar el puntero al objeto que desea obtener la función miembro llamada en en una variable global o estática, es decir

Object *x; 
void callback_trampoline() { x->foobar(); } 
... 
FMOD_Channel_SetCallback(CHANNEL, callback_trampoline); 
4

supongo que supone que funciona de esta manera:
Puede asignar algunos datos de usuario al canal llamando al FMOD_Channel_SetUserData. Esta información de usuario debe ser un puntero a su objeto C++ que maneja eventos. Luego debe escribir la devolución de llamada al estilo C que extrae ese objeto llamando al FMOD_Channel_GetUserData y luego llama a su método de instancia C++ en ese objeto.

+0

+1 ¡Oh, gane! Entonces, la API no está tan rota como pensé. –

2

Hay una solución no portátil, y bastante hackish que tiene la ventaja de al menos ser seguro para subprocesos, que los métodos "trampolín" no lo son.

Puede generar el código de la máquina de función real sobre la marcha. La idea básica es que tiene una plantilla para su función de devolución de llamada que toma un puntero de objeto y un puntero de función de miembro y le proporciona un bloque de memoria de montón que puede pasar a la biblioteca como una función de devolución de llamada de C, que Cuando se le llame, dará la vuelta y llamará a la función miembro sobre ese objeto.

Es complicado, y tendrá que proporcionar una implementación para cualquier plataforma nueva (cada vez que cambie la convención de llamadas), pero funciona, es seguro para subprocesos. (Por supuesto, también tendrás que tener cuidado con DEP). La otra solución de seguridad de subprocesos es recurrir al almacenamiento local de subprocesos (suponiendo que sepa que la devolución de llamada se realizará en el mismo subproceso que la llamada que realizó).

Consulte http://www.codeproject.com/KB/cpp/GenericThunks.aspx para ver un ejemplo de cómo podría generar generar thunk.

Cuestiones relacionadas